/* * 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 and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * 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, see . * * * user_interface.c - this source file contains an abstraction to the * nvidia-installer user interface. */ #include #include #include #include #include #include #include #include #include #include #include #include "nvidia-installer.h" #include "nvidia-installer-ui.h" #include "misc.h" #include "files.h" #include "user-interface.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 char ncurses_ui_array[]; extern const int ncurses_ui_array_size; #if defined(NV_INSTALLER_NCURSES6) extern const char ncurses6_ui_array[]; extern const int ncurses6_ui_array_size; #endif #if defined(NV_INSTALLER_NCURSESW6) extern const char ncursesw6_ui_array[]; extern const int ncursesw6_ui_array_size; #endif /* struct describing the ui data */ typedef struct { char *name; 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); /* * Definitions of very common answer choices used by callers of * ui_multiple_choice() or ui_paged_prompt() */ const char * const CONTINUE_ABORT_CHOICES[] = { [CONTINUE_CHOICE] = "Continue installation", [ABORT_CHOICE] = "Abort installation" }; /* * 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 }, */ #if defined(NV_INSTALLER_NCURSES6) { "ncurses6", "nvidia-installer ncurses v6 user interface", NULL, ncurses6_ui_array, ncurses6_ui_array_size }, #endif { "ncurses", "nvidia-installer ncurses user interface", NULL, ncurses_ui_array, ncurses_ui_array_size }, #if defined(NV_INSTALLER_NCURSESW6) { "ncursesw6", "nvidia-installer ncurses v6 user interface (widechar)", NULL, ncursesw6_ui_array, ncursesw6_ui_array_size }, #endif { "none", NULL, NULL, NULL, 0 } }; /* dlopen() the appropriate ui shared lib */ __ui = NULL; if (!op->silent) { if (op->ui_str) { for (i = 0; i < ARRAY_LEN(ui_list); i++) { if (strcmp(op->ui_str, ui_list[i].name) == 0) { break; } } if (i == ARRAY_LEN(ui_list)) { log_printf(op, NULL, "Invalid \"ui\" option: %s", op->ui_str); i = 0; } } else { i = 0; } for (; i < ARRAY_LEN(ui_list) && 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, NULL, "Using: %s", ui_list[i].descr); __extracted_user_interface_filename = ui_list[i].filename; break; } else { log_printf(op, NULL, "Unable to initialize: %s", ui_list[i].descr); dlclose(handle); __ui = NULL; } } else { log_printf(op, NULL, "Unable to load: %s", ui_list[i].descr); log_printf(op, NULL, ""); } } } /* fall back to the always built-in stream ui */ if (!__ui) { __ui = &stream_ui_dispatch_table; log_printf(op, 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; if (op->silent) return; NV_VSNPRINTF(title, fmt); __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; NV_VSNPRINTF(msg, fmt); if (op->no_questions) { ret = nvstrdup(def ? 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, NV_BULLET_STR, "%s", tmp); nvfree(msg); nvfree(tmp); return ret; } /* ui_get_input() */ /* * ui_error() - have the ui display an error message */ void ui_error(Options *op, const char *fmt, ...) { char *msg; NV_VSNPRINTF(msg, fmt); __ui->message(op, NV_MSG_LEVEL_ERROR, msg); log_printf(op, "ERROR: ", "%s", msg); free(msg); } /* ui_error() */ void ui_warn(Options *op, const char *fmt, ...) { char *msg; NV_VSNPRINTF(msg, fmt); __ui->message(op, NV_MSG_LEVEL_WARNING, msg); log_printf(op, "WARNING: ", "%s", msg); free(msg); } /* ui_error() */ void ui_message(Options *op, const char *fmt, ...) { char *msg; NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->message(op, NV_MSG_LEVEL_MESSAGE, msg); log_printf(op, NV_BULLET_STR, "%s", msg); free(msg); } /* ui_message() */ void ui_log(Options *op, const char *fmt, ...) { char *msg; NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); log_printf(op, NV_BULLET_STR, "%s", 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; if (!op->expert) return; NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); log_printf(op, NV_BULLET_STR, "%s", msg); free (msg); } /* ui_expert() */ void ui_command_output(Options *op, const char *fmt, ...) { char *msg; NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->command_output(op, msg); log_printf(op, NV_CMD_OUT_PREFIX, "%s", 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; if (!op->expert || op->no_questions) return TRUE; NV_VSNPRINTF(msg, fmt); 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; NV_VSNPRINTF(msg, fmt); 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, NV_BULLET_STR, "%s", tmp); nvfree(msg); nvfree(tmp); return ret; } /* ui_yes_no() */ int ui_multiple_choice (Options *op, const char * const *answers, int num_answers, int default_answer, const char *fmt, ...) { char *question, *tmp = NULL; int ret; NV_VSNPRINTF(question, fmt); if (op->no_questions) { ret = default_answer; } else { ret = __ui->multiple_choice(op, question, answers, num_answers, default_answer); } tmp = nvstrcat(question, " (Answer: ", answers[ret], ")", NULL); if (!op->silent) { __ui->message(op, NV_MSG_LEVEL_LOG, tmp); } log_printf(op, NV_BULLET_STR, "%s", tmp); nvfree(question); nvfree(tmp); return ret; } /* ui_multiple_choice() */ int ui_paged_prompt (Options *op, const char *question, const char *pager_title, const char *pager_text, const char * const *answers, int num_answers, int default_answer) { char *tmp; int ret; if (op->no_questions) { ret = default_answer; } else { ret = __ui->paged_prompt(op, question, pager_title, pager_text, answers, num_answers, default_answer); } tmp = nvstrcat(question, "\n\n", pager_text, "\n(Answer: ", answers[ret], ")", NULL); if (!op->silent) { __ui->message(op, NV_MSG_LEVEL_LOG, tmp); } log_printf(op, NV_BULLET_STR, "%s", tmp); nvfree(tmp); return ret; } void ui_status_begin(Options *op, const char *title, const char *fmt, ...) { char *msg; log_printf(op, NV_BULLET_STR, "%s", title); if (op->silent) return; NV_VSNPRINTF(msg, fmt); __ui->status_begin(op, title, msg); free(msg); } void ui_status_update(Options *op, const float percent, const char *fmt, ...) { char *msg; if (op->silent) return; NV_VSNPRINTF(msg, fmt); __ui->status_update(op, percent, msg); free(msg); } void ui_status_end(Options *op, const char *fmt, ...) { char *msg; NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->status_end(op, msg); log_printf(op, NV_BULLET_STR, "%s", msg); free(msg); } void ui_close (Options *op) { if (__ui) __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, 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, 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, NULL, "Unable to set file size for '%s' (%s)", ui->filename, strerror(errno)); goto failed; } if (write(fd, "", 1) != 1) { log_printf(op, 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, 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, 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); } 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; ui_close(NULL); /* * XXX don't have an Options struct to * pass to ui_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() */