diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
commit | 6d2a0069d419975ba01c2c423b18ef7cd2e76a6f (patch) | |
tree | 130e177fc9fa77d4c7d0788ba07c3b5af42dd84f /user-interface.c |
1.0-61061.0-6106
Diffstat (limited to 'user-interface.c')
-rw-r--r-- | user-interface.c | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/user-interface.c b/user-interface.c new file mode 100644 index 0000000..1ef5a1c --- /dev/null +++ b/user-interface.c @@ -0,0 +1,642 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * user_interface.c - this source file contains an abstraction to the + * nvidia-installer user interface. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <dlfcn.h> +#include <errno.h> +#include <signal.h> +#include "nvidia-installer.h" +#include "nvidia-installer-ui.h" +#include "misc.h" +#include "files.h" + + +/* + * global user interface pointer + */ + +InstallerUI *__ui = NULL; + +/* + * filename of extracted user interface + */ + +char *__extracted_user_interface_filename = NULL; + +/* pull in the default stream_ui dispatch table from stream_ui.c */ + +extern InstallerUI stream_ui_dispatch_table; + +/* pull in the user interface data arrays and sizes */ + +extern const unsigned char ncurses_ui_array[]; +extern const int ncurses_ui_array_size; + +/* struct describing the ui data */ + +typedef struct { + char *descr; + char *filename; + const char *data_array; + const int data_array_size; +} user_interface_attribute_t; + + +static int extract_user_interface(Options *op, user_interface_attribute_t *ui); +static void ui_signal_handler(int n); + +/* + * ui_init() - initialize the user interface; we start by looping over + * each of the possible ui shared libs (gtk, ncurses) until we find + * one that will work; if neither will work, then fall back to the + * built-in stream ui. Once we have chosen our ui, call it's init() + * function and return TRUE. + */ + +int ui_init(Options *op) +{ + void *handle; + int i; + user_interface_attribute_t ui_list[] = { + /* { "nvidia-installer GTK+ user interface", NULL, NULL, 0 }, */ + { "nvidia-installer ncurses user interface", NULL, + ncurses_ui_array, ncurses_ui_array_size }, + { NULL, NULL, NULL, 0 } + }; + + /* dlopen() the appropriate ui shared lib */ + + __ui = NULL; + + i = 0; + + if (((op->ui_str) && (strcmp(op->ui_str, "none") == 0)) || (op->silent)) { + i = 1; + } + + for (; ui_list[i].descr && !__ui; i++) { + + if (!extract_user_interface(op, &ui_list[i])) continue; + + handle = dlopen(ui_list[i].filename, RTLD_NOW); + + if (handle) { + __ui = dlsym(handle, "ui_dispatch_table"); + if (__ui && __ui->detect(op)) { + log_printf(op, TRUE, NULL, "Using: %s", + ui_list[i].descr); + __extracted_user_interface_filename = ui_list[i].filename; + break; + } else { + log_printf(op, TRUE, NULL, "Unable to initialize: %s", + ui_list[i].descr); + dlclose(handle); + __ui = NULL; + } + } else { + log_printf(op, TRUE, NULL, "Unable to load: %s", + ui_list[i].descr); + log_printf(op, TRUE, NULL, ""); + } + } + + /* fall back to the always built-in stream ui */ + + if (!__ui) { + __ui = &stream_ui_dispatch_table; + log_printf(op, TRUE, NULL, "Using built-in stream user interface"); + } + + /* + * init the ui + * + * XXX if init() fails, we should try to fall back to the build-in + * stream ui. + */ + + if (!__ui->init(op, nv_format_text_rows)) return FALSE; + + /* handle some common signals */ + + signal(SIGHUP, ui_signal_handler); + signal(SIGALRM, ui_signal_handler); + signal(SIGABRT, ui_signal_handler); + signal(SIGSEGV, ui_signal_handler); + signal(SIGTERM, ui_signal_handler); + signal(SIGINT, ui_signal_handler); + signal(SIGILL, ui_signal_handler); + signal(SIGBUS, ui_signal_handler); + + /* so far, so good */ + + return TRUE; + +} /* init_ui () */ + + + +/* + * ui_set_title() - + */ + +void ui_set_title(Options *op, const char *fmt, ...) +{ + char *title; + va_list ap; + + if (op->silent) return; + + va_start(ap, fmt); + title = assemble_string(fmt, ap); + va_end(ap); + + __ui->set_title(op, title); + free(title); + +} /* ui_set_title() */ + + + +/* + * ui_get_input() - + */ + +char *ui_get_input(Options *op, const char *def, const char *fmt, ...) +{ + char *msg, *tmp = NULL, *ret; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (op->no_questions) { + ret = nvstrdup(def); + tmp = nvstrcat(msg, " (Answer: '", ret," ')", NULL); + if (!op->silent) { + __ui->message(op, NV_MSG_LEVEL_LOG, tmp); + } + } else { + ret = __ui->get_input(op, def, msg); + tmp = nvstrcat(msg, " (Answer: '", ret," ')", NULL); + } + log_printf(op, TRUE, NV_BULLET_STR, tmp); + nvfree(msg); + nvfree(tmp); + + return ret; + +} /* ui_get_input() */ + + + +/* + * ui_display_license() + */ + +int ui_display_license (Options *op, const char *license) +{ + if (op->silent) return TRUE; + + return __ui->display_license(op, license); + +} /* ui_display_license() */ + + + +/* + * ui_error() - have the ui display an error message + */ + +void ui_error(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->message(op, NV_MSG_LEVEL_ERROR, msg); + log_printf(op, TRUE, "ERROR: ", msg); + + free(msg); + +} /* ui_error() */ + + + +void ui_warn(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->message(op, NV_MSG_LEVEL_WARNING, msg); + log_printf(op, TRUE, "WARNING: ", msg); + + free(msg); + +} /* ui_error() */ + + + +void ui_message(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->message(op, NV_MSG_LEVEL_MESSAGE, msg); + + log_printf(op, TRUE, NV_BULLET_STR, msg); + + free(msg); + +} /* ui_message() */ + + +void ui_log(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); + log_printf(op, TRUE, NV_BULLET_STR, msg); + + free(msg); + +} /* ui_message() */ + + +/* + * ui_expert() - this is essentially the same as ui_log, but the ui is + * only called to display the message when we are in expert mode. + */ + +void ui_expert(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + if (!op->expert) return; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); + log_printf(op, FALSE, NV_BULLET_STR, msg); + + free (msg); + +} /* ui_expert() */ + + + +void ui_command_output(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->command_output(op, msg); + + log_printf(op, FALSE, NV_CMD_OUT_PREFIX, msg); + + free(msg); + +} /* ui_command_output() */ + + + +/* + * ui_approve_command_list() + */ + +int ui_approve_command_list(Options *op, CommandList *c, const char *fmt, ...) +{ + char *msg; + int ret; + va_list ap; + + if (!op->expert || op->no_questions) return TRUE; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + ret = __ui->approve_command_list(op, c, msg); + free(msg); + + if (ret) __ui->message(op, NV_MSG_LEVEL_LOG, "Commandlist approved."); + else __ui->message(op, NV_MSG_LEVEL_LOG, "Commandlist rejected."); + + return ret; + +} /* ui_approve_command_list() */ + + +/* + * ui_yes_no() + */ + +int ui_yes_no (Options *op, const int def, const char *fmt, ...) +{ + char *msg, *tmp = NULL; + int ret; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (op->no_questions) { + ret = def; + tmp = nvstrcat(msg, " (Answer: ", (ret ? "Yes" : "No"), ")", NULL); + if (!op->silent) { + __ui->message(op, NV_MSG_LEVEL_LOG, tmp); + } + } else { + ret = __ui->yes_no(op, def, msg); + tmp = nvstrcat(msg, " (Answer: ", (ret ? "Yes" : "No"), ")", NULL); + } + + log_printf(op, FALSE, NV_BULLET_STR, tmp); + nvfree(msg); + nvfree(tmp); + + return ret; + +} /* ui_yes_no() */ + + + +void ui_status_begin(Options *op, const char *title, const char *fmt, ...) +{ + char *msg; + va_list ap; + + log_printf(op, TRUE, NV_BULLET_STR, title); + + if (op->silent) return; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->status_begin(op, title, msg); + free(msg); +} + + + +void ui_status_update(Options *op, const float percent, const char *fmt, ...) +{ + char *msg; + va_list ap; + + if (op->silent) return; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->status_update(op, percent, msg); + free(msg); +} + + + +void ui_status_end(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->status_end(op, msg); + log_printf(op, TRUE, NV_BULLET_STR, msg); + free(msg); +} + + + +void ui_close (Options *op) +{ + __ui->close(op); + + if (__extracted_user_interface_filename) { + unlink(__extracted_user_interface_filename); + } + + __ui = NULL; + +} /* ui_close() */ + + + +/* + * extract_user_interface() - we want the user interfaces to be shared + * libraries, separate from the main installer binary, to protect the + * main installer from any broken library dependencies that the user + * interfaces may introduce. However, we don't want to have to + * install the user interface shared libraries on the user's system + * (and risk not finding them -- or worse: finding the wrong ones -- + * when the installer gets run). + * + * The solution to this is to build the ui shared libraries as usual, + * but to include them INSIDE the installer binary as static data, so + * that the installer is contained within one single file. + * + * The user_interface_attribute_t struct contains everything that is + * necessary to extract the user interface files, dumping each to a + * temporary file so that it can be dlopen()ed. + */ + +static int extract_user_interface(Options *op, user_interface_attribute_t *ui) +{ + unsigned char *dst = (void *) -1; + int fd = -1; + + /* check that this ui is present in the binary */ + + if ((ui->data_array == NULL) || (ui->data_array_size == 0)) { + log_printf(op, TRUE, NULL, "%s: not present.", ui->descr); + return FALSE; + } + + /* create a temporary file */ + + ui->filename = nvstrcat(op->tmpdir, "/nv-XXXXXX", NULL); + + fd = mkstemp(ui->filename); + if (fd == -1) { + log_printf(op, TRUE, NULL, "unable to create temporary file (%s)", + strerror(errno)); + goto failed; + } + + /* set the temporary file's size */ + + if (lseek(fd, ui->data_array_size - 1, SEEK_SET) == -1) { + log_printf(op, TRUE, NULL, "Unable to set file size for '%s' (%s)", + ui->filename, strerror(errno)); + goto failed; + } + if (write(fd, "", 1) != 1) { + log_printf(op, TRUE, NULL, "Unable to write file size for '%s' (%s)", + ui->filename, strerror(errno)); + goto failed; + } + + /* mmap the temporary file */ + + if ((dst = mmap(0, ui->data_array_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0)) == (void *) -1) { + log_printf(op, TRUE, NULL, "Unable to map destination file '%s' " + "for copying (%s)", ui->filename, strerror(errno)); + goto failed; + } + + /* copy the data out to the file */ + + memcpy(dst, ui->data_array, ui->data_array_size); + + /* unmap the temporary file */ + + if (munmap(dst, ui->data_array_size) == -1) { + log_printf(op, TRUE, NULL, "Unable to unmap destination file '%s' " + "(%s)", ui->filename, strerror(errno)); + goto failed; + } + + /* close the file */ + + close(fd); + + return TRUE; + + failed: + + if (dst != (void *) -1) munmap(dst, ui->data_array_size); + if (fd != -1) { close(fd); unlink(ui->filename); } + if (ui->filename) free(ui->filename); + + return FALSE; + +} /* extract_user_interface() */ + + +/* + * ui_signal_handler() - if a signal goes off that is going to + * terminate the process, call the ui to close cleanly; then print a + * message to stderr. + */ + +static void ui_signal_handler(int n) +{ + const char *sig_names[] = { + "UNKNOWN", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ + "SIGUNUSED", /* 31 */ + }; + + const char *s; + + if (__ui) __ui->close(NULL); /* + * XXX don't have an Options struct to + * pass to close() + */ + /* + * print to stderr with write(2) rather than fprintf(3), since + * fprintf isn't guaranteed to be reentrant + */ + + s = (n < 32) ? sig_names[n] : "UNKNOWN"; + + write(2, "Received signal ", 16); + write(2, s, strlen(s)); + write(2, "; aborting.\n", 12); + + exit(128 + n); + +} /* ui_signal_handler() */ |