diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2013-10-04 08:50:40 -0700 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2013-10-04 08:50:40 -0700 |
commit | f0923811d809b403b23dee449b849a01df6d2e79 (patch) | |
tree | a134b5026058e9c621cf359b853d24c67c38cd39 | |
parent | a977103d446bc8c0c3827d8726c53bb8292a31cd (diff) |
331.13331.13
-rw-r--r-- | backup.c | 43 | ||||
-rw-r--r-- | command-list.c | 237 | ||||
-rw-r--r-- | common-utils/common-utils.c | 24 | ||||
-rw-r--r-- | common-utils/common-utils.h | 17 | ||||
-rw-r--r-- | common-utils/gen-manpage-opts-helper.c | 2 | ||||
-rw-r--r-- | common-utils/nvgetopt.h | 4 | ||||
-rw-r--r-- | files.c | 78 | ||||
-rw-r--r-- | files.h | 4 | ||||
-rw-r--r-- | install-from-cwd.c | 151 | ||||
-rw-r--r-- | kernel.c | 787 | ||||
-rw-r--r-- | kernel.h | 5 | ||||
-rw-r--r-- | manifest.c | 2 | ||||
-rw-r--r-- | misc.c | 189 | ||||
-rw-r--r-- | misc.h | 9 | ||||
-rw-r--r-- | mkprecompiled.c | 2 | ||||
-rw-r--r-- | ncurses-ui.c | 498 | ||||
-rw-r--r-- | nvidia-installer-ui.h | 14 | ||||
-rw-r--r-- | nvidia-installer.c | 32 | ||||
-rw-r--r-- | nvidia-installer.h | 12 | ||||
-rw-r--r-- | option_table.h | 25 | ||||
-rw-r--r-- | precompiled.c | 36 | ||||
-rw-r--r-- | precompiled.h | 3 | ||||
-rw-r--r-- | stream-ui.c | 103 | ||||
-rw-r--r-- | user-interface.c | 44 | ||||
-rw-r--r-- | user-interface.h | 2 | ||||
-rw-r--r-- | utils.mk | 2 | ||||
-rw-r--r-- | version.mk | 2 |
27 files changed, 1546 insertions, 781 deletions
@@ -708,7 +708,7 @@ static int do_uninstall(Options *op, const char *version) e->filename, strerror(errno)); removal_failed = TRUE; } - ui_status_update(op, percent, e->filename); + ui_status_update(op, percent, "%s", e->filename); break; case INSTALLED_SYMLINK: @@ -717,7 +717,7 @@ static int do_uninstall(Options *op, const char *version) e->filename, strerror(errno)); removal_failed = TRUE; } - ui_status_update(op, percent, e->filename); + ui_status_update(op, percent, "%s", e->filename); break; } } @@ -763,7 +763,7 @@ static int do_uninstall(Options *op, const char *version) restore_failed = TRUE; } } - ui_status_update(op, percent, e->filename); + ui_status_update(op, percent, "%s", e->filename); break; default: @@ -787,7 +787,7 @@ static int do_uninstall(Options *op, const char *version) } } } - ui_status_update(op, percent, e->filename); + ui_status_update(op, percent, "%s", e->filename); free(tmpstr); break; } @@ -817,14 +817,35 @@ static int do_uninstall(Options *op, const char *version) } /* - * attempt to unload the kernel module, but don't abort if this fails: the - * kernel may not have been configured with support for module unloading - * (Linux 2.6) or the user might have unloaded it themselves. + * attempt to unload the kernel module(s), but don't abort if this fails: + * the kernel may not have been configured with support for module + * unloading or the user might have unloaded it themselves or the module + * might not have existed at all. */ + cmd = nvstrcat(op->utils[RMMOD], " ", RMMOD_MODULE_NAME, NULL); run_command(op, cmd, NULL, FALSE, 0, TRUE); nvfree(cmd); + for (i = 0; i < NV_MAX_MODULE_INSTANCES; i++) { + char num[5]; + memset(num, 0, sizeof(num)); + snprintf(num, sizeof(num), "%d", i); + num[sizeof(num) - 1] = '\0'; + + cmd = nvstrcat(op->utils[RMMOD], " ", RMMOD_MODULE_NAME, + num, NULL); + run_command(op, cmd, NULL, FALSE, 0, TRUE); + + nvfree(cmd); + } + + cmd = nvstrcat(op->utils[RMMOD], " ", RMMOD_MODULE_NAME, + "-frontend", NULL); + run_command(op, cmd, NULL, FALSE, 0, TRUE); + + nvfree(cmd); + run_distro_hook(op, "post-uninstall"); free_backup_info(b); @@ -1106,7 +1127,7 @@ static int check_backup_log_entries(Options *op, BackupInfo *b) } } } - ui_status_update(op, percent, e->filename); + ui_status_update(op, percent, "%s", e->filename); break; @@ -1153,7 +1174,7 @@ static int check_backup_log_entries(Options *op, BackupInfo *b) free(tmpstr); } } - ui_status_update(op, percent, e->filename); + ui_status_update(op, percent, "%s", e->filename); break; @@ -1189,7 +1210,7 @@ static int check_backup_log_entries(Options *op, BackupInfo *b) ret = e->ok = FALSE; } } - ui_status_update(op, percent, tmpstr); + ui_status_update(op, percent, "%s", tmpstr); free(tmpstr); break; } @@ -1607,7 +1628,7 @@ static int sanity_check_backup_log_entries(Options *op, BackupInfo *b) } percent = (float) i / (float) (b->n); - ui_status_update(op, percent, e->filename); + ui_status_update(op, percent, "%s", e->filename); } ui_status_end(op, "done."); diff --git a/command-list.c b/command-list.c index a4d0fc4..45b4f19 100644 --- a/command-list.c +++ b/command-list.c @@ -74,6 +74,16 @@ static void add_file_to_list(const char*, const char*, FileList*); static void append_to_rpm_file_list(Options *op, Command *c); +/* + * find_conflicting_files() optionally takes an array of NoRecursionDirectory + * entries to indicate which directories should not be recursively searched. + */ + +typedef struct { + int level; /* max search depth: set to negative for unrestricted depth */ + char *name; /* name to find: NULL to end the list */ +} NoRecursionDirectory; + /* * build_command_list() - construct a list of all the things to do @@ -151,12 +161,12 @@ CommandList *build_command_list(Options *op, Package *p) find_conflicting_xfree86_libraries(op, DEFAULT_X_PREFIX, l); ui_status_update(op, 0.32f, XORG7_DEFAULT_X_PREFIX); find_conflicting_xfree86_libraries(op, XORG7_DEFAULT_X_PREFIX, l); - ui_status_update(op, 0.48f, op->x_prefix); + ui_status_update(op, 0.48f, "%s", op->x_prefix); find_conflicting_xfree86_libraries(op, op->x_prefix, l); - ui_status_update(op, 0.64f, op->x_module_path); + ui_status_update(op, 0.64f, "%s", op->x_module_path); find_conflicting_xfree86_libraries_fullpath(op, op->x_module_path, l); - ui_status_update(op, 0.80f, op->x_library_path); + ui_status_update(op, 0.80f, "%s", op->x_library_path); find_conflicting_xfree86_libraries_fullpath(op, op->x_library_path, l); ui_status_end(op, "done."); @@ -165,11 +175,11 @@ CommandList *build_command_list(Options *op, Package *p) ui_status_update(op, 0.20f, DEFAULT_X_PREFIX); find_conflicting_opengl_libraries(op, DEFAULT_X_PREFIX, l); - ui_status_update(op, 0.40f, op->x_prefix); + ui_status_update(op, 0.40f, "%s", op->x_prefix); find_conflicting_opengl_libraries(op, op->x_prefix, l); ui_status_update(op, 0.60f, DEFAULT_OPENGL_PREFIX); find_conflicting_opengl_libraries(op, DEFAULT_OPENGL_PREFIX, l); - ui_status_update(op, 0.80f, op->opengl_prefix); + ui_status_update(op, 0.80f, "%s", op->opengl_prefix); find_conflicting_opengl_libraries(op, op->opengl_prefix, l); ui_status_end(op, "done."); @@ -181,22 +191,22 @@ CommandList *build_command_list(Options *op, Package *p) ui_status_begin(op, "Searching for conflicting compat32 files:", "Searching"); prefix = nvstrcat(op->compat32_chroot, DEFAULT_X_PREFIX, NULL); - ui_status_update(op, 0.20f, prefix); + ui_status_update(op, 0.20f, "%s", prefix); find_conflicting_opengl_libraries(op, prefix, l); nvfree(prefix); prefix = nvstrcat(op->compat32_chroot, op->x_prefix, NULL); - ui_status_update(op, 0.40f, prefix); + ui_status_update(op, 0.40f, "%s", prefix); find_conflicting_opengl_libraries(op, prefix, l); nvfree(prefix); prefix = nvstrcat(op->compat32_chroot, DEFAULT_OPENGL_PREFIX, NULL); - ui_status_update(op, 0.60f, prefix); + ui_status_update(op, 0.60f, "%s", prefix); find_conflicting_opengl_libraries(op, prefix, l); nvfree(prefix); prefix = nvstrcat(op->compat32_chroot, op->compat32_prefix, NULL); - ui_status_update(op, 0.80f, prefix); + ui_status_update(op, 0.80f, "%s", prefix); find_conflicting_opengl_libraries(op, prefix, l); nvfree(prefix); @@ -457,7 +467,7 @@ int execute_command_list(Options *op, CommandList *c, int i, ret; float percent; - ui_status_begin(op, title, msg); + ui_status_begin(op, title, "%s", msg); for (i = 0; i < c->num; i++) { @@ -555,6 +565,20 @@ int execute_command_list(Options *op, CommandList *c, *************************************************************************** */ + +/* + * CONFLICT_ARCH_ALL: file always conflicts, regardless of arch + * CONFLICT_ARCH_32: file only conflicts if its arch is 32 bit + * CONFLICT_ARCH_64: file only conflicts if its arch is 64 bit + */ + +typedef enum { + CONFLICT_ARCH_ALL, + CONFLICT_ARCH_32, + CONFLICT_ARCH_64, +} ConflictArch; + + typedef struct { const char *name; int len; @@ -567,12 +591,15 @@ typedef struct { */ const char *requiredString; + + ConflictArch conflictArch; } ConflictingFileInfo; static void find_conflicting_files(Options *op, char *path, ConflictingFileInfo *files, - FileList *l); + FileList *l, + const NoRecursionDirectory *skipdirs); static void find_conflicting_libraries(Options *op, const char *prefix, @@ -580,28 +607,59 @@ static void find_conflicting_libraries(Options *op, FileList *l); static ConflictingFileInfo __xfree86_opengl_libs[] = { - { "libnvidia-glcore.", 17, /* strlen("libnvidia-glcore.") */ NULL }, - { "libGL.", 6, /* strlen("libGL.") */ NULL }, - { "libGLwrapper.", 13, /* strlen("libGLwrapper.") */ NULL }, - { "libglx.", 7, /* strlen("libglx.") */ "glxModuleData" }, - { NULL, 0, NULL } + { "libnvidia-glcore.", 17, /* strlen("libnvidia-glcore.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libGL.", 6, /* strlen("libGL.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libGLwrapper.", 13, /* strlen("libGLwrapper.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libglx.", 7, /* strlen("libglx.") */ + "glxModuleData", CONFLICT_ARCH_ALL }, + + /* XXX we do not currently build 64-bit EGL libraries due to problems + * with the ABI, so only conflict with 32-bit EGL libraries for now. */ + + { "libEGL.", 7, /* strlen("libEGL.") */ + NULL, CONFLICT_ARCH_32 }, + { "libGLESv1_CM.", 13, /* strlen("libGLESv1_CM." */ + NULL, CONFLICT_ARCH_32 }, + { "libGLESv2.", 10, /* strlen("libGLESv2." */ + NULL, CONFLICT_ARCH_32 }, + { NULL, 0, NULL, CONFLICT_ARCH_ALL } }; static ConflictingFileInfo __xfree86_non_opengl_libs[] = { - { "nvidia_drv.", 11, /* strlen("nvidia_drv.") */ NULL }, - { "libvdpau.", 9, /* strlen("libvdpau.") */ NULL }, - { "libvdpau_trace.", 15, /* strlen("libvdpau_trace.") */ NULL }, - { "libvdpau_nvidia.", 16, /* strlen("libvdpau_nvidia.") */ NULL }, - { "libnvidia-cfg.", 14, /* strlen("libnvidia-cfg.") */ NULL }, - { "libcuda.", 8, /* strlen("libcuda.") */ NULL }, - { "libnvidia-compiler.", 19, /* strlen("libnvidia-compiler.") */ NULL }, - { "libnvcuvid.", 11, /* strlen("libnvcuvid.") */ NULL }, - { "libnvidia-ml.", 13, /* strlen("libnvidia-ml.") */ NULL }, - { "libnvidia-encode.", 17, /* strlen("libnvidia-encode.") */ NULL }, - { "libnvidia-vgx.", 14, /* strlen("libnvidia-vgx.") */ NULL }, - { "libnvidia-ifr.", 14, /* strlen("libnvidia-ifr.") */ NULL }, - { "libnvidia-vgxcfg.", 17, /* strlen("libnvidia-vgxcfg.") */ NULL }, - { NULL, 0, NULL } + { "nvidia_drv.", 11, /* strlen("nvidia_drv.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libvdpau_nvidia.", 16, /* strlen("libvdpau_nvidia.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-cfg.", 14, /* strlen("libnvidia-cfg.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libcuda.", 8, /* strlen("libcuda.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-compiler.", 19, /* strlen("libnvidia-compiler.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvcuvid.", 11, /* strlen("libnvcuvid.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-ml.", 13, /* strlen("libnvidia-ml.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-encode.", 17, /* strlen("libnvidia-encode.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-vgx.", 14, /* strlen("libnvidia-vgx.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-ifr.", 14, /* strlen("libnvidia-ifr.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-vgxcfg.", 17, /* strlen("libnvidia-vgxcfg.") */ + NULL, CONFLICT_ARCH_ALL }, + { NULL, 0, NULL, CONFLICT_ARCH_ALL } +}; + +static ConflictingFileInfo __xfree86_vdpau_wrapper_libs[] = { + { "libvdpau.", 9, /* strlen("libvdpau.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libvdpau_trace.", 15, /* strlen("libvdpau_trace.") */ + NULL, CONFLICT_ARCH_ALL }, + { NULL, 0, NULL, CONFLICT_ARCH_ALL } }; /* @@ -618,6 +676,9 @@ static void find_conflicting_xfree86_libraries(Options *op, find_conflicting_libraries(op, xprefix, __xfree86_opengl_libs, l); } find_conflicting_libraries(op, xprefix, __xfree86_non_opengl_libs, l); + if (op->install_vdpau_wrapper == NV_OPTIONAL_BOOL_TRUE) { + find_conflicting_libraries(op, xprefix, __xfree86_vdpau_wrapper_libs, l); + } } /* find_conflicting_xfree86_libraries() */ @@ -636,28 +697,39 @@ static void find_conflicting_xfree86_libraries_fullpath(Options *op, FileList *l) { if (!op->no_opengl_files) { - find_conflicting_files(op, path, __xfree86_opengl_libs, l); + find_conflicting_files(op, path, __xfree86_opengl_libs, l, NULL); + } + find_conflicting_files(op, path, __xfree86_non_opengl_libs, l, NULL); + if (op->install_vdpau_wrapper == NV_OPTIONAL_BOOL_TRUE) { + find_conflicting_files(op, path, __xfree86_vdpau_wrapper_libs, l, NULL); } - find_conflicting_files(op, path, __xfree86_non_opengl_libs, l); } /* find_conflicting_xfree86_libraries_fullpath() */ static ConflictingFileInfo __opengl_libs[] = { - { "libnvidia-glcore.", 17, /* strlen("libnvidia-glcore.") */ NULL }, - { "libGL.", 6, /* strlen("libGL.") */ NULL }, - { "libnvidia-tls.", 14, /* strlen("libnvidia-tls.") */ NULL }, - { "libGLwrapper.", 13, /* strlen("libGLwrapper.") */ NULL }, - { NULL, 0, NULL } + { "libnvidia-glcore.", 17, /* strlen("libnvidia-glcore.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libGL.", 6, /* strlen("libGL.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-tls.", 14, /* strlen("libnvidia-tls.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libGLwrapper.", 13, /* strlen("libGLwrapper.") */ + NULL, CONFLICT_ARCH_ALL }, + { NULL, 0, NULL, CONFLICT_ARCH_ALL } }; static ConflictingFileInfo __non_opengl_libs[] = { - { "libnvidia-cfg.", 14, /* strlen("libnvidia-cfg.") */ NULL }, - { "libcuda.", 8, /* strlen("libcuda.") */ NULL }, - { "libnvidia-compiler.", 19, /* strlen("libnvidia-compiler.") */ NULL }, - { "libnvidia-ml.", 13, /* strlen("libnvidia-ml.") */ NULL }, - { NULL, 0, NULL } + { "libnvidia-cfg.", 14, /* strlen("libnvidia-cfg.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libcuda.", 8, /* strlen("libcuda.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-compiler.", 19, /* strlen("libnvidia-compiler.") */ + NULL, CONFLICT_ARCH_ALL }, + { "libnvidia-ml.", 13, /* strlen("libnvidia-ml.") */ + NULL, CONFLICT_ARCH_ALL }, + { NULL, 0, NULL, CONFLICT_ARCH_ALL } }; /* @@ -692,6 +764,14 @@ static void find_conflicting_kernel_modules(Options *op, char *paths[3]; char *tmp = get_kernel_name(op); + /* Don't descend into the "build" or "source" directories; these won't + * contain modules, and may be symlinks back to an actual source tree. */ + static const NoRecursionDirectory skipdirs[] = { + { 1, "build" }, + { 1, "source" }, + { 0, NULL } + }; + memset(files, 0, sizeof(files)); files[1].name = NULL; files[1].len = 0; @@ -714,7 +794,7 @@ static void find_conflicting_kernel_modules(Options *op, files[0].name = p->bad_module_filenames[n]; files[0].len = strlen(files[0].name); - find_conflicting_files(op, paths[i], files, l); + find_conflicting_files(op, paths[i], files, l, skipdirs); } } @@ -754,12 +834,14 @@ static void find_existing_files(Package *p, FileList *l, /* * ignore_conflicting_file() - ignore (i.e., do not put it on the list * of files to backup) the conflicting file 'filename' if requiredString - * is non-NULL and we cannot find the string in 'filename'. + * is non-NULL and we cannot find the string in 'filename', or if the + * file only conflicts on specific architectures, and the file's + * architecture does not match. */ static int ignore_conflicting_file(Options *op, const char *filename, - const char *requiredString) + const ConflictingFileInfo info) { int fd = -1; struct stat stat_buf; @@ -767,9 +849,29 @@ static int ignore_conflicting_file(Options *op, int ret = FALSE; int i, len; - /* if no requiredString, do not ignore this conflicting file */ + /* check if the file only conflicts on certain architectures */ + + if (info.conflictArch != CONFLICT_ARCH_ALL) { + ElfFileType elftype = get_elf_architecture(filename); + + switch (elftype) { + case ELF_ARCHITECTURE_32: + ret = info.conflictArch != CONFLICT_ARCH_32; + break; + case ELF_ARCHITECTURE_64: + ret = info.conflictArch != CONFLICT_ARCH_64; + break; + default: + ui_warn(op, "Unable to determine the architecture of the file " + "'%s', which has an architecture-specific conflict.", + filename); + break; + } + } + + /* if no requiredString, do not check for the required string */ - if (!requiredString) return FALSE; + if (!info.requiredString) return ret; if ((fd = open(filename, O_RDONLY)) == -1) { ui_error(op, "Unable to open '%s' for reading (%s)", @@ -803,10 +905,10 @@ static int ignore_conflicting_file(Options *op, ret = TRUE; - len = strlen(requiredString); + len = strlen(info.requiredString); for (i = 0; (i + len) <= stat_buf.st_size; i++) { - if ((strncmp(&file[i], requiredString, len) == 0) && + if ((strncmp(&file[i], info.requiredString, len) == 0) && (((i + len) == stat_buf.st_size) || (file[i+len] == '\0'))) { ret = FALSE; break; @@ -841,7 +943,8 @@ static int ignore_conflicting_file(Options *op, static void find_conflicting_files(Options *op, char *path, ConflictingFileInfo *files, - FileList *l) + FileList *l, + const NoRecursionDirectory *skipdirs) { int i; char *paths[2]; @@ -859,9 +962,10 @@ static void find_conflicting_files(Options *op, case FTS_F: case FTS_SLNONE: for (i = 0; files[i].name; i++) { + /* end compare at len e.g. so "libGL." matches "libGL.so.1" */ if (!strncmp(ent->fts_name, files[i].name, files[i].len) && !ignore_conflicting_file(op, ent->fts_path, - files[i].requiredString)) { + files[i])) { add_file_to_list(NULL, ent->fts_path, l); } } @@ -869,14 +973,17 @@ static void find_conflicting_files(Options *op, case FTS_DP: case FTS_D: - if (op->no_recursion || - /* - * stop recursing into any "nvidia-cg-toolkit" - * directory to prevent libGL.so.1 from being deleted - * (see bug 843595). - */ - !strcmp("nvidia-cg-toolkit", ent->fts_name)) + if (op->no_recursion) { fts_set(fts, ent, FTS_SKIP); + } else if (skipdirs) { + const NoRecursionDirectory *dir; + for (dir = skipdirs; dir->name; dir++) { + if ((dir->level < 0 || dir->level >= ent->fts_level) && + strcmp(ent->fts_name, dir->name) == 0) { + fts_set(fts, ent, FTS_SKIP); + } + } + } break; default: @@ -885,7 +992,7 @@ static void find_conflicting_files(Options *op, * and directories; traversing the hierarchy logically * to simplify handling of paths with symbolic links * to directories, we only need to handle broken links - * and, if recursion was disabled, directories. + * and, if recursion was not disabled, directories. */ break; } @@ -912,6 +1019,16 @@ static void find_conflicting_libraries(Options *op, int i, j; char *paths[4]; + /* + * stop recursing into any "nvidia-cg-toolkit" + * directory to prevent libGL.so.1 from being deleted + * (see bug 843595). + */ + static const NoRecursionDirectory skipdirs[] = { + { -1, "nvidia-cg-toolkit" }, + { 0, NULL } + }; + paths[0] = nvstrcat(prefix, "/", "lib", NULL); paths[1] = nvstrcat(prefix, "/", "lib64", NULL); paths[2] = nvstrcat(prefix, "/", "lib32", NULL); @@ -937,7 +1054,7 @@ static void find_conflicting_libraries(Options *op, } } - if (paths[i]) find_conflicting_files(op, paths[i], files, l); + if (paths[i]) find_conflicting_files(op, paths[i], files, l, skipdirs); } for (i = 0; i < 3; i++) diff --git a/common-utils/common-utils.c b/common-utils/common-utils.c index b2958a7..9bd349e 100644 --- a/common-utils/common-utils.c +++ b/common-utils/common-utils.c @@ -235,6 +235,28 @@ char *nvasprintf(const char *fmt, ...) } /* nvasprintf() */ +/* + * nv_append_sprintf() - similar to glib's g_string_append_printf(), except + * instead of operating on a GString it operates on a (char **). Appends a + * formatted string to the end of the dynamically-allocated string pointed to by + * *buf (or the empty string if *buf is NULL), potentially reallocating the + * string in the process. This function only returns on succcess. + */ +void nv_append_sprintf(char **buf, const char *fmt, ...) +{ + char *prefix, *suffix; + + prefix = *buf; + NV_VSNPRINTF(suffix, fmt); + + if (!prefix) { + *buf = suffix; + } else { + *buf = nvstrcat(prefix, suffix, NULL); + free(prefix); + free(suffix); + } +} /* @@ -413,7 +435,7 @@ TextRows *nv_format_text_rows(const char *prefix, } } - /* look for any newline inbetween a and b, and move b to it */ + /* look for any newline between a and b, and move b to it */ for (c = a; c < b; c++) if (*c == '\n') { b = c; break; } diff --git a/common-utils/common-utils.h b/common-utils/common-utils.h index 92c57dd..3bc934f 100644 --- a/common-utils/common-utils.h +++ b/common-utils/common-utils.h @@ -72,6 +72,7 @@ char *nvstrtolower(char *s); char *nvstrtoupper(char *s); char *nvstrchrnul(char *s, int c); char *nvasprintf(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void nv_append_sprintf(char **buf, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(2, 3); void nvfree(void *s); char *tilde_expansion(const char *str); @@ -184,4 +185,20 @@ static NV_INLINE uint64_t nv_encode_version(unsigned int major, #define NV_VERSION4(major, minor, micro, nano) \ nv_encode_version(major, minor, micro, nano) +/* + * Helper enum that can be used for boolean values that might or might not be + * set. Care should be taken to avoid simple boolean testing, as a value of + * NV_OPTIONAL_BOOL_DEFAULT would evaluate as true. + * + * The user is responsible for unconditionally initializing the default value of + * any such booleans to NV_OPTIONAL_BOOL_DEFAULT, before any code path that + * might optionally set their values is executed. + */ + +typedef enum { + NV_OPTIONAL_BOOL_DEFAULT = -1, + NV_OPTIONAL_BOOL_FALSE = FALSE, + NV_OPTIONAL_BOOL_TRUE = TRUE +} NVOptionalBool; + #endif /* __COMMON_UTILS_H__ */ diff --git a/common-utils/gen-manpage-opts-helper.c b/common-utils/gen-manpage-opts-helper.c index d2abb1c..532015d 100644 --- a/common-utils/gen-manpage-opts-helper.c +++ b/common-utils/gen-manpage-opts-helper.c @@ -89,7 +89,7 @@ static void print_option(const NVGetoptOption *o) * '^' : toggles bold on and off * '-' : is backslashified: "\-" * - * Whitespace is omited when italics or bold is on + * Whitespace is omitted when italics or bold is on */ italics = 0; diff --git a/common-utils/nvgetopt.h b/common-utils/nvgetopt.h index 5847546..fc735ee 100644 --- a/common-utils/nvgetopt.h +++ b/common-utils/nvgetopt.h @@ -34,8 +34,8 @@ /* * indicates that the option is a boolean value; the presence of the - * option will be interpretted as a TRUE value; if the option is - * prepended with '--no-', the option will be interpretted as a FALSE + * option will be interpreted as a TRUE value; if the option is + * prepended with '--no-', the option will be interpreted as a FALSE * value. On success, nvgetopt will return the parsed boolean value * through 'boolval'. */ @@ -505,6 +505,8 @@ int set_destinations(Options *op, Package *p) case FILE_TYPE_VDPAU_LIB: case FILE_TYPE_VDPAU_SYMLINK: + case FILE_TYPE_VDPAU_WRAPPER_LIB: + case FILE_TYPE_VDPAU_WRAPPER_SYMLINK: if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; @@ -975,26 +977,30 @@ int get_prefixes (Options *op) } /* get_prefixes() */ - /* - * add_kernel_module_to_package() - append the kernel module - * (contained in p->kernel_module_build_directory) to the package list - * for installation. + * add_kernel_module_helper() - append a kernel module (contained in + * p->kernel_module_build_directory/subdir) to the package list. */ -int add_kernel_module_to_package(Options *op, Package *p) +static void add_kernel_module_helper(Options *op, Package *p, + const char *filename, const char *subdir) { char *file, *name, *dst; - file = nvstrcat(p->kernel_module_build_directory, "/", - p->kernel_module_filename, NULL); + file = nvstrcat(p->kernel_module_build_directory, "/", subdir, + filename, NULL); name = strrchr(file, '/'); - if (name) name++; - if (!name) name = file; - dst = nvstrcat(op->kernel_module_installation_path, "/", - p->kernel_module_filename, NULL); + if (name && name[0]) { + name++; + } + + if (!name || !name[0]) { + name = file; + } + + dst = nvstrcat(op->kernel_module_installation_path, "/", filename, NULL); add_package_entry(p, file, @@ -1004,7 +1010,36 @@ int add_kernel_module_to_package(Options *op, Package *p) dst, FILE_TYPE_KERNEL_MODULE, FILE_TLS_CLASS_NONE, + FILE_COMPAT_ARCH_NONE, 0644); +} + +/* + * add_kernel_modules_to_package() - add any to-be-installed kernel modules + * to the package list for installation. + */ + +int add_kernel_modules_to_package(Options *op, Package *p) +{ + int i; + + if (op->multiple_kernel_modules) { + add_kernel_module_helper(op, p, p->kernel_frontend_module_filename, ""); + } + + for (i = 0; i < op->num_kernel_modules; i++) { + + char *tmp, *name; + + name = nvstrdup(p->kernel_module_filename); + + tmp = strrchr(name, '0'); + if (tmp) *tmp = *tmp + i; + + add_kernel_module_helper(op, p, name, ""); + + nvfree(name); + } return TRUE; @@ -1013,8 +1048,8 @@ int add_kernel_module_to_package(Options *op, Package *p) /* - * Clear the file type for each package entry that is not type - * FILE_TYPE_KERNEL_MODULE or FILE_TYPE_KERNEL_MODULE_SRC. + * Invalidate each package entry that is not type + * FILE_TYPE_KERNEL_MODULE{,_CMD,_SRC}. */ void remove_non_kernel_module_files_from_package(Options *op, Package *p) @@ -1032,7 +1067,7 @@ void remove_non_kernel_module_files_from_package(Options *op, Package *p) /* - * Clear the file type for each package entry that is an OpenGL File + * Invalidate each package entry that is an OpenGL file */ void remove_opengl_files_from_package(Options *op, Package *p) { @@ -1952,6 +1987,7 @@ void process_libGL_la_files(Options *op, Package *p) NULL, /* dst */ FILE_TYPE_LIBGL_LA, p->entries[i].tls_class, + p->entries[i].compat_arch, p->entries[i].mode); } @@ -2027,6 +2063,7 @@ void process_dot_desktop_files(Options *op, Package *p) NULL, /* dst */ FILE_TYPE_DOT_DESKTOP, p->entries[i].tls_class, + p->entries[i].compat_arch, p->entries[i].mode); } } @@ -2283,13 +2320,6 @@ static int get_x_paths_helper(Options *op, /* * attempt to determine the path through the various query mechanisms - * - * xorg-server >= 1.3 has a stupid version number regression because the - * reported Xorg version was tied to the server version, meaning what used - * to report 7.2 now reports, for example, 1.2.99.903. This means we set - * op->modular_xorg to FALSE. However, any server with this regression will - * also support the -showDefaultModulePath option so we should still be able - * to get the right path. */ /* @@ -2464,7 +2494,7 @@ static void get_x_library_and_module_paths(Options *op) * at the given path, keep reprompting until a valid path to a regular file or * symbolic link is given. This is just a thin wrapper around ui_get_input(). */ -char *get_filename(Options *op, const char *def, const char *fmt, ...) +char *get_filename(Options *op, const char *def, const char *msg) { struct stat stat_buf; char *file = NULL; @@ -2476,7 +2506,7 @@ char *get_filename(Options *op, const char *def, const char *fmt, ...) return nvstrdup(def); } - file = ui_get_input(op, file ? file : def, fmt); + file = ui_get_input(op, file ? file : def, "%s", msg); while (stat(file, &stat_buf) == -1 || !(S_ISREG(stat_buf.st_mode) || S_ISLNK(stat_buf.st_mode))) { @@ -2485,7 +2515,7 @@ char *get_filename(Options *op, const char *def, const char *fmt, ...) ui_message(op, "File \"%s\" does not exist, or is not a regular " "file. Please enter another filename.", file); - file = ui_get_input(op, oldfile ? oldfile : def, fmt); + file = ui_get_input(op, oldfile ? oldfile : def, "%s", msg); nvfree(oldfile); } @@ -33,7 +33,7 @@ void select_tls_class(Options *op, Package *p); /* XXX move? */ int set_destinations(Options *op, Package *p); /* XXX move? */ int get_license_acceptance(Options *op); /* XXX move? */ int get_prefixes(Options *op); /* XXX move? */ -int add_kernel_module_to_package(Options *op, Package *p); +int add_kernel_modules_to_package(Options *op, Package *p); void remove_non_kernel_module_files_from_package(Options *op, Package *p); void remove_opengl_files_from_package(Options *op, Package *p); void remove_trailing_slashes(char *s); @@ -65,7 +65,7 @@ void process_dot_desktop_files(Options *op, Package *p); int set_security_context(Options *op, const char *filename); void get_default_prefixes_and_paths(Options *op); char *nv_strreplace(char *src, char *orig, char *replace); -char *get_filename(Options *op, const char *def, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(3, 4); +char *get_filename(Options *op, const char *def, const char *msg); int secure_delete(Options *op, const char *file); void invalidate_package_entry(PackageEntry *entry); diff --git a/install-from-cwd.c b/install-from-cwd.c index 9803a0f..19e6b84 100644 --- a/install-from-cwd.c +++ b/install-from-cwd.c @@ -96,7 +96,7 @@ int install_from_cwd(Options *op) */ if ((p = parse_manifest(op)) == NULL) goto failed; - + ui_set_title(op, "%s (%s)", p->description, p->version); /* @@ -153,25 +153,7 @@ int install_from_cwd(Options *op) /* attempt to build a kernel module for the target kernel */ if (!op->no_kernel_module) { - - /* Offer the DKMS option if DKMS exists and the kernel module sources - * will be installed somewhere. Don't offer DKMS as an option if module - * signing was requested. */ - - if (find_system_util("dkms") && !op->no_kernel_module_source && - !(op->module_signing_secret_key && op->module_signing_public_key)) { - op->dkms = ui_yes_no(op, op->dkms, - "Would you like to register the kernel module " - "sources with DKMS? This will allow DKMS to " - "automatically build a new module, if you " - "install a different kernel later."); - } - - /* Only do the normal kernel module install if not using DKMS */ - - if (op->dkms) { - op->no_kernel_module = TRUE; - } else if (!install_kernel_module(op, p)) { + if (!install_kernel_module(op, p)) { goto failed; } } else { @@ -263,6 +245,14 @@ int install_from_cwd(Options *op) if (!uninstall_existing_driver(op, FALSE)) goto failed; } + /* + * Determine whether the VDPAU wrapper should be installed: this must be + * done after uninstallation of the previous driver, to avoid detecting a + * leftover wrapper, and before building the command list. + */ + + should_install_vdpau_wrapper(op, p); + /* build a list of operations to execute to do the install */ if ((c = build_command_list(op, p)) == NULL) goto failed; @@ -397,6 +387,26 @@ static int install_kernel_module(Options *op, Package *p) { PrecompiledInfo *precompiled_info; + /* Offer the DKMS option if DKMS exists and the kernel module sources + * will be installed somewhere. Don't offer DKMS as an option if module + * signing was requested. */ + + if (find_system_util("dkms") && !op->no_kernel_module_source && + !(op->module_signing_secret_key && op->module_signing_public_key)) { + op->dkms = ui_yes_no(op, op->dkms, + "Would you like to register the kernel module " + "sources with DKMS? This will allow DKMS to " + "automatically build a new module, if you " + "install a different kernel later."); + } + + /* Only do the normal kernel module install if not using DKMS */ + + if (op->dkms) { + op->no_kernel_module = TRUE; + return TRUE; + } + /* determine where to install the kernel module */ if (!determine_kernel_module_installation_path(op)) return FALSE; @@ -420,9 +430,12 @@ static int install_kernel_module(Options *op, Package *p) if ((precompiled_info = find_precompiled_kernel_interface(op, p))) { + int i; + + /* - * we have a prebuilt kernel interface, so now link the kernel - * interface with the binary portion of the kernel module. + * we have a prebuilt kernel interface package, so now link the + * kernel interface files to produce the kernel module. * * XXX if linking fails, maybe we should fall through and * attempt to build the kernel module? No, if linking fails, @@ -430,8 +443,6 @@ static int install_kernel_module(Options *op, Package *p) * abort. */ - int i; - for (i = 0; i < precompiled_info->num_files; i++) { if (!link_kernel_module(op, p, p->kernel_module_build_directory, &(precompiled_info->files[i]))) { @@ -477,9 +488,9 @@ static int install_kernel_module(Options *op, Package *p) if (!test_kernel_module(op, p)) return FALSE; - /* add the kernel module to the list of things to install */ + /* add the kernel modules to the list of things to install */ - if (!add_kernel_module_to_package(op, p)) return FALSE; + if (!add_kernel_modules_to_package(op, p)) return FALSE; return TRUE; } @@ -495,7 +506,7 @@ int add_this_kernel(Options *op) { Package *p; PrecompiledFileInfo *fileInfos; - int num_files; + int num_expected_files = 1; /* parse the manifest */ @@ -505,14 +516,17 @@ int add_this_kernel(Options *op) if (!determine_kernel_source_path(op, p)) goto failed; + if (op->multiple_kernel_modules) + num_expected_files = op->num_kernel_modules + 1; + /* build the precompiled files */ - num_files = build_kernel_interface(op, p, &fileInfos); - if (!num_files) goto failed; + if (num_expected_files != build_kernel_interface(op, p, &fileInfos)) + goto failed; /* pack the precompiled files */ - if (!pack_precompiled_files(op, p, num_files, fileInfos)) + if (!pack_precompiled_files(op, p, num_expected_files, fileInfos)) goto failed; free_package(p); @@ -564,7 +578,7 @@ int add_this_kernel(Options *op) static Package *parse_manifest (Options *op) { - char *buf, *c, *flag , *tmpstr; + char *buf, *c, *flag, *tmpstr, *module_suffix = ""; int done, n, line; int fd, ret, len = 0; struct stat stat_buf, entry_stat_buf; @@ -601,17 +615,30 @@ static Package *parse_manifest (Options *op) p->version = get_next_line(ptr, &ptr, manifest, len); if (!p->version) goto invalid_manifest_file; - /* new third line is the kernel interface filename */ + /* Ignore the third line */ line++; - p->kernel_interface_filename = get_next_line(ptr, &ptr, manifest, len); - if (!p->kernel_interface_filename) goto invalid_manifest_file; + nvfree(get_next_line(ptr, &ptr, manifest, len)); /* the fourth line is the kernel module name */ line++; - p->kernel_module_name = get_next_line(ptr, &ptr, manifest, len); - if (!p->kernel_module_name) goto invalid_manifest_file; + tmpstr = get_next_line(ptr, &ptr, manifest, len); + if (!tmpstr) goto invalid_manifest_file; + + if (op->multiple_kernel_modules) { + module_suffix = "0"; + p->kernel_frontend_module_name = nvstrcat(tmpstr, "-frontend", NULL); + p->kernel_frontend_module_filename = + nvstrcat(p->kernel_frontend_module_name, ".ko", NULL); + p->kernel_frontend_interface_filename = nvstrdup("nv-linuxfrontend.o"); + } + + p->kernel_module_name = nvstrcat(tmpstr, module_suffix, NULL); + p->kernel_module_filename = nvstrcat(p->kernel_module_name, ".ko", NULL); + p->kernel_interface_filename = nvstrcat("nv-linux", module_suffix, ".o", NULL); + + nvfree(tmpstr); /* * the fifth line is a whitespace-separated list of kernel modules @@ -870,6 +897,7 @@ void add_package_entry(Package *p, char *dst, PackageEntryFileType type, PackageEntryFileTlsClass tls_class, + PackageEntryFileCompatArch compat_arch, mode_t mode) { int n; @@ -882,15 +910,16 @@ void add_package_entry(Package *p, memset(&p->entries[n], 0, sizeof(PackageEntry)); - p->entries[n].file = file; - p->entries[n].path = path; - p->entries[n].name = name; - p->entries[n].target = target; - p->entries[n].dst = dst; - p->entries[n].type = type; - p->entries[n].tls_class = tls_class; - p->entries[n].mode = mode; - p->entries[n].caps = get_file_type_capabilities(type); + p->entries[n].file = file; + p->entries[n].path = path; + p->entries[n].name = name; + p->entries[n].target = target; + p->entries[n].dst = dst; + p->entries[n].type = type; + p->entries[n].tls_class = tls_class; + p->entries[n].mode = mode; + p->entries[n].caps = get_file_type_capabilities(type); + p->entries[n].compat_arch = compat_arch; if (stat(p->entries[n].file, &stat_buf) != -1) { p->entries[n].inode = stat_buf.st_ino; @@ -954,6 +983,10 @@ static void free_package(Package *p) nvfree((char *) p->entries); + nvfree(p->kernel_frontend_module_filename); + nvfree(p->kernel_frontend_module_name); + nvfree(p->kernel_frontend_interface_filename); + nvfree((char *) p); } /* free_package() */ @@ -966,7 +999,7 @@ static void free_package(Package *p) static int assisted_module_signing(Options *op, Package *p) { - int generate_keys = FALSE, do_sign = FALSE, secureboot; + int generate_keys = FALSE, do_sign = FALSE, secureboot, i; secureboot = secure_boot_enabled(); @@ -1148,9 +1181,27 @@ guess_fail: } /* Now that we have keys (user-supplied or installer-generated), - * sign the kernel module which we built earlier. */ - if (!sign_kernel_module(op, p->kernel_module_build_directory, TRUE)) { - return FALSE; + * sign the kernel module/s which we built earlier. */ + + for (i = 0; i < op->num_kernel_modules; i++) { + char module_instance_str[5]; + memset(module_instance_str, 0, sizeof(module_instance_str)); + + if (op->multiple_kernel_modules) { + snprintf(module_instance_str, sizeof(module_instance_str), "%d", i); + } + + if (!sign_kernel_module(op, p->kernel_module_build_directory, + module_instance_str, TRUE)) { + return FALSE; + } + } + + if (op->multiple_kernel_modules) { + if (!sign_kernel_module(op, p->kernel_module_build_directory, + "frontend", TRUE)) { + return FALSE; + } } if (generate_keys) { @@ -1224,6 +1275,7 @@ guess_fail: NULL, /* dst */ FILE_TYPE_MODULE_SIGNING_KEY, FILE_TLS_CLASS_NONE, + FILE_COMPAT_ARCH_NONE, 0444); ui_message(op, "An X.509 certificate containing the public signing " @@ -1256,6 +1308,7 @@ guess_fail: NULL, /* dst */ FILE_TYPE_MODULE_SIGNING_KEY, FILE_TLS_CLASS_NONE, + FILE_COMPAT_ARCH_NONE, 0400); ui_message(op, "The private signing key will be installed to %s/%s. " @@ -53,23 +53,25 @@ static int check_for_loaded_kernel_module(Options *op, const char *); static void check_for_warning_messages(Options *op); static int rmmod_kernel_module(Options *op, const char *); static PrecompiledInfo *download_updated_kernel_interface(Options*, Package*, - const char*); + const char*, + char* const*); static int fbdev_check(Options *op, Package *p); static int xen_check(Options *op, Package *p); static int preempt_rt_check(Options *op, Package *p); static PrecompiledInfo *scan_dir(Options *op, Package *p, const char *directory_name, - const char *proc_version_string); + const char *proc_version_string, + char *const *search_filelist); static char *build_distro_precompiled_kernel_interface_dir(Options *op); static char *convert_include_path_to_source_path(const char *inc); -static char *guess_kernel_module_filename(Options *op); static char *get_machine_arch(Options *op); static int init_libkmod(void); static void close_libkmod(void); static int run_conftest(Options *op, Package *p, const char *args, char **result); +static void replace_zero(char* filename, int i); /* libkmod handle and function pointers */ static void *libkmod = NULL; @@ -80,6 +82,7 @@ static int (*lkmod_module_new_from_path)(struct kmod_ctx*, const char*, struct kmod_module**) = NULL; static int (*lkmod_module_insert_module)(struct kmod_module*, unsigned int, const char*) = NULL; +static void free_search_filelist(char **); /* * Message text that is used by several error messages. @@ -423,23 +426,24 @@ int determine_kernel_output_path(Options *op) * the linked module and append the signature. */ static int attach_signature(Options *op, Package *p, - const PrecompiledFileInfo *fileInfo) { + const PrecompiledFileInfo *fileInfo, + const char *module_name) { uint32 actual_crc; - char *module_filename; + char *module_path; int ret = FALSE, command_ret; ui_log(op, "Attaching module signature to linked kernel module."); - module_filename = nvstrcat(p->kernel_module_build_directory, "/", - p->kernel_module_filename, NULL); + module_path = nvstrcat(p->kernel_module_build_directory, "/", + module_name, NULL); - command_ret = verify_crc(op, module_filename, fileInfo->linked_module_crc, + command_ret = verify_crc(op, module_path, fileInfo->linked_module_crc, &actual_crc); if (command_ret) { FILE *module_file; - module_file = fopen(module_filename, "a+"); + module_file = fopen(module_path, "a+"); if (module_file && fileInfo->signature_size) { command_ret = fwrite(fileInfo->signature, 1, @@ -485,11 +489,27 @@ attach_done: ui_error(op, "Failed to attach signature."); } - nvfree(module_filename); + nvfree(module_path); return ret; } /* attach_signature() */ +/* + * Look for the presence of char '0' in filename and replace + * it with the integer value passed as input. This is used + * especially for multiple kernel module builds. + */ + +static void replace_zero(char *filename, int i) +{ + char *name; + + if (i < 0 || i > 9) return; + + name = strrchr(filename, '0'); + if (name) *name = *name + i; +} + /* * link_kernel_module() - link the prebuilt kernel interface against @@ -519,14 +539,12 @@ int link_kernel_module(Options *op, Package *p, const char *build_directory, return FALSE; } - p->kernel_module_filename = guess_kernel_module_filename(op); - cmd = nvstrcat("cd ", build_directory, "; ", op->utils[LD], " ", LD_OPTIONS, " -o ", fileInfo->linked_module_name, " ", fileInfo->name, " ", fileInfo->core_object_name, NULL); - + ret = run_command(op, cmd, &result, TRUE, 0, TRUE); - + free(cmd); if (ret != 0) { @@ -540,7 +558,8 @@ int link_kernel_module(Options *op, Package *p, const char *build_directory, PRECOMPILED_ATTR(LINKED_MODULE_CRC); if ((fileInfo->attributes & attrmask) == attrmask) { - return attach_signature(op, p, fileInfo); + return attach_signature(op, p, fileInfo, + fileInfo->linked_module_name); } return TRUE; @@ -548,6 +567,62 @@ int link_kernel_module(Options *op, Package *p, const char *build_directory, } /* link_kernel_module() */ + +static int build_kernel_module_helper(Options *op, const char *dir, + const char *module, int num_instances) +{ + int ret; + char *instances = NULL, *cmd, *tmp; + + tmp = op->multiple_kernel_modules && num_instances ? + nvasprintf("%d", num_instances) : NULL; + instances = nvstrcat(" NV_BUILD_MODULE_INSTANCES=", tmp, NULL); + nvfree(tmp); + + tmp = nvasprintf("Building %s kernel module:", module); + ui_status_begin(op, tmp, "Building"); + nvfree(tmp); + + cmd = nvstrcat("cd ", dir, "; make module", + " SYSSRC=", op->kernel_source_path, + " SYSOUT=", op->kernel_output_path, + instances, NULL); + nvfree(instances); + + ret = run_command(op, cmd, NULL, TRUE, 25, TRUE); + + nvfree(cmd); + + if (ret != 0) { + ui_status_end(op, "Error."); + ui_error(op, "Unable to build the %s kernel module.", module); + /* XXX need more descriptive error message */ + } + + ui_status_end(op, "done."); + + return ret == 0; +} + + +static int check_file(Options *op, Package *p, const char *filename, + const char *modname) +{ + int ret; + char *path; + + path = nvstrcat(p->kernel_module_build_directory, "/", filename, NULL); + ret = access(path, F_OK); + nvfree(path); + + if (ret == -1) { + ui_error(op, "The NVIDIA %s module was not created.", modname); + } + + return ret != -1; +} + + /* * build_kernel_module() - determine the kernel include directory, * copy the kernel module source files into a temporary directory, and @@ -556,7 +631,7 @@ int link_kernel_module(Options *op, Package *p, const char *build_directory, int build_kernel_module(Options *op, Package *p) { - char *result, *cmd, *tmp; + char *result, *cmd; int len, ret; /* @@ -583,63 +658,35 @@ int build_kernel_module(Options *op, Package *p) if (!xen_check(op, p)) return FALSE; if (!preempt_rt_check(op, p)) return FALSE; - cmd = nvstrcat("cd ", p->kernel_module_build_directory, - "; make print-module-filename", - " SYSSRC=", op->kernel_source_path, - " SYSOUT=", op->kernel_output_path, NULL); - - ret = run_command(op, cmd, &p->kernel_module_filename, FALSE, 0, FALSE); - - free(cmd); - - if (ret != 0) { - ui_error(op, "Unable to determine the NVIDIA kernel module filename."); - nvfree(result); - return FALSE; - } - ui_log(op, "Cleaning kernel module build directory."); len = strlen(p->kernel_module_build_directory) + 32; cmd = nvalloc(len); - + snprintf(cmd, len, "cd %s; make clean", p->kernel_module_build_directory); ret = run_command(op, cmd, &result, TRUE, 0, TRUE); free(result); free(cmd); - - ui_status_begin(op, "Building kernel module:", "Building"); - - cmd = nvstrcat("cd ", p->kernel_module_build_directory, - "; make module", - " SYSSRC=", op->kernel_source_path, - " SYSOUT=", op->kernel_output_path, NULL); - - ret = run_command(op, cmd, &result, TRUE, 25, TRUE); - free(cmd); + ret = build_kernel_module_helper(op, p->kernel_module_build_directory, + "NVIDIA", op->num_kernel_modules); - if (ret != 0) { - ui_status_end(op, "Error."); - ui_error(op, "Unable to build the NVIDIA kernel module."); - /* XXX need more descriptive error message */ + if (!ret) { return FALSE; } - /* check that the file actually exists */ + /* check that the frontend file actually exists */ + if (op->multiple_kernel_modules) { + if (!check_file(op, p, p->kernel_frontend_module_filename, "frontend")) { + return FALSE; + } + } - tmp = nvstrcat(p->kernel_module_build_directory, "/", - p->kernel_module_filename, NULL); - if (access(tmp, F_OK) == -1) { - free(tmp); - ui_status_end(op, "Error."); - ui_error(op, "The NVIDIA kernel module was not created."); + /* check that the file actually exists */ + if (!check_file(op, p, p->kernel_module_filename, "kernel")) { return FALSE; } - free(tmp); - - ui_status_end(op, "done."); ui_log(op, "Kernel module compilation complete."); @@ -654,9 +701,11 @@ int build_kernel_module(Options *op, Package *p) * for ensuring that the kernel module is already built successfully and that * op->module_signing_{secret,public}_key are set. */ -int sign_kernel_module(Options *op, const char *build_directory, int status) { +int sign_kernel_module(Options *op, const char *build_directory, + const char *module_suffix, int status) { char *cmd, *mod_sign_cmd, *mod_sign_hash; int ret, success; + char *build_module_instances_parameter; /* if module_signing_script isn't set, then set mod_sign_cmd below to end * the nvstrcat() that builds cmd early. */ @@ -673,11 +722,22 @@ int sign_kernel_module(Options *op, const char *build_directory, int status) { ui_status_begin(op, "Signing kernel module:", "Signing"); } + if (op->multiple_kernel_modules) { + build_module_instances_parameter = + nvasprintf(" NV_BUILD_MODULE_INSTANCES=%d", + NV_MAX_MODULE_INSTANCES); + } + else { + build_module_instances_parameter = nvstrdup(""); + } + cmd = nvstrcat("cd ", build_directory, "; make module-sign" " SYSSRC=", op->kernel_source_path, " SYSOUT=", op->kernel_output_path, " MODSECKEY=", op->module_signing_secret_key, " MODPUBKEY=", op->module_signing_public_key, + " NV_MODULE_SUFFIX=", module_suffix, + build_module_instances_parameter, mod_sign_cmd ? mod_sign_cmd : "", mod_sign_hash ? mod_sign_hash : "", NULL); @@ -687,6 +747,7 @@ int sign_kernel_module(Options *op, const char *build_directory, int status) { nvfree(mod_sign_hash); nvfree(mod_sign_cmd); nvfree(cmd); + nvfree(build_module_instances_parameter); if (status) { ui_status_end(op, success ? "done." : "Failed to sign kernel module."); @@ -706,7 +767,9 @@ int sign_kernel_module(Options *op, const char *build_directory, int status) { */ static int create_detached_signature(Options *op, Package *p, const char *build_dir, - PrecompiledFileInfo *fileInfo) + PrecompiledFileInfo *fileInfo, + const char *module_suffix, + const char *module_filename) { int ret, command_ret; struct stat st; @@ -722,7 +785,7 @@ static int create_detached_signature(Options *op, Package *p, goto done; } - module_path = nvstrcat(build_dir, "/", p->kernel_module_filename, NULL); + module_path = nvstrcat(build_dir, "/", module_filename, NULL); command_ret = stat(module_path, &st); if (command_ret != 0) { @@ -738,7 +801,7 @@ static int create_detached_signature(Options *op, Package *p, ui_status_update(op, .50, "Signing linked module"); - ret = sign_kernel_module(op, build_dir, FALSE); + ret = sign_kernel_module(op, build_dir, module_suffix, FALSE); if (!ret) { error = "Failed to sign the linked kernel module."; @@ -763,7 +826,7 @@ done: } else { ui_status_end(op, "Error."); if (error) { - ui_error(op, error); + ui_error(op, "%s", error); } } @@ -772,30 +835,115 @@ done: } /* create_detached_signature() */ - /* - * build_kernel_interface() - build the kernel interface(s), and store any - * built interfaces in a newly allocated PrecompiledFileInfo array, a pointer - * to which is passed back to the caller. Return the number of packaged - * interface files, or 0 on error. + * build_kernel_interface_file() - build the kernel interface(s). + * Returns true if the build was successful, or false on error. * * This is done by copying the sources to a temporary working * directory and building the kernel interface in that directory. - * The tmpdir is removed when complete. + */ + +static int build_kernel_interface_file(Options *op, const char *tmpdir, + PrecompiledFileInfo *fileInfo, + const char *kernel_interface_filename, + const char *module_suffix, + const char *build_module_instances_parameter) +{ + char *cmd; + char *kernel_interface; + int command_ret; + + cmd = nvstrcat("cd ", tmpdir, "; make ", + kernel_interface_filename, + " SYSSRC=", op->kernel_source_path, + " SYSOUT=", op->kernel_output_path, + " NV_MODULE_SUFFIX=", module_suffix, + build_module_instances_parameter, NULL); + + command_ret = run_command(op, cmd, NULL, TRUE, 25 /* XXX */, TRUE); + + free(cmd); + + if (command_ret != 0) { + ui_status_end(op, "Error."); + ui_error(op, "Unable to build the NVIDIA kernel module interface."); + /* XXX need more descriptive error message */ + return FALSE; + } + + /* check that the file exists */ + + kernel_interface = nvstrcat(tmpdir, "/", + kernel_interface_filename, NULL); + + if (access(kernel_interface, F_OK) == -1) { + ui_status_end(op, "Error."); + ui_error(op, "The NVIDIA kernel module interface was not created."); + nvfree(kernel_interface); + return FALSE; + } + + nvfree(kernel_interface); + return TRUE; +} + +/* + * pack_kernel_interface() - Store the input built interfaces in the + * PrecompiledFileInfo array. + * + * Returns true if the packing was successful, or false on error. + */ + +static int pack_kernel_interface(Options *op, Package *p, + const char *build_dir, + PrecompiledFileInfo *fileInfo, + const char *module_suffix, + const char *kernel_interface, + const char *module_filename, + const char *core_file) +{ + int command_ret; + + command_ret = precompiled_read_interface(fileInfo, kernel_interface, + module_filename, + core_file); + + if (command_ret) { + if (op->module_signing_secret_key && op->module_signing_public_key) { + if (!create_detached_signature(op, p, build_dir, fileInfo, + module_suffix, + module_filename)) { + return FALSE; + } + } + return TRUE; + } + + return FALSE; +} + +/* + * build_kernel_interface() - build the kernel interface(s), and store any + * built interfaces in a newly allocated PrecompiledFileInfo array. Return + * the number of packaged interface files, or 0 on error. + * + * The tmpdir created to build the kernel interface is removed before exit. + * + * For multi-RM, the frontend module interface should be compiled and + * packaged too. * * XXX this and build_kernel_module() should be merged. - * XXX for multi-RM the dispatch module should be compiled separately, then - * packaged whole with file type PRECOMPILED_FILE_TYPE_MODULE. */ int build_kernel_interface(Options *op, Package *p, PrecompiledFileInfo ** fileInfos) { char *tmpdir = NULL; - char *cmd = NULL; char *dstfile = NULL; - int files_packaged = 0, command_ret, i; - const int num_files = 1; /* XXX multi-RM */ + int files_packaged = 0, i; + int num_files = 1; + int ret_status; + char *build_module_instances_parameter = NULL; *fileInfos = NULL; @@ -826,67 +974,113 @@ int build_kernel_interface(Options *op, Package *p, touch_directory(op, p->kernel_module_build_directory); - *fileInfos = nvalloc(sizeof(PrecompiledFileInfo) * num_files); + if (op->multiple_kernel_modules) { + num_files = op->num_kernel_modules; + build_module_instances_parameter = + nvasprintf(" NV_BUILD_MODULE_INSTANCES=%d", + NV_MAX_MODULE_INSTANCES); + } + else { + build_module_instances_parameter = nvstrdup(""); + } + + *fileInfos = nvalloc(sizeof(PrecompiledFileInfo) * (num_files + 1)); for (i = 0; i < num_files; i++) { char *kernel_interface, *kernel_module_filename; + char *kernel_interface_filename; PrecompiledFileInfo *fileInfo = *fileInfos + i; + char module_instance_str[5]; + + memset(module_instance_str, 0, sizeof(module_instance_str)); + if (op->multiple_kernel_modules) { + snprintf(module_instance_str, sizeof(module_instance_str), "%d", i); + } + + kernel_interface_filename = nvstrdup(p->kernel_interface_filename); + + replace_zero(kernel_interface_filename, i); /* build the kernel interface */ ui_status_begin(op, "Building kernel interface:", "Building (%d/%d)", i + 1, num_files); - cmd = nvstrcat("cd ", tmpdir, "; make ", p->kernel_interface_filename, - " SYSSRC=", op->kernel_source_path, NULL); - - command_ret = run_command(op, cmd, NULL, TRUE, 25 /* XXX */, TRUE); + ret_status = build_kernel_interface_file(op, tmpdir, fileInfo, + kernel_interface_filename, module_instance_str, + build_module_instances_parameter); - if (command_ret != 0) { - ui_status_end(op, "Error."); - ui_error(op, "Unable to build the NVIDIA kernel module interface."); - /* XXX need more descriptive error message */ + if (!ret_status) { + nvfree(kernel_interface_filename); goto failed; } - /* check that the file exists */ + ui_status_end(op, "done."); + + ui_log(op, "Kernel module interface compilation complete."); kernel_interface = nvstrcat(tmpdir, "/", - p->kernel_interface_filename, NULL); + kernel_interface_filename, NULL); + + kernel_module_filename = nvstrdup(p->kernel_module_filename); - if (access(kernel_interface, F_OK) == -1) { - ui_status_end(op, "Error."); - ui_error(op, "The NVIDIA kernel module interface was not created."); - nvfree(kernel_interface); + replace_zero(kernel_module_filename, i); + + /* add the kernel interface to the list of files to be packaged */ + ret_status = pack_kernel_interface(op, p, tmpdir, fileInfo, + module_instance_str, + kernel_interface, + kernel_module_filename, + "nv-kernel.o"); + + nvfree(kernel_interface); + nvfree(kernel_interface_filename); + nvfree(kernel_module_filename); + + if (!ret_status) + goto failed; + + files_packaged++; + } + + if (op->multiple_kernel_modules) { + char *frontend_interface_filename; + PrecompiledFileInfo *fileInfo = *fileInfos + num_files; + + ui_status_begin(op, "Building frontend interface: ", "Building"); + + ret_status = build_kernel_interface_file(op, tmpdir, fileInfo, + p->kernel_frontend_interface_filename, "frontend", + build_module_instances_parameter); + + if (!ret_status) goto failed; - } ui_status_end(op, "done."); - ui_log(op, "Kernel module interface compilation complete."); + ui_log(op, "Frontend kernel module interface compilation complete."); + + frontend_interface_filename = nvstrcat(tmpdir, "/", + p->kernel_frontend_interface_filename, + NULL); /* add the kernel interface to the list of files to be packaged */ + ret_status = pack_kernel_interface(op, p, tmpdir, fileInfo, + "frontend", + frontend_interface_filename, + p->kernel_frontend_module_filename, + ""); - kernel_module_filename = guess_kernel_module_filename(op); - command_ret = precompiled_read_interface(fileInfo, kernel_interface, - kernel_module_filename, - "nv-kernel.o"); - nvfree(kernel_interface); - nvfree(kernel_module_filename); + nvfree(frontend_interface_filename); - if (command_ret) { - if (op->module_signing_secret_key && op->module_signing_public_key) { - if (!create_detached_signature(op, p, tmpdir, fileInfo)) { - goto failed; - } - } - files_packaged++; - } else { + if (!ret_status) goto failed; - } + + files_packaged++; } failed: + nvfree(build_module_instances_parameter); if (files_packaged == 0) { nvfree(*fileInfos); @@ -897,7 +1091,7 @@ failed: remove_directory(op, tmpdir); nvfree(tmpdir); } - if (cmd) nvfree(cmd); + if (dstfile) nvfree(dstfile); return files_packaged; @@ -1072,6 +1266,118 @@ kmod_done: } /* do_insmod() */ +/* + * Determine if the load error be ignored or not. Also print detailed + * error messages corresponding to the return status from do_install(). + * + * Returns true if user chooses to ignore the load error, else, false. + */ + +static int ignore_load_error(Options *op, Package *p, + const char *module_filename, + const char* data, int insmod_status) +{ + int ignore_error = FALSE, secureboot, module_sig_force, enokey; + const char *probable_reason, *signature_related; + + enokey = (-insmod_status == ENOKEY); + secureboot = (secure_boot_enabled() == 1); + module_sig_force = + (test_kernel_config_option(op, p, "CONFIG_MODULE_SIG_FORCE") == + KERNEL_CONFIG_OPTION_DEFINED); + + if (enokey) { + probable_reason = ","; + signature_related = ""; + } else if (module_sig_force) { + probable_reason = ". CONFIG_MODULE_SIG_FORCE is set on the target " + "kernel, so this is likely"; + } else if (secureboot) { + probable_reason = ". Secure boot is enabled on this system, so " + "this is likely"; + } else { + probable_reason = ", possibly"; + } + + if (!enokey) { + signature_related = "if this module loading failure is due to the " + "lack of a trusted signature, "; + } + + if (enokey || secureboot || module_sig_force || op->expert) { + if (op->kernel_module_signed) { + + ignore_error = ui_yes_no(op, TRUE, + "The signed kernel module failed to " + "load%s because the kernel does not " + "trust any key which is capable of " + "verifying the module signature. " + "Would you like to install the signed " + "kernel module anyway?\n\nNote that %s" + "you will not be able to load the " + "installed module until after a key " + "that can verify the module signature " + "is added to a key database that is " + "trusted by the kernel. This will " + "likely require rebooting your " + "computer.", probable_reason, + signature_related); + } else { + const char *secureboot_message, *dkms_message; + + secureboot_message = secureboot == 1 ? + "and sign the kernel module when " + "prompted to do so." : + "and set the --module-signing-secret-" + "key and --module-signing-public-key " + "options on the command line, or run " + "the installer in expert mode to " + "enable the interactive module " + "signing prompts."; + + dkms_message = op->dkms ? " Module signing is incompatible " + "with DKMS, so please select the " + "non-DKMS option when building the " + "kernel module to be signed." : ""; + ui_error(op, "The kernel module failed to load%s because it " + "was not signed by a key that is trusted by the " + "kernel. Please try installing the driver again, %s%s", + probable_reason, secureboot_message, dkms_message); + } + } + + if (ignore_error) { + ui_log(op, "An error was encountered when loading the kernel " + "module, but that error was ignored, and the kernel module " + "will be installed, anyway. The error was: %s", data); + } else { + ui_error(op, "Unable to load the kernel module '%s'. This " + "happens most frequently when this kernel module was " + "built against the wrong or improperly configured " + "kernel sources, with a version of gcc that differs " + "from the one used to build the target kernel, or " + "if a driver such as rivafb, nvidiafb, or nouveau is " + "present and prevents the NVIDIA kernel module from " + "obtaining ownership of the NVIDIA graphics device(s), " + "or no NVIDIA GPU installed in this system is supported " + "by this NVIDIA Linux graphics driver release.\n\n" + "Please see the log entries 'Kernel module load " + "error' and 'Kernel messages' at the end of the file " + "'%s' for more information.", + module_filename, op->log_file_name); + + /* + * if in expert mode, run_command() would have caused this to + * be written to the log file; so if not in expert mode, print + * the output now. + */ + + if (!op->expert) ui_log(op, "Kernel module load error: %s", data); + } + + return ignore_error; +} /* ignore_load_error() */ + /* * test_kernel_module() - attempt to insmod the kernel module and then @@ -1123,114 +1429,36 @@ int test_kernel_module(Options *op, Package *p) nvfree(cmd); } - /* Load nvidia.ko */ + if (op->multiple_kernel_modules) { + /* Load nvidia-frontend.ko */ + + module_path = nvstrcat(p->kernel_module_build_directory, "/", + p->kernel_frontend_module_filename, NULL); + ret = do_insmod(op, module_path, &data); + nvfree(module_path); + + if (ret != 0) { + ret = ignore_load_error(op, p, p->kernel_frontend_module_filename, + data, ret); + goto test_exit; + } + + nvfree(data); + } + + /* + * Load nvidia0.ko while building multiple kernel modules or + * load nvidia.ko for non-multiple-kernel-module/simple builds. + */ + module_path = nvstrcat(p->kernel_module_build_directory, "/", p->kernel_module_filename, NULL); ret = do_insmod(op, module_path, &data); nvfree(module_path); if (ret != 0) { - int ignore_error = FALSE, secureboot, module_sig_force, enokey; - const char *probable_reason, *signature_related; - - enokey = (-ret == ENOKEY); - secureboot = (secure_boot_enabled() == 1); - module_sig_force = - (test_kernel_config_option(op, p, "CONFIG_MODULE_SIG_FORCE") == - KERNEL_CONFIG_OPTION_DEFINED); - - if (enokey) { - probable_reason = ","; - signature_related = ""; - } else if (module_sig_force) { - probable_reason = ". CONFIG_MODULE_SIG_FORCE is set on the target " - "kernel, so this is likely"; - } else if (secureboot) { - probable_reason = ". Secure boot is enabled on this system, so " - "this is likely"; - } else { - probable_reason = ", possibly"; - } - - if (!enokey) { - signature_related = "if this module loading failure is due to the " - "lack of a trusted signature, "; - } - - if (enokey || secureboot || module_sig_force || op->expert) { - if (op->kernel_module_signed) { - - ignore_error = ui_yes_no(op, TRUE, - "The signed kernel module failed to " - "load%s because the kernel does not " - "trust any key which is capable of " - "verifying the module signature. " - "Would you like to install the signed " - "kernel module anyway?\n\nNote that %s" - "you will not be able to load the " - "installed module until after a key " - "that can verify the module signature " - "is added to a key database that is " - "trusted by the kernel. This will " - "likely require rebooting your " - "computer.", probable_reason, - signature_related); - } else { - const char *secureboot_message, *dkms_message; - - secureboot_message = secureboot == 1 ? - "and sign the kernel module when " - "prompted to do so." : - "and set the --module-signing-secret-" - "key and --module-signing-public-key " - "options on the command line, or run " - "the installer in expert mode to " - "enable the interactive module " - "signing prompts."; - - dkms_message = op->dkms ? " Module signing is incompatible " - "with DKMS, so please select the " - "non-DKMS option when building the " - "kernel module to be signed." : ""; - ui_error(op, "The kernel module failed to load%s because it " - "was not signed by a key that is trusted by the " - "kernel. Please try installing the driver again, %s%s", - probable_reason, secureboot_message, dkms_message); - ignore_error = FALSE; - } - } - - if (ignore_error) { - ui_log(op, "An error was encountered when loading the kernel " - "module, but that error was ignored, and the kernel module " - "will be installed, anyway. The error was: %s", data); - ret = TRUE; - } else { - ui_error(op, "Unable to load the kernel module '%s'. This " - "happens most frequently when this kernel module was " - "built against the wrong or improperly configured " - "kernel sources, with a version of gcc that differs " - "from the one used to build the target kernel, or " - "if a driver such as rivafb, nvidiafb, or nouveau is " - "present and prevents the NVIDIA kernel module from " - "obtaining ownership of the NVIDIA graphics device(s), " - "or no NVIDIA GPU installed in this system is supported " - "by this NVIDIA Linux graphics driver release.\n\n" - "Please see the log entries 'Kernel module load " - "error' and 'Kernel messages' at the end of the file " - "'%s' for more information.", - p->kernel_module_filename, op->log_file_name); - - /* - * if in expert mode, run_command() would have caused this to - * be written to the log file; so if not in expert mode, print - * the output now. - */ - - if (!op->expert) ui_log(op, "Kernel module load error: %s", data); - ret = FALSE; - } - + ret = ignore_load_error(op, p, p->kernel_module_filename, + data, ret); } else { /* @@ -1248,10 +1476,19 @@ int test_kernel_module(Options *op, Package *p) */ cmd = nvstrcat(op->utils[RMMOD], " ", p->kernel_module_name, NULL); run_command(op, cmd, NULL, FALSE, 0, TRUE); + + if (op->multiple_kernel_modules) { + nvfree(cmd); + cmd = nvstrcat(op->utils[RMMOD], " ", + p->kernel_frontend_module_name, NULL); + run_command(op, cmd, NULL, FALSE, 0, TRUE); + } + ret = TRUE; nvfree(cmd); } +test_exit: nvfree(data); /* @@ -1411,7 +1648,42 @@ int check_for_unloaded_kernel_module(Options *op, Package *p) } /* check_for_unloaded_kernel_module() */ +/* + * add_file_to_search_filelist() - Add file to build a list + * of files expected to be unpacked. + */ + +static void add_file_to_search_filelist(char **search_filelist, char *filename) +{ + int index = 0; + + while(search_filelist[index]) { + index++; + } + + if (index == SEARCH_FILELIST_MAX_ENTRIES) + return; + + search_filelist[index] = nvstrdup(filename); +} /* add_file_to_search_filelist() */ + + +/* + * free_search_filelist() - frees the list of files expected + * to unpack + */ + +static void free_search_filelist(char **search_filelist) +{ + int index = 0; + + while (search_filelist[index]) { + free(search_filelist[index]); + index++; + } + +} /*free_search_filelist() */ /* @@ -1427,7 +1699,9 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) { char *proc_version_string, *tmp; PrecompiledInfo *info = NULL; - + char *search_filelist[SEARCH_FILELIST_MAX_ENTRIES+1]; + int index; + /* allow the user to completely skip this search */ if (op->no_precompiled_interface) { @@ -1445,7 +1719,22 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (!mkdir_recursive(op, p->kernel_module_build_directory, 0755)) goto failed; - + + memset(search_filelist, 0, sizeof(search_filelist)); + + if (op->multiple_kernel_modules) { + add_file_to_search_filelist(search_filelist, + p->kernel_frontend_interface_filename); + } + + for (index = 0; index < op->num_kernel_modules; index++) { + char *tmp; + tmp = nvstrdup(p->kernel_interface_filename); + replace_zero(tmp, index); + add_file_to_search_filelist(search_filelist, tmp); + free(tmp); + } + /* * if the --precompiled-kernel-interfaces-path option was * specified, search that directory, first @@ -1453,7 +1742,7 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (op->precompiled_kernel_interfaces_path) { info = scan_dir(op, p, op->precompiled_kernel_interfaces_path, - proc_version_string); + proc_version_string, search_filelist); } /* @@ -1464,11 +1753,11 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (!info) { tmp = build_distro_precompiled_kernel_interface_dir(op); if (tmp) { - info = scan_dir(op, p, tmp, proc_version_string); + info = scan_dir(op, p, tmp, proc_version_string, search_filelist); nvfree(tmp); } } - + /* * if we still haven't found a match, search in * p->precompiled_kernel_interface_directory (the directory @@ -1478,7 +1767,7 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (!info) { info = scan_dir(op, p, p->precompiled_kernel_interface_directory, - proc_version_string); + proc_version_string, search_filelist); } /* @@ -1488,16 +1777,20 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (!info && !op->no_network && op->precompiled_kernel_interfaces_url) { info = download_updated_kernel_interface(op, p, - proc_version_string); + proc_version_string, + search_filelist); if (!info) { ui_message(op, "No matching precompiled kernel interface was " "found at '%s'; this means that the installer will need " "to compile a kernel interface for your kernel.", op->precompiled_kernel_interfaces_url); + free_search_filelist(search_filelist); return NULL; } } + free_search_filelist(search_filelist); + /* If we found one, ask expert users if they really want to use it */ if (info && op->expert) { @@ -1893,7 +2186,8 @@ static int rmmod_kernel_module(Options *op, const char *module_name) static PrecompiledInfo * download_updated_kernel_interface(Options *op, Package *p, - const char *proc_version_string) + const char *proc_version_string, + char *const *search_filelist) { int fd = -1; int dst_fd = -1; @@ -1999,7 +2293,7 @@ download_updated_kernel_interface(Options *op, Package *p, /* XXX once we have gpg setup, should check the file here */ info = get_precompiled_info(op, dstfile, proc_version_string, - p->version); + p->version, search_filelist); if (!info) { ui_error(op, "The format of the downloaded precompiled package " @@ -2207,7 +2501,8 @@ static int preempt_rt_check(Options *op, Package *p) static PrecompiledInfo *scan_dir(Options *op, Package *p, const char *directory_name, - const char *proc_version_string) + const char *proc_version_string, + char *const *search_filelist) { DIR *dir; struct dirent *ent; @@ -2232,7 +2527,7 @@ static PrecompiledInfo *scan_dir(Options *op, Package *p, filename = nvstrcat(directory_name, "/", ent->d_name, NULL); info = get_precompiled_info(op, filename, proc_version_string, - p->version); + p->version, search_filelist); free(filename); @@ -2313,62 +2608,6 @@ static char *convert_include_path_to_source_path(const char *inc) } /* convert_include_path_to_source_path() */ - -/* - * guess_kernel_module_filename() - parse uname to decide if the - * kernel module filename is "nvidia.o" or "nvidia.ko". - */ - -static char *guess_kernel_module_filename(Options *op) -{ - struct utsname uname_buf; - char *tmp, *str, *dot0, *dot1; - int major, minor; - - if (op->kernel_name) { - str = op->kernel_name; - } else { - if (uname(&uname_buf) == -1) { - ui_error (op, "Unable to determine kernel version (%s)", - strerror (errno)); - return NULL; - } - str = uname_buf.release; - } - - tmp = nvstrdup(str); - - dot0 = strchr(tmp, '.'); - if (!dot0) goto fail; - - *dot0 = '\0'; - - major = atoi(tmp); - - dot0++; - dot1 = strchr(dot0, '.'); - if (!dot1) goto fail; - - *dot1 = '\0'; - - minor = atoi(dot0); - - if ((major > 2) || ((major == 2) && (minor > 4))) { - return nvstrdup("nvidia.ko"); - } else { - return nvstrdup("nvidia.o"); - } - - fail: - ui_error (op, "Unable to determine if kernel is 2.6.0 or greater from " - "uname string '%s'; assuming the kernel module filename is " - "'nvidia.o'.", str); - return nvstrdup("nvidia.o"); - -} /* guess_kernel_module_filename() */ - - - /* * get_machine_arch() - get the machine architecture, substituting * i386 for i586 and i686 or arm for arm7l. @@ -46,9 +46,12 @@ PrecompiledInfo *find_precompiled_kernel_interface (Options*, Package*); char *get_kernel_name (Options*); KernelConfigOptionStatus test_kernel_config_option (Options*, Package*, const char*); -int sign_kernel_module (Options*, const char*, int); +int sign_kernel_module (Options*, const char*, + const char*, int); char *guess_module_signing_hash (Options*, Package*); +#define SEARCH_FILELIST_MAX_ENTRIES 32 + #ifndef ENOKEY #define ENOKEY 126 /* Required key not available */ #endif @@ -94,7 +94,9 @@ static const struct { { ENTRY(GLX_MODULE_SYMLINK, F, F, F, T, T, F, T ) }, { ENTRY(XMODULE_NEWSYM, F, F, F, T, T, F, F ) }, { ENTRY(VDPAU_LIB, T, F, T, T, F, T, F ) }, + { ENTRY(VDPAU_WRAPPER_LIB, T, F, T, T, F, T, F ) }, { ENTRY(VDPAU_SYMLINK, T, F, F, T, T, F, F ) }, + { ENTRY(VDPAU_WRAPPER_SYMLINK, T, F, F, T, T, F, F ) }, { ENTRY(NVCUVID_LIB, T, F, T, F, F, T, F ) }, { ENTRY(NVCUVID_LIB_SYMLINK, T, F, F, F, T, F, F ) }, { ENTRY(ENCODEAPI_LIB, T, F, T, F, F, T, F ) }, @@ -36,6 +36,9 @@ #include <dirent.h> #include <libgen.h> #include <pci/pci.h> +#include <dlfcn.h> +#include <elf.h> +#include <link.h> #ifndef PCI_CLASS_DISPLAY_3D #define PCI_CLASS_DISPLAY_3D 0x302 @@ -448,8 +451,8 @@ int read_text_file(const char *filename, char **buf) return FALSE; while (((line = fget_next_line(fp, &eof)) != NULL)) { - if ((index + strlen(line) + 1) > buflen) { - buflen += 2 * strlen(line); + if ((index + strlen(line) + 2) > buflen) { + buflen = 2 * (index + strlen(line) + 2); tmpbuf = (char *)nvalloc(buflen); if (!tmpbuf) { if (*buf) nvfree(*buf); @@ -505,7 +508,7 @@ int find_system_utils(Options *op) { "tr", "coreutils" }, { "sed", "sed" } }; - + /* keep in sync with the SystemOptionalUtils enum type */ const struct { const char *util, *package; } optional_utils[] = { { "chcon", "selinux" }, @@ -534,22 +537,35 @@ int find_system_utils(Options *op) needed_utils[i].package, needed_utils[i].util); return FALSE; } - + ui_expert(op, "found `%s` : `%s`", needed_utils[i].util, op->utils[i]); } - + for (j = 0, i = MAX_SYSTEM_UTILS; i < MAX_SYSTEM_OPTIONAL_UTILS; i++, j++) { op->utils[i] = find_system_util(optional_utils[j].util); if (op->utils[i]) { ui_expert(op, "found `%s` : `%s`", - optional_utils[j].util, op->utils[i]); - } else { - op->utils[i] = NULL; + optional_utils[j].util, op->utils[i]); } } - + + /* If no program called `X` is found; try searching for known X servers */ + if (op->utils[XSERVER] == NULL) { + static const char* xservers[] = { "Xorg", "XFree86" }; + int i; + + for (i = 0; i < ARRAY_LEN(xservers); i++) { + op->utils[XSERVER] = find_system_util(xservers[i]); + if (op->utils[XSERVER]) { + ui_expert(op, "found `%s` : `%s`", + xservers[i], op->utils[XSERVER]); + break; + } + } + } + return TRUE; } /* find_system_utils() */ @@ -1152,22 +1168,21 @@ void should_install_compat32_files(Options *op, Package *p) * files are to be installed anyway. */ install_compat32_files = ui_yes_no(op, TRUE, - "Install NVIDIA's 32-bit compatibility OpenGL " - "libraries?"); + "Install NVIDIA's 32-bit compatibility libraries?"); if (install_compat32_files && (op->compat32_chroot != NULL) && access(op->compat32_chroot, F_OK) < 0) { install_compat32_files = ui_yes_no(op, FALSE, - "The NVIDIA 32-bit compatibility OpenGL libraries are " + "The NVIDIA 32-bit compatibility libraries are " "to be installed relative to the top-level prefix (chroot) " "'%s'; however, this directory does not exist. Please " "consult your distribution's documentation to confirm the " "correct top-level installation prefix for 32-bit " "compatiblity libraries.\n\nDo you wish to install the " - "32-bit NVIDIA OpenGL compatibility libraries anyway?", + "NVIDIA 32-bit compatibility libraries anyway?", op->compat32_chroot); } - + if (!install_compat32_files) { for (i = 0; i < p->num_entries; i++) { if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { @@ -1181,6 +1196,79 @@ void should_install_compat32_files(Options *op, Package *p) /* + * detect_library() - attempt to dlopen(3) a DSO, to detect its availability. + */ +static int detect_library(const char *library) +{ + void *handle = dlopen(library, RTLD_NOW); + + if (handle) { + dlclose(handle); + return TRUE; + } + + return FALSE; +} + + +/* + * should_install_vdpau_wrapper() - ask the user if he/she wishes to + * install the VDPAU wrapper library. + */ + +void should_install_vdpau_wrapper(Options *op, Package *p) +{ + /* + * If the user did not specifically request installation or non-installation + * of the VDPAU wrapper, default to installing only if the wrapper was not + * detected. + */ + if (op->install_vdpau_wrapper == NV_OPTIONAL_BOOL_DEFAULT) { + if (detect_library("libvdpau.so.1")) { + op->install_vdpau_wrapper = NV_OPTIONAL_BOOL_FALSE; + } else { + op->install_vdpau_wrapper = NV_OPTIONAL_BOOL_TRUE; + } + } + + /* give expert users an opportunity to override the default behavior and/or + * change their minds about any explicit command line setting */ + if (op->expert) { + if (ui_yes_no(op, op->install_vdpau_wrapper, + "Install the libvdpau wrapper library?")) { + op->install_vdpau_wrapper = NV_OPTIONAL_BOOL_TRUE; + } else { + op->install_vdpau_wrapper = NV_OPTIONAL_BOOL_FALSE; + } + } + + if (op->install_vdpau_wrapper == NV_OPTIONAL_BOOL_TRUE) { + ui_message(op, "nvidia-installer will install the libvdpau and " + "libvdpau_trace libraries that were included with this " + "installer package. These libraries are available " + "separately through the libvdpau project and will be " + "removed from the NVIDIA Linux driver installer package " + "in the future, so it is recommended that VDPAU users " + "install libvdpau separately, e.g. by using packages " + "available from their distributions, or by building " + "from the sources available at:\n\n" + "http://people.freedesktop.org/~aplattner/vdpau"); + } else { + int i; + + ui_log(op, "Skipping installation of the libvdpau wrapper library."); + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].type == FILE_TYPE_VDPAU_WRAPPER_LIB || + p->entries[i].type == FILE_TYPE_VDPAU_WRAPPER_SYMLINK) { + invalidate_package_entry(&(p->entries[i])); + } + } + } +} + + +/* * check_installed_files_from_package() - scan through the entries in * the package, making sure that all symbolic links and files are * properly installed. @@ -1199,7 +1287,7 @@ void check_installed_files_from_package(Options *op, Package *p) for (i = 0; i < p->num_entries; i++) { percent = (float) i / (float) p->num_entries; - ui_status_update(op, percent, p->entries[i].dst); + ui_status_update(op, percent, "%s", p->entries[i].dst); if (p->entries[i].caps.is_symlink && /* Don't bother checking FILE_TYPE_NEWSYMs because we may not have @@ -2459,8 +2547,10 @@ done: static int prompt_for_user_cancel(Options *op, const char *file, int default_cancel, const char *text) { - int ret, file_read; - char *message = NULL; + int ret, file_read, msglen; + char *message = NULL, *prompt; + + const char *buttons[2] = {"Continue Installation", "Cancel Installation"}; file_read = read_text_file(file, &message); @@ -2468,12 +2558,21 @@ static int prompt_for_user_cancel(Options *op, const char *file, message = nvstrdup(""); } - ret = ui_yes_no(op, default_cancel, - "%s\n\n%s\nWould you like to cancel this installation?", - text, message); + msglen = strlen(message); + + prompt = nvstrcat(text, msglen > 0 ? "\n\nPlease review the message " + "provided by the maintainer of this alternate " + "installation method and decide how to proceed:" : NULL, + NULL); + + ret = ui_paged_prompt(op, prompt, msglen > 0 ? "Information about the " + "alternate information method" : "", message, + buttons, 2, default_cancel); + nvfree(message); + nvfree(prompt); - return ret; + return ret == 1; } #define INSTALL_PRESENT_FILE "alternate-install-present" @@ -2516,7 +2615,7 @@ int check_for_alternate_install(Options *op) "uninstall the existing installation before installing this " "driver."; - return !prompt_for_user_cancel(op, alt_inst_present, TRUE, msg); + return !prompt_for_user_cancel(op, alt_inst_present, 1, msg); } if (access(alt_inst_avail, F_OK) == 0) { @@ -2528,7 +2627,7 @@ int check_for_alternate_install(Options *op) "better with your system than a driver installed by " "nvidia-installer."; - return !prompt_for_user_cancel(op, alt_inst_avail, FALSE, msg); + return !prompt_for_user_cancel(op, alt_inst_avail, 0, msg); } return TRUE; @@ -2995,3 +3094,47 @@ int secure_boot_enabled(void) { return ret; } + + + +/* + * get_elf_architecture() - attempt to read an ELF header from the given file; + * returns ELF_ARCHITECTURE_{32,64,UNKNOWN} if the architecture could be parsed, + * ELF_INVALID_FILE on error, or if the file is not valid ELF. + */ + +ElfFileType get_elf_architecture(const char *filename) +{ + FILE *fp; + ElfW(Ehdr) header; + + fp = fopen(filename, "r"); + + /* Read the ELF header */ + + if (fp) { + int ret = fread(&header, sizeof(header), 1, fp); + fclose(fp); + + if (ret != 1) { + return ELF_INVALID_FILE; + } + } else { + return ELF_INVALID_FILE; + } + + /* Verify the magic number */ + + if (strncmp((char *) header.e_ident, "\177ELF", 4) != 0) { + return ELF_INVALID_FILE; + } + + /* Parse the architecture from the ELF header */ + + switch(header.e_ident[EI_CLASS]) { + case ELFCLASS32: return ELF_ARCHITECTURE_32; + case ELFCLASS64: return ELF_ARCHITECTURE_64; + case ELFCLASSNONE: return ELF_ARCHITECTURE_UNKNOWN; + default: return ELF_INVALID_FILE; + } +} @@ -30,6 +30,13 @@ #include "nvidia-installer.h" #include "command-list.h" +typedef enum { + ELF_INVALID_FILE, + ELF_ARCHITECTURE_UNKNOWN, + ELF_ARCHITECTURE_32, + ELF_ARCHITECTURE_64, +} ElfFileType; + char *read_next_word (char *buf, char **e); int check_euid(Options *op); @@ -50,6 +57,7 @@ int continue_after_error(Options *op, const char *fmt, ...) NV_ATTRIBUTE_PRINTF( int do_install(Options *op, Package *p, CommandList *c); void should_install_opengl_headers(Options *op, Package *p); void should_install_compat32_files(Options *op, Package *p); +void should_install_vdpau_wrapper(Options *op, Package *p); void check_installed_files_from_package(Options *op, Package *p); int tls_test(Options *op, int compat_32_libs); int check_runtime_configuration(Options *op, Package *p); @@ -70,5 +78,6 @@ int unprelink(Options *op, const char *filename); int verify_crc(Options *op, const char *filename, unsigned int crc, unsigned int *actual_crc); int secure_boot_enabled(void); +ElfFileType get_elf_architecture(const char *filename); #endif /* __NVIDIA_INSTALLER_MISC_H__ */ diff --git a/mkprecompiled.c b/mkprecompiled.c index 3a4d3ca..f6ac578 100644 --- a/mkprecompiled.c +++ b/mkprecompiled.c @@ -522,7 +522,7 @@ static Options *parse_commandline(int argc, char *argv[]) break; } - op->package = get_precompiled_info(op, op->package_file, NULL, NULL); + op->package = get_precompiled_info(op, op->package_file, NULL, NULL, NULL); if (!op->package && op->action != PACK) { fprintf(stderr, "Unable to read package file '%s'.\n", diff --git a/ncurses-ui.c b/ncurses-ui.c index 7775fff..b0d6e04 100644 --- a/ncurses-ui.c +++ b/ncurses-ui.c @@ -186,8 +186,8 @@ static void nv_ncurses_destroy_region(RegionStruct *); /* helper functions for drawing buttons */ -static void nv_ncurses_draw_button(DataStruct *, RegionStruct *, - int, int, int, int, char *, bool, bool); +static void nv_ncurses_draw_button(DataStruct *, RegionStruct *, int, int, + int, int, const char *, bool, bool); static void nv_ncurses_erase_button(RegionStruct *, int, int, int, int); @@ -206,6 +206,8 @@ static PagerStruct *nv_ncurses_create_pager(DataStruct *, int, int, int, int, static void nv_ncurses_pager_update(DataStruct *, PagerStruct *); static void nv_ncurses_pager_handle_events(DataStruct *, PagerStruct *, int); static void nv_ncurses_destroy_pager(PagerStruct *); +static int nv_ncurses_paged_prompt(Options *, const char*, const char*, + const char*, const char **, int, int); /* progress bar helper functions */ @@ -217,11 +219,9 @@ static void init_position(int p[4], int w); /* misc helper functions */ -static TextRows *nv_ncurses_create_command_list_textrows(DataStruct *, - CommandList *, int); +static char *nv_ncurses_create_command_list_text(DataStruct *, CommandList *); static char *nv_ncurses_mode_to_permission_string(mode_t); -static void nv_ncurses_concat_text_rows(TextRows *, TextRows *); static void nv_ncurses_free_text_rows(TextRows *); static int nv_ncurses_format_print(DataStruct *, RegionStruct *, @@ -247,6 +247,7 @@ InstallerUI ui_dispatch_table = { nv_ncurses_command_output, nv_ncurses_approve_command_list, nv_ncurses_yes_no, + nv_ncurses_paged_prompt, nv_ncurses_status_begin, nv_ncurses_status_update, nv_ncurses_status_end, @@ -621,140 +622,17 @@ static char *nv_ncurses_get_input(Options *op, static int nv_ncurses_display_license(Options *op, const char *license) { - DataStruct *d = (DataStruct *) op->ui_priv; - TextRows *t_pager = NULL; - int ch, x, cur = 0; - int button_width, button_y, accept_x, no_accept_x, accepted; - PagerStruct *p = NULL; - char *str; - static const char *descr = "Please read the following LICENSE " "and then select either \"Accept\" to accept the license " "and continue with the installation, or select " "\"Do Not Accept\" to abort the installation."; - nv_ncurses_check_resize(d, FALSE); - accepted = FALSE; - - draw_license: - - /* free the pager, and pager's text rows */ - - if (d->message) nv_ncurses_destroy_region(d->message); - if (p) nv_ncurses_destroy_pager(p); - if (t_pager) nv_ncurses_free_text_rows(t_pager); - - /* create the message region and print descr in it */ - - nv_ncurses_do_message_region(d, NULL, descr, TRUE, 2); - - /* draw the accept buttons */ - - button_width = 15; - button_y = d->message->h - 2; - accept_x = (d->message->w - (button_width * 3)) / 2; - no_accept_x = accept_x + (button_width * 2); - - nv_ncurses_draw_button(d, d->message, accept_x, button_y, button_width, - 1, "Accept", accepted, FALSE); - - nv_ncurses_draw_button(d, d->message, no_accept_x, button_y, button_width, - 1, "Do Not Accept", !accepted, FALSE); - - /* init the pager to display the license */ - - t_pager = d->format_text_rows(NULL, license, d->message->w, TRUE); - p = nv_ncurses_create_pager(d, 1, d->message->h + 2, d->message->w, - d->height - d->message->h - 4, - t_pager, "NVIDIA Software License", cur); - refresh(); - - /* process key strokes */ - - do { - /* if a resize occurred, jump back to the top and redraw */ - - if (nv_ncurses_check_resize(d, FALSE)) { - cur = p->cur; - goto draw_license; - } - ch = getch(); - - switch (ch) { - case NV_NCURSES_TAB: - case KEY_LEFT: - case KEY_RIGHT: - - /* - * any of left, right, and tab will toggle which button is - * selected - */ - - accepted ^= 1; - - nv_ncurses_draw_button(d, d->message, accept_x, button_y, - button_width, 1, "Accept", - accepted, FALSE); - - nv_ncurses_draw_button(d, d->message, no_accept_x, button_y, - button_width, 1, "Do Not Accept", - !accepted, FALSE); - refresh(); - break; - - case NV_NCURSES_CTRL('L'): - nv_ncurses_check_resize(d, TRUE); - cur = p->cur; - goto draw_license; - break; - - default: - break; - } - - nv_ncurses_pager_handle_events(d, p, ch); - - } while (ch != NV_NCURSES_ENTER); - - /* animate the button being pushed down */ - - x = accepted ? accept_x : no_accept_x; - str = accepted ? "Accept" : "Do Not Accept"; - - nv_ncurses_erase_button(d->message, x, button_y, button_width, 1); - nv_ncurses_draw_button(d, d->message, x, button_y, button_width, 1, - str, TRUE, TRUE); - refresh(); - usleep(NV_NCURSES_BUTTON_PRESS_TIME); - - nv_ncurses_erase_button(d->message, x, button_y, button_width, 1); - nv_ncurses_draw_button(d, d->message, x, button_y, button_width, 1, - str, TRUE, FALSE); - refresh(); - usleep(NV_NCURSES_BUTTON_PRESS_TIME); - - /* free the text rows used by the pager */ - - nv_ncurses_free_text_rows(t_pager); - - /* free the pager */ - - nv_ncurses_destroy_pager(p); - - /* restore the footer */ + static const char *buttons[2] = {"Accept", "Do Not Accept"}; - nv_ncurses_set_footer(d, NV_NCURSES_DEFAULT_FOOTER_LEFT, - NV_NCURSES_DEFAULT_FOOTER_RIGHT); - - /* free the message region */ - - nv_ncurses_destroy_region(d->message); - d->message = NULL; - - refresh(); + int ret = nv_ncurses_paged_prompt(op, descr, "NVIDIA Software License", + license, buttons, 2, 1); - return accepted; - + return ret == 0; } /* nv_ncurses_display_license() */ @@ -892,11 +770,9 @@ static int nv_ncurses_approve_command_list(Options *op, CommandList *cl, const char *descr) { DataStruct *d = (DataStruct *) op->ui_priv; - TextRows *t_pager = NULL; - PagerStruct *p = NULL; - char *str, *question; - int button_w, button_y, yes_x, no_x; - int len, ch, x, yes, cur = 0; + char *commandlist, *question; + int ret, len; + const char *buttons[2] = {"Yes", "No"}; /* initialize the question string */ @@ -905,131 +781,15 @@ static int nv_ncurses_approve_command_list(Options *op, CommandList *cl, snprintf(question, len, "The following operations will be performed to " "install the %s. Is this acceptable?", descr); - /* check if the window was resized */ + commandlist = nv_ncurses_create_command_list_text(d, cl); - nv_ncurses_check_resize(d, FALSE); - yes = 1; - - draw_command_list: - - /* free any existing message region, pager, and pager textrows */ - - if (d->message) { - nv_ncurses_destroy_region(d->message); - d->message = NULL; - } - if (p) nv_ncurses_destroy_pager(p); - if (t_pager) nv_ncurses_free_text_rows(t_pager); - - /* create the message region and print descr in it */ - - nv_ncurses_do_message_region(d, NULL, question, TRUE, 2); - - /* draw the yes/no buttons */ - - button_w = 7; - button_y = d->message->h - 2; - yes_x = (d->message->w - button_w*3)/2 + 0; - no_x = (d->message->w - button_w*3)/2 + 2*button_w; - - nv_ncurses_draw_button(d, d->message, yes_x, button_y, - button_w, 1, "Yes", yes, FALSE); - nv_ncurses_draw_button(d, d->message, no_x, button_y, - button_w, 1, "No", !yes, FALSE); - - /* draw the command list */ - - t_pager = nv_ncurses_create_command_list_textrows(d, cl, d->message->w); - p = nv_ncurses_create_pager(d, 1, d->message->h + 2, d->message->w, - d->height - d->message->h - 4, - t_pager, "Proposed Commandlist", cur); - refresh(); - - /* process key strokes */ - - do { - /* if a resize occurred, jump back to the top and redraw */ - - if (nv_ncurses_check_resize(d, FALSE)) { - cur = p->cur; - goto draw_command_list; - } - ch = getch(); - - switch (ch) { - case NV_NCURSES_TAB: - case KEY_LEFT: - case KEY_RIGHT: - - /* - * any of left, right, and tab will toggle which button is - * selected - */ - - yes ^= 1; - - nv_ncurses_draw_button(d, d->message, yes_x, button_y, - button_w, 1, "Yes", yes, FALSE); - nv_ncurses_draw_button(d, d->message, no_x, button_y, - button_w, 1, "No", !yes, FALSE); - refresh(); - break; - - case NV_NCURSES_CTRL('L'): - nv_ncurses_check_resize(d, TRUE); - cur = p->cur; - goto draw_command_list; - break; - - default: - break; - } - - nv_ncurses_pager_handle_events(d, p, ch); - - } while (ch != NV_NCURSES_ENTER); - - /* animate the button being pushed down */ - - x = yes ? yes_x : no_x; - str = yes ? "Yes" : "No"; - - nv_ncurses_erase_button(d->message, x, button_y, button_w, 1); - nv_ncurses_draw_button(d, d->message, x, button_y, - button_w, 1, str, TRUE, TRUE); - refresh(); - usleep(NV_NCURSES_BUTTON_PRESS_TIME); - - nv_ncurses_erase_button(d->message, x, button_y, button_w, 1); - nv_ncurses_draw_button(d, d->message, x, button_y, - button_w, 1, str, TRUE, FALSE); - refresh(); - usleep(NV_NCURSES_BUTTON_PRESS_TIME); - - /* free the text rows used by the pager */ - - nv_ncurses_free_text_rows(t_pager); - - /* free the pager */ - - nv_ncurses_destroy_pager(p); - - /* restore the footer */ - - nv_ncurses_set_footer(d, NV_NCURSES_DEFAULT_FOOTER_LEFT, - NV_NCURSES_DEFAULT_FOOTER_RIGHT); - - /* free the message region */ - - nv_ncurses_destroy_region(d->message); - d->message = NULL; - - refresh(); + ret = nv_ncurses_paged_prompt(op, question, "Proposed CommandList", + commandlist, buttons, 2, 0); free(question); + free(commandlist); - return yes; - + return ret == 0; } /* nv_ncurses_approve_command_list() */ @@ -1552,7 +1312,7 @@ static void nv_ncurses_destroy_region(RegionStruct *region) static void nv_ncurses_draw_button(DataStruct *d, RegionStruct *region, int x, int y, int w, int h, - char *str, bool hilite, bool down) + const char *str, bool hilite, bool down) { int i, j, n, attr = 0; @@ -1906,6 +1666,161 @@ static void nv_ncurses_pager_handle_events(DataStruct *d, } } /* nv_ncurses_pager_handle_events() */ +static void draw_buttons(DataStruct *d, const char **buttons, int num_buttons, + int button, int button_w, const int *buttons_x, + int button_y) +{ + int i; + + for (i = 0; i < num_buttons; i++) { + nv_ncurses_draw_button(d, d->message, buttons_x[i], button_y, + button_w, 1, buttons[i], button == i, FALSE); + } +} + +/* + * nv_ncurses_paged_prompt() - display a question with multiple-choice buttons + * along with a scrollable paged text area, allowing the user to review the + * pageable text and select a button corresponding to the desired response. + * Returns the index of the button selected from the passed-in buttons array. + */ + +static int nv_ncurses_paged_prompt(Options *op, const char *question, + const char *pager_title, + const char *pager_text, const char **buttons, + int num_buttons, int default_button) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + TextRows *t_pager = NULL; + int ch, cur = 0; + int i, button_w = 0, button_y, button = default_button; + int buttons_x[num_buttons]; + PagerStruct *p = NULL; + + /* check if the window was resized */ + + nv_ncurses_check_resize(d, FALSE); + + for (i = 0; i < num_buttons; i++) { + int len = strlen(buttons[i]); + if (len > button_w) { + button_w = len; + } + } + button_w += 4; + + print_message: + + /* free any existing message region, pager, and pager textrows */ + + if (d->message) { + nv_ncurses_destroy_region(d->message); + d->message = NULL; + } + + if (p) { + nv_ncurses_destroy_pager(p); + p = NULL; + } + if (t_pager) { + nv_ncurses_free_text_rows(t_pager); + t_pager = NULL; + } + + /* create the message region and print the question in it */ + + nv_ncurses_do_message_region(d, NULL, question, TRUE, 2); + + button_y = d->message->h - 2; + + for (i = 0; i < num_buttons; i++) { + buttons_x[i] = (i + 1) * (d->message->w / (num_buttons + 1)) - + button_w / 2; + } + + draw_buttons(d, buttons, num_buttons, button, button_w, buttons_x, + button_y); + + /* draw the paged text */ + + t_pager = d->format_text_rows(NULL, pager_text, d->message->w, TRUE); + p = nv_ncurses_create_pager(d, 1, d->message->h + 2, d->message->w, + d->height - d->message->h - 4, t_pager, + pager_title, cur); + refresh(); + + /* process key strokes */ + + do { + /* if a resize occurred, jump back to the top and redraw */ + if (nv_ncurses_check_resize(d, FALSE)) { + cur = p->cur; + goto print_message; + } + + ch = getch(); + + switch (ch) { + case NV_NCURSES_TAB: + case KEY_RIGHT: + button = (button + 1) % num_buttons; + draw_buttons(d, buttons, num_buttons, button, button_w, + buttons_x, button_y); + refresh(); + break; + + case KEY_LEFT: + button = (button + num_buttons - 1) % num_buttons; + draw_buttons(d, buttons, num_buttons, button, button_w, + buttons_x, button_y); + refresh(); + break; + + case NV_NCURSES_CTRL('L'): + nv_ncurses_check_resize(d, TRUE); + cur = p->cur; + goto print_message; + break; + + default: + break; + } + + nv_ncurses_pager_handle_events(d, p, ch); + } while (ch != NV_NCURSES_ENTER); + + /* animate the button being pushed down */ + + nv_ncurses_erase_button(d->message, buttons_x[button], button_y, + button_w, 1); + nv_ncurses_draw_button(d, d->message, buttons_x[button], button_y, + button_w, 1, buttons[button], TRUE, TRUE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + nv_ncurses_erase_button(d->message, buttons_x[button], button_y, + button_w, 1); + nv_ncurses_draw_button(d, d->message, buttons_x[button], button_y, + button_w, 1, buttons[button], TRUE, FALSE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + /* restore the footer */ + + nv_ncurses_set_footer(d, NV_NCURSES_DEFAULT_FOOTER_LEFT, + NV_NCURSES_DEFAULT_FOOTER_RIGHT); + + /* clean up */ + + nv_ncurses_free_text_rows(t_pager); + nv_ncurses_destroy_pager(p); + nv_ncurses_destroy_region(d->message); + d->message = NULL; + + refresh(); + + return button; +} @@ -1962,20 +1877,14 @@ static void init_position(int p[4], int w) /* - * nv_ncurses_create_command_list_textrows() - build TextRows to - * describe the command list + * nv_ncurses_create_command_list_text() - build a string from the command list */ -static TextRows *nv_ncurses_create_command_list_textrows(DataStruct *d, - CommandList *cl, - int w) +static char *nv_ncurses_create_command_list_text(DataStruct *d, CommandList *cl) { int i, len; Command *c; - char *str, *perms; - TextRows *t0, *t1; - - t0 = t1 = NULL; + char *str, *perms, *ret = strdup(""); for (i = 0; i < cl->num; i++) { c = &cl->cmds[i]; @@ -2032,24 +1941,30 @@ static TextRows *nv_ncurses_create_command_list_textrows(DataStruct *d, } if (str) { - t1 = d->format_text_rows(NV_BULLET_STR, str, w, TRUE); - - if (t0) { - nv_ncurses_concat_text_rows(t0, t1); - nv_ncurses_free_text_rows(t1); - } else { - t0 = t1; - } - t1 = NULL; - + int lenret, lenstr; + char *tmp; + + lenret = strlen(ret); + lenstr = strlen(str); + tmp = malloc(lenret + lenstr + 2 /* "\n\0" */); + + tmp[0] = 0; + + strncat(tmp, ret, lenret); + strncat(tmp, str, lenstr); + + tmp[lenret + lenstr] = '\n'; + tmp[lenret + lenstr + 1] = 0; + free(str); + free(ret); + ret = tmp; } } - return t0; - -} /* nv_ncurses_create_command_list_textrows() */ + return ret; +} /* @@ -2080,27 +1995,6 @@ static char *nv_ncurses_mode_to_permission_string(mode_t mode) } /* mode_to_permission_string() */ -/* - * nv_ncurses_concat_text_rows() - concatenate two text rows - */ - -static void nv_ncurses_concat_text_rows(TextRows *t0, TextRows *t1) -{ - int n, i; - - n = t0->n + t1->n; - - t0->t = (char **) realloc(t0->t, sizeof(char *) * n); - - for (i = 0; i < t1->n; i++) { - t0->t[i + t0->n] = strdup(t1->t[i]); - } - - t0->m = NV_MAX(t0->m, t1->m); - t0->n = n; - -} /* nv_ncurses_concat_text_rows() */ - /* diff --git a/nvidia-installer-ui.h b/nvidia-installer-ui.h index 1418a9c..e8f0c81 100644 --- a/nvidia-installer-ui.h +++ b/nvidia-installer-ui.h @@ -128,6 +128,20 @@ typedef struct __nv_installer_ui { int (*yes_no)(Options *op, const int def, const char *msg); /* + * paged_prompt - ask the question 'question' with 'num_answers' possible + * multiple choice answers in 'answers', with 'default_answer' as the + * default answer, and with scrollable 'pager_text' given as supplementary + * information, described by 'pager_title'. Returns the index into 'answers' + * of the user-selected answer to 'question'. The names of answers must + * not begin with digits, and the caller is responsible for enforcing this. + */ + + int (*paged_prompt)(Options *op, const char *question, + const char *pager_title, const char *pager_text, + const char **answers, int num_answers, + int default_answer); + + /* * status_begin(), status_update(), status_end() - these three * functions display the status of some process. It is expected * that status_begin() would be called, followed by some number of diff --git a/nvidia-installer.c b/nvidia-installer.c index cc3c752..3d8cb59 100644 --- a/nvidia-installer.c +++ b/nvidia-installer.c @@ -61,7 +61,7 @@ extern const char *pNV_ID; static void print_version(void) { fmtout(""); - fmtout(pNV_ID); + fmtout("%s", pNV_ID); fmtoutp(TAB, "The NVIDIA Software Installer for Unix/Linux."); fmtout(""); fmtoutp(TAB, "This program is used to install, upgrade and uninstall " @@ -79,8 +79,8 @@ static void print_version(void) static void print_help_helper(const char *name, const char *description) { - fmtoutp(TAB, name); - fmtoutp(BIGTAB, description); + fmtoutp(TAB, "%s", name); + fmtoutp(BIGTAB, "%s", description); fmtout(""); } @@ -146,7 +146,9 @@ static Options *load_default_options(void) op->run_distro_scripts = TRUE; op->no_kernel_module_source = FALSE; op->dkms = FALSE; + op->install_vdpau_wrapper = NV_OPTIONAL_BOOL_DEFAULT; op->check_for_alternate_installs = TRUE; + op->num_kernel_modules = 1; return op; @@ -170,6 +172,8 @@ static void parse_commandline(int argc, char *argv[], Options *op) int print_help_args_only_after = FALSE; int print_advanced_help = FALSE; char *strval = NULL, *program_name = NULL; + int boolval; + int intval = 0; /* * if the installer was invoked as "nvidia-uninstall", perform an @@ -183,9 +187,7 @@ static void parse_commandline(int argc, char *argv[], Options *op) while (1) { - c = nvgetopt(argc, argv, __options, &strval, - NULL, /* boolval */ - NULL, /* intval */ + c = nvgetopt(argc, argv, __options, &strval, &boolval, &intval, NULL, /* doubleval */ NULL); /* disable_val */ @@ -406,6 +408,24 @@ static void parse_commandline(int argc, char *argv[], Options *op) case MODULE_SIGNING_X509_HASH_OPTION: op->module_signing_x509_hash = strval; break; + case INSTALL_VDPAU_WRAPPER_OPTION: + op->install_vdpau_wrapper = boolval ? NV_OPTIONAL_BOOL_TRUE : + NV_OPTIONAL_BOOL_FALSE; + break; + case MULTIPLE_KERNEL_MODULES_OPTION: + if (intval < 0) { + fmterr("Invalid parameter for '--multiple-kernel-modules'"); + goto fail; + } + op->multiple_kernel_modules = TRUE; + if (intval > NV_MAX_MODULE_INSTANCES) + op->num_kernel_modules = NV_MAX_MODULE_INSTANCES; + else + op->num_kernel_modules = intval; + break; + case NO_CHECK_FOR_ALTERNATE_INSTALLS_OPTION: + op->check_for_alternate_installs = FALSE; + break; default: goto fail; } diff --git a/nvidia-installer.h b/nvidia-installer.h index 7f9effe..3389e10 100644 --- a/nvidia-installer.h +++ b/nvidia-installer.h @@ -146,6 +146,10 @@ typedef struct __options { int no_kernel_module_source; int dkms; int check_for_alternate_installs; + int multiple_kernel_modules; + int num_kernel_modules; + + NVOptionalBool install_vdpau_wrapper; char *opengl_prefix; char *opengl_libdir; @@ -247,7 +251,9 @@ typedef enum { FILE_TYPE_CUDA_LIB, FILE_TYPE_CUDA_SYMLINK, FILE_TYPE_VDPAU_LIB, + FILE_TYPE_VDPAU_WRAPPER_LIB, FILE_TYPE_VDPAU_SYMLINK, + FILE_TYPE_VDPAU_WRAPPER_SYMLINK, FILE_TYPE_UTILITY_BIN_SYMLINK, FILE_TYPE_CUDA_ICD, FILE_TYPE_NVCUVID_LIB, @@ -358,6 +364,9 @@ typedef struct __package { char **bad_module_filenames; char *kernel_module_build_directory; char *precompiled_kernel_interface_directory; + char *kernel_frontend_module_filename; + char *kernel_frontend_module_name; + char *kernel_frontend_interface_filename; PackageEntry *entries; /* array of filename/checksum/bytesize entries */ int num_entries; @@ -458,6 +467,8 @@ typedef struct __package { #define NUM_TIMES_QUESTIONS_ASKED 3 +#define NV_MAX_MODULE_INSTANCES 8 + #define LD_OPTIONS "-d -r" #define NVIDIA_VERSION_PROC_FILE "/proc/driver/nvidia/version" @@ -495,6 +506,7 @@ void add_package_entry(Package *p, char *dst, PackageEntryFileType type, PackageEntryFileTlsClass tls_class, + PackageEntryFileCompatArch compat_arch, mode_t mode); /* XXX */ diff --git a/option_table.h b/option_table.h index 897a054..4afb9bb 100644 --- a/option_table.h +++ b/option_table.h @@ -92,7 +92,9 @@ enum { MODULE_SIGNING_KEY_PATH_OPTION, MODULE_SIGNING_HASH_OPTION, MODULE_SIGNING_X509_HASH_OPTION, + INSTALL_VDPAU_WRAPPER_OPTION, NO_CHECK_FOR_ALTERNATE_INSTALLS_OPTION, + MULTIPLE_KERNEL_MODULES_OPTION, }; static const NVGetoptOption __options[] = { @@ -208,15 +210,15 @@ static const NVGetoptOption __options[] = { { "compat32-chroot", COMPAT32_CHROOT_OPTION, NVGETOPT_STRING_ARGUMENT, NULL, "The top-level prefix (chroot) relative to which the 32bit " - "compatibility OpenGL libraries will be installed on Linux/x86-64 " - "systems; this option is unset by default, the 32bit OpenGL " + "compatibility libraries will be installed on Linux/x86-64 " + "systems; this option is unset by default, the 32bit " "library installation prefix (see below) and the 32bit library " "path alone determine the target location. Only under very rare " "circumstances should this option be used." }, { "compat32-prefix", COMPAT32_PREFIX_OPTION, NVGETOPT_STRING_ARGUMENT, NULL, - "The prefix under which the 32bit compatibility OpenGL components " + "The prefix under which the 32bit compatibility components " "of the NVIDIA driver will be installed; the default is: '" DEFAULT_OPENGL_PREFIX "'. Only under rare circumstances should " "this option be used." }, @@ -224,7 +226,7 @@ static const NVGetoptOption __options[] = { { "compat32-libdir", COMPAT32_LIBDIR_OPTION, NVGETOPT_STRING_ARGUMENT, NULL, "The path relative to the 32bit compatibility prefix under which the " - "32bit compatibility OpenGL components of the NVIDIA driver will " + "32bit compatibility components of the NVIDIA driver will " "be installed. The default is '" DEFAULT_LIBDIR "' or '" UBUNTU_DEFAULT_COMPAT32_LIBDIR "', depending on the installed Linux " "distribution. Only under very rare circumstances should this " @@ -605,6 +607,16 @@ static const NVGetoptOption __options[] = { "name must be one of the message digest algorithms recognized by " "the x509(1) command." }, + { "install-vdpau-wrapper", INSTALL_VDPAU_WRAPPER_OPTION, + NVGETOPT_IS_BOOLEAN, NULL, + "The NVIDIA driver package includes a VDPAU wrapper library for " + "convenience. By default, the wrapper library provided with the " + "driver package will not be installed if an existing wrapper " + "library is detected. Setting the '--install-vdpau-wrapper' option " + "will force the wrapper library to be installed; setting the " + "'--no-install-vdpau-wrapper' option will force the wrapper library to " + "be excluded from the installation." }, + { "no-check-for-alternate-installs", NO_CHECK_FOR_ALTERNATE_INSTALLS_OPTION, 0, NULL, "Maintainers of alternate driver installation methods can report the " @@ -612,6 +624,11 @@ static const NVGetoptOption __options[] = { "nvidia-installer. Setting this option skips the check for alternate " "driver installations." }, + { "multiple-kernel-modules", MULTIPLE_KERNEL_MODULES_OPTION, + NVGETOPT_INTEGER_ARGUMENT, NULL, + "Build and install multiple NVIDIA kernel modules. The maximum number " + "of NVIDIA kernel modules that may be built is 8." }, + /* Orphaned options: These options were in the long_options table in * nvidia-installer.c but not in the help. */ { "debug", 'd', 0, NULL,NULL }, diff --git a/precompiled.c b/precompiled.c index a5f8bfc..8d9e84e 100644 --- a/precompiled.c +++ b/precompiled.c @@ -148,7 +148,8 @@ char *read_proc_version(Options *op, const char *proc_mount_point) PrecompiledInfo *get_precompiled_info(Options *op, const char *filename, const char *real_proc_version_string, - const char *package_version) + const char *package_version, + char *const *search_filelist) { int fd, offset, num_files, i; char *buf; @@ -160,7 +161,7 @@ PrecompiledInfo *get_precompiled_info(Options *op, fd = size = 0; buf = description = proc_version_string = NULL; - + /* open the file to be unpacked */ if ((fd = open(filename, O_RDONLY)) == -1) { @@ -288,6 +289,35 @@ PrecompiledInfo *get_precompiled_info(Options *op, } } + /* + * Check for the package validity. + * + * The package is valid if all of the files specified in + * search_filelist are present in the package. + */ + + if (search_filelist != NULL) { + int index; + + for (index = 0; search_filelist[index]; index++) { + int found = FALSE; + + for (i = 0; i < num_files; i++) { + if (!strcmp(search_filelist[index], fileInfos[i].name)) { + found = TRUE; + break; + } + } + + if (!found) { + ui_log(op, "Required file '%s' not found in package '%s'", + search_filelist[index], filename); + goto done; + } + } + } + + /* * now that we can no longer fail, allocate and initialize the * PrecompiledInfo structure @@ -309,7 +339,7 @@ PrecompiledInfo *get_precompiled_info(Options *op, proc_version_string = description = NULL; fileInfos = NULL; - done: +done: /* cleanup whatever needs cleaning up */ diff --git a/precompiled.h b/precompiled.h index 7d727a6..ca33e20 100644 --- a/precompiled.h +++ b/precompiled.h @@ -163,7 +163,8 @@ char *read_proc_version(Options *op, const char *proc_mount_point); PrecompiledInfo *get_precompiled_info(Options *op, const char *filename, const char *real_proc_version_string, - const char *package_version); + const char *package_version, + char *const *search_filelist); PrecompiledFileInfo *precompiled_find_file(const PrecompiledInfo *info, const char *file); diff --git a/stream-ui.c b/stream-ui.c index be0326c..0b33199 100644 --- a/stream-ui.c +++ b/stream-ui.c @@ -45,6 +45,8 @@ void stream_message (Options*, int level, const char*); void stream_command_output (Options*, const char*); int stream_approve_command_list(Options*, CommandList*, const char*); int stream_yes_no (Options*, const int, const char*); +int stream_paged_prompt (Options *op, const char *, const char *, + const char *, const char **, int, int); void stream_status_begin (Options*, const char*, const char*); void stream_status_update (Options*, const float, const char*); void stream_status_end (Options*, const char*); @@ -60,6 +62,7 @@ InstallerUI stream_ui_dispatch_table = { stream_command_output, stream_approve_command_list, stream_yes_no, + stream_paged_prompt, stream_status_begin, stream_status_update, stream_status_end, @@ -183,7 +186,7 @@ char *stream_get_input(Options *op, const char *def, const char *msg) char *buf; fmt(stdout, NULL, ""); - fmt(stdout, NULL, msg); + fmt(stdout, NULL, "%s", msg); fprintf(stdout, " [default: '%s']: ", def); fflush(stdout); @@ -272,7 +275,7 @@ void stream_message(Options *op, const int level, const char *msg) if ((level == NV_MSG_LEVEL_LOG) && (d->status_active)) return; if (msg_attrs[level].newline) fmt(msg_attrs[level].stream, NULL, ""); - fmt(msg_attrs[level].stream, msg_attrs[level].prefix, msg); + fmt(msg_attrs[level].stream, msg_attrs[level].prefix, "%s", msg); if (msg_attrs[level].newline) fmt(msg_attrs[level].stream, NULL, ""); } /* stream_message() */ @@ -292,7 +295,7 @@ void stream_command_output(Options *op, const char *msg) if ((!op->expert) || (d->status_active)) return; - fmtoutp(" ", msg); + fmtoutp(" ", "%s", msg); } /* stream_command_output() */ @@ -384,7 +387,7 @@ int stream_yes_no(Options *op, const int def, const char *msg) int eof, ret = def; fmt(stdout, NULL, ""); - fmt(stdout, NULL, msg); + fmt(stdout, NULL, "%s", msg); if (def) fprintf(stdout, " [default: (Y)es]: "); else fprintf(stdout, " [default: (N)o]: "); fflush(stdout); @@ -405,6 +408,96 @@ int stream_yes_no(Options *op, const int def, const char *msg) } /* stream_yes_no() */ +/* + * select_option - given a list of options and a user-provided selection, check + * to see if the selection matches any of the available options. Options can be + * selected either by index or by option name. Returns the caller-provided + * default option if the user's selection is an empty string, the index of the + * first (case-insensitive) matching option, or a negative number if no option + * matched the user's input. + */ + +static int select_option(const char **options, int num_options, + int default_option, const char *selection) +{ + int i; + + if (strlen(selection) == 0) { + return default_option; + } + + if (isdigit(selection[0])) { + int index = atoi(selection); + + if (index < 1 || index > num_options) { + return -2; + } + + return index - 1; + } + + for (i = 0; i < num_options; i++) { + if (strcasecmp(selection, options[i]) == 0) { + return i; + } + } + + return -1; +} + + + +/* + * stream_paged_prompt() - display a question with multiple-choice answers + * along with a text area (not scrollable), allowing the user to review the text + * and select a button corresponding to the desired response. Returns the index + * answer selected from the passed-in answers array. + */ + +int stream_paged_prompt(Options *op, const char *question, + const char *pager_title, const char *pager_text, + const char **answers, int num_answers, + int default_answer) +{ + int ret = default_answer; + + fmtout(""); + fmtout("________"); + fmtout(""); + fmtout("%s", pager_title); + fmtout(""); + fmtout("%s", pager_text); + fmtout(""); + fmtout("________"); + + do { + char *str; + int i; + + if (ret < 0) { + fmterr("Invalid response!"); + } + + fmtout(""); + fmtout("%s", question); + fmtout("Valid responses are: "); + + for (i = 0; i < num_answers; i++) { + fmtout(" (%d)\t\"%s\"%s", i + 1, answers[i], + i == default_answer ? " [ default ]" : ""); + } + + fmtout("Please select your response by number or name:"); + str = fget_next_line(stdin, NULL); + + ret = select_option(answers, num_answers, default_answer, str); + free(str); + } while (ret < 0); + + return ret; +} + + /* * stream_status_begin() - we assume that title is always passed, but @@ -417,7 +510,7 @@ void stream_status_begin(Options *op, const char *title, const char *msg) d->status_active = TRUE; - fmtout(title); + fmtout("%s", title); d->status_label = nvstrdup(msg); print_status_bar(d, STATUS_BEGIN, 0.0); diff --git a/user-interface.c b/user-interface.c index ea21045..6b655a0 100644 --- a/user-interface.c +++ b/user-interface.c @@ -214,7 +214,7 @@ char *ui_get_input(Options *op, const char *def, const char *fmt, ...) ret = __ui->get_input(op, def, msg); tmp = nvstrcat(msg, " (Answer: '", ret, "')", NULL); } - log_printf(op, NV_BULLET_STR, tmp); + log_printf(op, NV_BULLET_STR, "%s", tmp); nvfree(msg); nvfree(tmp); @@ -249,7 +249,7 @@ void ui_error(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); __ui->message(op, NV_MSG_LEVEL_ERROR, msg); - log_printf(op, "ERROR: ", msg); + log_printf(op, "ERROR: ", "%s", msg); free(msg); @@ -264,7 +264,7 @@ void ui_warn(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); __ui->message(op, NV_MSG_LEVEL_WARNING, msg); - log_printf(op, "WARNING: ", msg); + log_printf(op, "WARNING: ", "%s", msg); free(msg); @@ -280,7 +280,7 @@ void ui_message(Options *op, const char *fmt, ...) if (!op->silent) __ui->message(op, NV_MSG_LEVEL_MESSAGE, msg); - log_printf(op, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, "%s", msg); free(msg); @@ -294,7 +294,7 @@ void ui_log(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); - log_printf(op, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, "%s", msg); free(msg); @@ -315,7 +315,7 @@ void ui_expert(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); - log_printf(op, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, "%s", msg); free (msg); @@ -385,7 +385,7 @@ int ui_yes_no (Options *op, const int def, const char *fmt, ...) tmp = nvstrcat(msg, " (Answer: ", (ret ? "Yes" : "No"), ")", NULL); } - log_printf(op, NV_BULLET_STR, tmp); + log_printf(op, NV_BULLET_STR, "%s", tmp); nvfree(msg); nvfree(tmp); @@ -394,12 +394,38 @@ int ui_yes_no (Options *op, const int def, const char *fmt, ...) } /* ui_yes_no() */ +int ui_paged_prompt (Options *op, const char *question, const char *pager_title, + const char *pager_text, const char **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("(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, title); + log_printf(op, NV_BULLET_STR, "%s", title); if (op->silent) return; @@ -432,7 +458,7 @@ void ui_status_end(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->status_end(op, msg); - log_printf(op, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, "%s", msg); free(msg); } diff --git a/user-interface.h b/user-interface.h index bcf5dbb..ac216c3 100644 --- a/user-interface.h +++ b/user-interface.h @@ -38,6 +38,8 @@ void ui_expert (Options*, const char*, ...) NV_ATTRIB void ui_command_output (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); int ui_approve_command_list(Options*, CommandList*,const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); int ui_yes_no (Options*, const int, const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); +int ui_paged_prompt (Options *, const char *, const char *, + const char *, const char **, int, int); void ui_status_begin (Options*, const char*, const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); void ui_status_update (Options*, const float, const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); void ui_status_end (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); @@ -31,7 +31,7 @@ LD ?= ld # only set these warnings and optimizations if CFLAGS is unset CFLAGS ?= -Wall -O2 # always set these -f CFLAGS -CFLAGS += -fno-strict-aliasing -fno-omit-frame-pointer +CFLAGS += -fno-strict-aliasing -fno-omit-frame-pointer -Wformat=2 CC_ONLY_CFLAGS ?= LDFLAGS ?= BIN_LDFLAGS ?= @@ -1 +1 @@ -NVIDIA_VERSION = 325.15 +NVIDIA_VERSION = 331.13 |