From b581a5923ce164af5c8cd5634f4cba4fd098f711 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Wed, 13 Feb 2008 10:20:36 -0800 Subject: 1.0-6629 --- Makefile | 36 ++++- backup.c | 10 +- command-list.c | 208 ++++++++++++++++----------- command-list.h | 4 +- crc.c | 1 + files.c | 86 +++++++++-- install-from-cwd.c | 132 ++++++++++------- kernel.c | 114 ++++++++++++--- kernel.h | 1 + log.c | 45 +++++- misc.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++--- misc.h | 4 + nvidia-installer.c | 66 +++++++-- nvidia-installer.h | 70 +++++++-- rtld_test.c | 4 + rtld_test_Linux-x86 | Bin 0 -> 3056 bytes rtld_test_Linux-x86_64 | Bin 0 -> 4688 bytes sanity.c | 24 +++- user-interface.c | 6 +- 19 files changed, 964 insertions(+), 228 deletions(-) create mode 100644 rtld_test.c create mode 100755 rtld_test_Linux-x86 create mode 100755 rtld_test_Linux-x86_64 diff --git a/Makefile b/Makefile index 655d171..7e8224a 100644 --- a/Makefile +++ b/Makefile @@ -102,8 +102,14 @@ TLS_TEST_DSO_SO = tls_test_dso_$(INSTALLER_OS)-$(INSTALLER_ARCH).so TLS_TEST_32_C = g_tls_test_32.c TLS_TEST_DSO_32_C = g_tls_test_dso_32.c -TLS_TEST_32 = tls_test_Linux-x86 -TLS_TEST_DSO_SO_32 = tls_test_dso_Linux-x86.so +TLS_TEST_32 = tls_test_$(INSTALLER_OS)-x86 +TLS_TEST_DSO_SO_32 = tls_test_dso_$(INSTALLER_OS)-x86.so + +RTLD_TEST_C = g_rtld_test.c +RTLD_TEST = rtld_test_$(INSTALLER_OS)-$(INSTALLER_ARCH) + +RTLD_TEST_32_C = g_rtld_test_32.c +RTLD_TEST_32 = rtld_test_$(INSTALLER_OS)-x86 GEN_UI_ARRAY = ./gen-ui-array CONFIG_H = config.h @@ -115,7 +121,8 @@ TLS_MODEL=initial-exec PIC=-fPIC CFLAGS += -DNV_X86_64 # Only Linux-x86_64 needs the tls_test_32 files -COMPAT_32_SRC = $(TLS_TEST_32_C) $(TLS_TEST_DSO_32_C) +COMPAT_32_SRC = $(TLS_TEST_32_C) $(TLS_TEST_DSO_32_C) \ + $(RTLD_TEST_32_C) else # So far all other platforms use local-exec TLS_MODEL=local-exec @@ -144,7 +151,7 @@ SRC = backup.c \ sanity.c ALL_SRC = $(SRC) $(NCURSES_UI_C) $(TLS_TEST_C) $(TLS_TEST_DSO_C) \ - $(COMPAT_32_SRC) $(STAMP_C) + $(RTLD_TEST_C) $(COMPAT_32_SRC) $(STAMP_C) OBJS = $(ALL_SRC:.c=.o) @@ -196,6 +203,12 @@ $(TLS_TEST_32_C): $(GEN_UI_ARRAY) $(TLS_TEST_32) $(TLS_TEST_DSO_32_C): $(GEN_UI_ARRAY) $(TLS_TEST_DSO_SO_32) $(GEN_UI_ARRAY) $(TLS_TEST_DSO_SO_32) tls_test_dso_array_32 > $@ +$(RTLD_TEST_C): $(GEN_UI_ARRAY) $(RTLD_TEST) + $(GEN_UI_ARRAY) $(RTLD_TEST) rtld_test_array > $@ + +$(RTLD_TEST_32_C): $(GEN_UI_ARRAY) $(RTLD_TEST_32) + $(GEN_UI_ARRAY) $(RTLD_TEST_32) rtld_test_array_32 > $@ + ncurses-ui.o: ncurses-ui.c $(CONFIG_H) $(CC) -c $(ALL_CFLAGS) $< -fPIC -o $@ @@ -228,7 +241,7 @@ $(STAMP_C): $(filter-out $(STAMP_C:.c=.o), $(OBJS)) clean clobber: rm -rf $(NVIDIA_INSTALLER) $(MKPRECOMPILED) \ $(NCURSES_UI) $(NCURSES_UI_C) \ - $(TLS_TEST_C) $(TLS_TEST_DSO_C) $(COMPAT_32_SRC) \ + $(TLS_TEST_C) $(TLS_TEST_DSO_C) $(RTLD_TEST_C) $(COMPAT_32_SRC) \ $(GEN_UI_ARRAY) $(CONFIG_H) $(STAMP_C) *.o *~ *.d # rule to rebuild tls_test and tls_test_dso; a precompiled tls_test @@ -250,6 +263,19 @@ rebuild_tls_test_dso: tls_test_dso.c tls_test: tls_test.c touch $@ +# rule to rebuild rtld_test; a precompiled rtld_test is distributed with +# nvidia-installer to simplify x86-64 builds. + +rebuild_rtld_test: rtld_test.c + gcc -Wall -O2 -fomit-frame-pointer -o $(RTLD_TEST) -lGL $< + strip $(RTLD_TEST) + +# dummy rule to override implicit rule that builds dls_test from +# rtld_test.c + +rtld_test: rtld_test.c + touch $@ + print_version: @ echo $(NVIDIA_INSTALLER_VERSION) diff --git a/backup.c b/backup.c index 9841541..899cd74 100644 --- a/backup.c +++ b/backup.c @@ -219,8 +219,14 @@ int do_backup(Options *op, const char *filename) } if (lstat(filename, &stat_buf) == -1) { - ui_error(op, "Unable to determine properties for file '%s' (%s).", - filename, strerror(errno)); + switch (errno) { + case ENOENT: + ret_val = TRUE; + break; + default: + ui_error(op, "Unable to determine properties for file '%s' (%s).", + filename, strerror(errno)); + } goto done; } diff --git a/command-list.c b/command-list.c index ae63036..fc721d6 100644 --- a/command-list.c +++ b/command-list.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -91,17 +92,33 @@ CommandList *build_command_list(Options *op, Package *p) if (!op->kernel_module_only) { find_conflicting_xfree86_libraries - (DEFAULT_XFREE86_INSTALLATION_PREFIX,l); + (op, DEFAULT_XFREE86_INSTALLATION_PREFIX, l); if (strcmp(DEFAULT_XFREE86_INSTALLATION_PREFIX, op->xfree86_prefix) != 0) - find_conflicting_xfree86_libraries(op->xfree86_prefix, l); + find_conflicting_xfree86_libraries(op, op->xfree86_prefix, l); find_conflicting_opengl_libraries - (DEFAULT_OPENGL_INSTALLATION_PREFIX,l); + (op, DEFAULT_OPENGL_INSTALLATION_PREFIX, l); if (strcmp(DEFAULT_OPENGL_INSTALLATION_PREFIX, op->opengl_prefix) != 0) - find_conflicting_opengl_libraries(op->opengl_prefix, l); + find_conflicting_opengl_libraries(op, op->opengl_prefix, l); + +#if defined(NV_X86_64) + if (op->compat32_prefix != NULL) { + char *prefix = nvstrcat(op->compat32_prefix, + DEFAULT_OPENGL_INSTALLATION_PREFIX, NULL); + find_conflicting_opengl_libraries(op, prefix, l); + nvfree(prefix); + + if (strcmp(DEFAULT_OPENGL_INSTALLATION_PREFIX, + op->opengl_prefix) != 0) { + prefix = nvstrcat(op->compat32_prefix, op->opengl_prefix, NULL); + find_conflicting_opengl_libraries(op, prefix, l); + nvfree(prefix); + } + } +#endif /* NV_X86_64 */ } find_conflicting_kernel_modules(op, p, l); @@ -148,6 +165,16 @@ CommandList *build_command_list(Options *op, Package *p) p->entries[i].dst, p->entries[i].mode); } + + /* + * delete the temporary libGL.la file generated based on + * the template earlier. + */ + + if (p->entries[i].flags & FILE_TYPE_LIBGL_LA) { + add_command(c, DELETE_CMD, + p->entries[i].file); + } } @@ -365,7 +392,24 @@ int execute_command_list(Options *op, CommandList *c, *************************************************************************** */ -static const char *__libdirs[] = { "lib", "lib64", NULL }; +typedef struct { + const char *name; + int len; +} ConflictingLibInfo; + +static void find_conflicting_libraries(Options *op, + const char *prefix, + ConflictingLibInfo *libs, + FileList *l); + +static ConflictingLibInfo __xfree86_libs[] = { + { "libGLcore.", 10 /* strlen("libGLcore.") */ }, + { "libGL.", 6 /* strlen("libGL.") */ }, + { "libGLwrapper.", 13 /* strlen("libGLwrapper.") */ }, + { "libglx.", 7 /* strlen("libglx.") */ }, + { "libXvMCNVIDIA", 14 /* strlen("libXvMCNVIDIA") */ }, + { NULL, 0 } +}; /* * find_conflicting_xfree86_libraries() - search for conflicting @@ -373,60 +417,21 @@ static const char *__libdirs[] = { "lib", "lib64", NULL }; * libdirs. */ -void find_conflicting_xfree86_libraries(const char *xprefix, FileList *l) +void find_conflicting_xfree86_libraries(Options *op, + const char *xprefix, FileList *l) { - char *s; - const char *libdir; - int i; + find_conflicting_libraries(op, xprefix, __xfree86_libs, l); - for (i = 0; __libdirs[i]; i++) { - - libdir = __libdirs[i]; - - /* - * [xprefix]/[libdir]/libGL.* - * [xprefix]/[libdir]/libGLcore.* - * [xprefix]/[libdir]/libXvMCNVIDIA* - * [xprefix]/[libdir]/libGLwrapper.* - */ - - s = nvstrcat(xprefix, "/", libdir, NULL); - find_matches(s, "libGL.", l, FALSE); - find_matches(s, "libGLcore.", l, FALSE); - find_matches(s, "libXvMCNVIDIA", l, FALSE); - find_matches(s, "libGLwrapper.", l, FALSE); - free(s); - - /* - * [xprefix]/[libdir]/tls/libGL.* - * [xprefix]/[libdir]/tls/libGLcore.* - * [xprefix]/[libdir]/tls/libXvMCNVIDIA* - * [xprefix]/[libdir]/tls/libGLwrapper.* - */ - - s = nvstrcat(xprefix, "/", libdir, "/tls", NULL); - find_matches(s, "libGL.", l, FALSE); - find_matches(s, "libGLcore.", l, FALSE); - find_matches(s, "libXvMCNVIDIA", l, FALSE); - find_matches(s, "libGLwrapper.", l, FALSE); - free(s); - - /* - * [xprefix]/[libdir]/modules/extensions/libGLcore.* - * [xprefix]/[libdir]/modules/extensions/libglx.* - * [xprefix]/[libdir]/modules/extensions/libGLwrapper.* - */ - - s = nvstrcat(xprefix, "/", libdir, "/modules/extensions", NULL); - find_matches(s, "libglx.", l, FALSE); - find_matches(s, "libGLcore.", l, FALSE); - find_matches(s, "libGLwrapper.", l, FALSE); - free(s); - } - } /* find_conflicting_xfree86_libraries() */ +static ConflictingLibInfo __opengl_libs[] = { + { "libGLcore.", 10 /* strlen("libGLcore.") */ }, + { "libGL.", 6 /* strlen("libGL.") */ }, + { "libnvidia-tls.", 14 /* strlen("libnvidia-tls.") */ }, + { "libGLwrapper.", 13 /* strlen("libGLwrapper.") */ }, + { NULL, 0 } +}; /* * find_conflicting_opengl_libraries() - search for conflicting @@ -434,40 +439,11 @@ void find_conflicting_xfree86_libraries(const char *xprefix, FileList *l) * libdirs. */ -void find_conflicting_opengl_libraries(const char *glprefix, FileList *l) +void find_conflicting_opengl_libraries(Options *op, + const char *glprefix, FileList *l) { - char *s; - const char *libdir; - int i; + find_conflicting_libraries(op, glprefix, __opengl_libs, l); - for (i = 0; __libdirs[i]; i++) { - - libdir = __libdirs[i]; - - /* - * [glprefix]/[libdir]/libGL.* - * [glprefix]/[libdir]/libGLcore.* - * [glprefix]/[libdir]/libGLwrapper.* - * [glprefix]/[libdir]/tls/libGL.* - * [glprefix]/[libdir]/tls/libGLcore.* - * [glprefix]/[libdir]/tls/libGLwrapper.* - */ - - s = nvstrcat(glprefix, "/", libdir, NULL); - find_matches(s, "libGL.", l, FALSE); - find_matches(s, "libGLcore.", l, FALSE); - find_matches(s, "libGLwrapper.", l, FALSE); - find_matches(s, "libnvidia-tls.", l, FALSE); - free(s); - - s = nvstrcat(glprefix, "/", libdir, "/tls", NULL); - find_matches(s, "libGL.", l, FALSE); - find_matches(s, "libGLcore.", l, FALSE); - find_matches(s, "libGLwrapper.", l, FALSE); - find_matches(s, "libnvidia-tls.", l, FALSE); - free(s); - } - } /* find_conflicting_opengl_libraries() */ @@ -538,6 +514,66 @@ static void find_existing_files(Package *p, FileList *l, unsigned int flag) + +/* + * find_conflicting_libraries() - search for any conflicting + * libraries in all relevant libdirs within the hierarchy under + * the given prefix. + */ + +static void find_conflicting_libraries(Options *op, + const char *prefix, + ConflictingLibInfo *libs, + FileList *l) +{ + int i; + char *paths[3]; + FTS *fts; + FTSENT *ent; + + paths[0] = nvstrcat(prefix, "/", "lib", NULL); + paths[1] = nvstrcat(prefix, "/", "lib64", NULL); + paths[2] = NULL; + + fts = fts_open(paths, FTS_LOGICAL | FTS_NOSTAT, NULL); + if (!fts) return; + + while ((ent = fts_read(fts)) != NULL) { + switch (ent->fts_info) { + case FTS_F: + case FTS_SLNONE: + for (i = 0; libs[i].name; i++) { + if (!strncmp(ent->fts_name, libs[i].name, libs[i].len)) + add_file_to_list(NULL, ent->fts_path, l); + } + break; + + case FTS_DP: + case FTS_D: + if (op->no_recursion) + fts_set(fts, ent, FTS_SKIP); + break; + + default: + /* + * we only care about regular files, symbolic links + * 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. + */ + break; + } + } + + fts_close(fts); + + for (i = 0; paths[i]; i++) + nvfree(paths[i]); + +} /* find_conflicting_libraries() */ + + /* * condense_file_list() - Take a FileList stucture and delete any * duplicate entries in the list. This is a pretty brain dead diff --git a/command-list.h b/command-list.h index d25415e..ca85a23 100644 --- a/command-list.h +++ b/command-list.h @@ -83,8 +83,8 @@ CommandList *build_command_list(Options*, Package *); void free_command_list(Options*, CommandList*); int execute_command_list(Options*, CommandList*, const char*, const char*); -void find_conflicting_xfree86_libraries(const char*, FileList*); -void find_conflicting_opengl_libraries(const char*, FileList*); +void find_conflicting_xfree86_libraries(Options*,const char*, FileList*); +void find_conflicting_opengl_libraries(Options*, const char*, FileList*); void condense_file_list(FileList *l); #endif /* __NVIDIA_INSTALLER_COMMAND_LIST_H__ */ diff --git a/crc.c b/crc.c index d085e1a..a5b4a4f 100644 --- a/crc.c +++ b/crc.c @@ -92,6 +92,7 @@ uint32 compute_crc(Options *op, const char *filename) if ((fd = open(filename, O_RDONLY)) == -1) goto fail; if (fstat(fd, &stat_buf) == -1) goto fail; + if (stat_buf.st_size == 0) return 0; len = stat_buf.st_size; buf = mmap(0, len, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); diff --git a/files.c b/files.c index 02934e0..a407520 100644 --- a/files.c +++ b/files.c @@ -223,6 +223,8 @@ int copy_file(Options *op, const char *srcfile, srcfile, strerror (errno)); goto fail; } + if (stat_buf.st_size == 0) + goto done; if (lseek(dst_fd, stat_buf.st_size - 1, SEEK_SET) == -1) { ui_error (op, "Unable to set file size for '%s' (%s)", dstfile, strerror (errno)); @@ -259,6 +261,7 @@ int copy_file(Options *op, const char *srcfile, goto fail; } + done: /* * the mode used to create dst_fd may have been affected by the * user's umask; so explicitly set the mode again @@ -377,7 +380,9 @@ void select_tls_class(Options *op, Package *p) int i; if (!tls_test(op, FALSE)) { - + op->which_tls = (op->which_tls & TLS_LIB_TYPE_FORCED); + op->which_tls |= TLS_LIB_CLASSIC_TLS; + /* * tls libraries will not run on this system; just install the * classic OpenGL libraries: clear the FILE_TYPE of any @@ -387,12 +392,16 @@ void select_tls_class(Options *op, Package *p) ui_log(op, "Installing classic TLS OpenGL libraries."); for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_CLASS_NEW_TLS) { + if ((p->entries[i].flags & FILE_CLASS_NEW_TLS) && + (p->entries[i].flags & FILE_CLASS_NATIVE)) { + nvfree(p->entries[i].dst); p->entries[i].flags &= ~FILE_TYPE_MASK; p->entries[i].dst = NULL; } } } else { + op->which_tls = (op->which_tls & TLS_LIB_TYPE_FORCED); + op->which_tls |= TLS_LIB_NEW_TLS; /* * tls libraries will run on this system: install both the @@ -410,7 +419,9 @@ void select_tls_class(Options *op, Package *p) */ if (!tls_test(op, TRUE)) { - + op->which_tls_compat32 = (op->which_tls_compat32 & TLS_LIB_TYPE_FORCED); + op->which_tls_compat32 |= TLS_LIB_CLASSIC_TLS; + /* * 32bit tls libraries will not run on this system; just * install the classic OpenGL libraries: clear the FILE_TYPE @@ -420,13 +431,17 @@ void select_tls_class(Options *op, Package *p) ui_log(op, "Installing classic TLS 32bit OpenGL libraries."); for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_CLASS_NEW_TLS_32) { + if ((p->entries[i].flags & FILE_CLASS_NEW_TLS) && + (p->entries[i].flags & FILE_CLASS_COMPAT32)) { + nvfree(p->entries[i].dst); p->entries[i].flags &= ~FILE_TYPE_MASK; p->entries[i].dst = NULL; } } } else { - + op->which_tls_compat32 = (op->which_tls_compat32 & TLS_LIB_TYPE_FORCED); + op->which_tls_compat32 |= TLS_LIB_NEW_TLS; + /* * 32bit tls libraries will run on this system: install both * the classic and new TLS libraries. @@ -478,6 +493,17 @@ int set_destinations(Options *op, Package *p) path = p->entries[i].path; break; + case FILE_TYPE_TLS_LIB: + case FILE_TYPE_TLS_SYMLINK: + prefix = op->opengl_prefix; + path = p->entries[i].path; + break; + + case FILE_TYPE_LIBGL_LA: + prefix = op->opengl_prefix; + path = p->entries[i].path; + break; + /* * XXX should the OpenGL headers and documentation also go * under the OpenGL installation prefix? The Linux OpenGL @@ -528,6 +554,23 @@ int set_destinations(Options *op, Package *p) name = p->entries[i].name; p->entries[i].dst = nvstrcat(prefix, "/", path, "/", name, NULL); + +#if defined(NV_X86_64) + if ((p->entries[i].flags & FILE_CLASS_COMPAT32) && + (op->compat32_prefix != NULL)) { + + /* + * prepend an additional prefix; this is currently only + * used for Debian GNU/Linux on Linux/x86-64, but may see + * more use in the future. + */ + + char *dst = p->entries[i].dst; + p->entries[i].dst = nvstrcat(op->compat32_prefix, dst, NULL); + + nvfree(dst); + } +#endif /* NV_X86_64 */ } return TRUE; @@ -625,6 +668,21 @@ int get_prefixes (Options *op) remove_trailing_slashes(op->opengl_prefix); ui_expert(op, "OpenGL installation prefix is: '%s'", op->opengl_prefix); +#if defined(NV_X86_64) + if (op->expert) { + ret = ui_get_input(op, op->compat32_prefix, + "Compat32 installation prefix"); + if (ret && ret[0]) { + op->compat32_prefix = ret; + if (!confirm_path(op, op->compat32_prefix)) return FALSE; + } + } + + remove_trailing_slashes(op->compat32_prefix); + ui_expert(op, "Compat32 installation prefix is: '%s'", + op->compat32_prefix); +#endif /* NV_X86_64 */ + if (op->expert) { ret = ui_get_input(op, op->installer_prefix, "Installer installation prefix"); @@ -717,7 +775,10 @@ void remove_non_kernel_module_files_from_package(Options *op, Package *p) void remove_trailing_slashes(char *s) { - int len = strlen(s); + int len; + + if (s == NULL) return; + len = strlen(s); while (s[len-1] == '/') s[--len] = '\0'; @@ -1348,8 +1409,10 @@ void process_libGL_la_files(Options *op, Package *p) struct stat stat_buf; char *src, *dst, *tmp, *tmp0, *tmp1, *tmpfile; - for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_TYPE_LIBGL_LA) { + int package_num_entries = p->num_entries; + + for (i = 0; i < package_num_entries; i++) { + if ((p->entries[i].flags & FILE_TYPE_LIBGL_LA)) { src_fd = dst_fd = -1; tmp0 = tmp1 = src = dst = tmpfile = NULL; @@ -1436,6 +1499,11 @@ void process_libGL_la_files(Options *op, Package *p) memcpy(dst, tmp1, len); + /* invalidate the template file */ + + p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].dst = NULL; + /* add this new file to the package */ n = p->num_entries; @@ -1447,7 +1515,7 @@ void process_libGL_la_files(Options *op, Package *p) p->entries[n].path = p->entries[i].path; p->entries[n].target = NULL; p->entries[n].flags = ((p->entries[i].flags & FILE_CLASS_MASK) | - FILE_TYPE_OPENGL_LIB); + FILE_TYPE_LIBGL_LA); p->entries[n].mode = p->entries[i].mode; p->entries[n].name = nvstrdup(p->entries[i].name); diff --git a/install-from-cwd.c b/install-from-cwd.c index eb72e77..514b79f 100644 --- a/install-from-cwd.c +++ b/install-from-cwd.c @@ -150,8 +150,12 @@ int install_from_cwd(Options *op) if (!link_kernel_module(op, p)) goto failed; } else { - - /* XXX we need to do a cc version check */ + /* + * make sure the required development tools are present on + * this system before attempting to verify the compiler and + * trying to build a custom kernel interface. + */ + if (!check_development_tools(op)) goto failed; /* * we do not have a prebuilt kernel interface; thus we'll need @@ -162,6 +166,13 @@ int install_from_cwd(Options *op) if (!determine_kernel_source_path(op)) goto failed; determine_kernel_output_path(op); + /* + * make sure that the selected or default system compiler + * is compatible with the target kernel; the user may choose + * to override the check. + */ + if (!check_cc_version(op, p)) goto failed; + /* and now, build the kernel interface */ if (!build_kernel_module(op, p)) goto failed; @@ -211,6 +222,15 @@ int install_from_cwd(Options *op) */ process_libGL_la_files(op, p); + +#if defined(NV_X86_64) + /* + * ask if we should install the 32bit compatibility files on + * this machine. + */ + + should_install_compat32_files(op, p); +#endif /* NV_X86_64 */ } /* @@ -264,6 +284,7 @@ int install_from_cwd(Options *op) check_installed_files_from_package(op, p); if (!check_sysvipc(op)) goto failed; + if (!check_runtime_configuration(op, p)) goto failed; /* done */ @@ -367,6 +388,8 @@ int add_this_kernel(Options *op) * - a filename (relative to the cwd) * - an octal value describing the permissions * - a flag describing the file type + * - certain file types have an architecture + * - certain file types have a second flag * - certain file types will have a path * - symbolic links will name the target of the link */ @@ -378,7 +401,7 @@ static Package *parse_manifest (Options *op) int fd, len = 0; struct stat stat_buf; Package *p; - char *manifest = NULL, *ptr; + char *manifest = MAP_FAILED, *ptr; p = (Package *) nvalloc(sizeof (Package)); @@ -396,7 +419,7 @@ static Package *parse_manifest (Options *op) len = stat_buf.st_size; manifest = mmap(0, len, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); - if (manifest == (void *) -1) goto cannot_open; + if (manifest == MAP_FAILED) goto cannot_open; /* the first line is the description */ @@ -412,35 +435,20 @@ static Package *parse_manifest (Options *op) if (!nvid_version(p->version_string, &p->major, &p->minor, &p->patch)) goto invalid_manifest_file; - /* the third line is the kernel module filename */ - - line++; - p->kernel_module_filename = get_next_line(ptr, &ptr); - if (!p->kernel_module_filename) goto invalid_manifest_file; - - /* - * XXX The kernel module filename in the .manifest file can no - * longer be used: the filename is different for 2.4 or 2.6 - * kernels. - */ - - free(p->kernel_module_filename); - p->kernel_module_filename = NULL; - - /* new fourth line is the kernel interface filename */ + /* new third line is the kernel interface filename */ line++; p->kernel_interface_filename = get_next_line(ptr, &ptr); if (!p->kernel_interface_filename) goto invalid_manifest_file; - /* the fifth line is the kernel module name */ + /* the fourth line is the kernel module name */ line++; p->kernel_module_name = get_next_line(ptr, &ptr); if (!p->kernel_module_name) goto invalid_manifest_file; /* - * the sixth line is a whitespace-separated list of kernel modules + * the fifth line is a whitespace-separated list of kernel modules * to be unloaded before installing the new kernel module */ @@ -460,7 +468,7 @@ static Package *parse_manifest (Options *op) } while (p->bad_modules[n-1]); /* - * the seventh line is a whitespace-separated list of kernel module + * the sixth line is a whitespace-separated list of kernel module * filenames to be uninstalled before installing the new kernel * module */ @@ -480,7 +488,7 @@ static Package *parse_manifest (Options *op) p->bad_module_filenames[n-1] = read_next_word(c, &c); } while (p->bad_module_filenames[n-1]); - /* the eighth line is the kernel module build directory */ + /* the seventh line is the kernel module build directory */ line++; p->kernel_module_build_directory = get_next_line(ptr, &ptr); @@ -488,7 +496,7 @@ static Package *parse_manifest (Options *op) remove_trailing_slashes(p->kernel_module_build_directory); /* - * the nineth line is the directory containing precompiled kernel + * the eigth line is the directory containing precompiled kernel * interfaces */ @@ -539,7 +547,7 @@ static Package *parse_manifest (Options *op) } free(tmpstr); - /* now, parse the flags */ + /* every file has a type field */ p->entries[n].flags = 0x0; @@ -558,39 +566,63 @@ static Package *parse_manifest (Options *op) p->entries[n].flags |= FILE_TYPE_LIBGL_LA; else if (strcmp(flag, "XFREE86_LIB") == 0) p->entries[n].flags |= FILE_TYPE_XFREE86_LIB; + else if (strcmp(flag, "TLS_LIB") == 0) + p->entries[n].flags |= FILE_TYPE_TLS_LIB; else if (strcmp(flag, "DOCUMENTATION") == 0) p->entries[n].flags |= FILE_TYPE_DOCUMENTATION; else if (strcmp(flag, "OPENGL_SYMLINK") == 0) p->entries[n].flags |= FILE_TYPE_OPENGL_SYMLINK; else if (strcmp(flag, "XFREE86_SYMLINK") == 0) p->entries[n].flags |= FILE_TYPE_XFREE86_SYMLINK; + else if (strcmp(flag, "TLS_SYMLINK") == 0) + p->entries[n].flags |= FILE_TYPE_TLS_SYMLINK; else if (strcmp(flag, "INSTALLER_BINARY") == 0) p->entries[n].flags |= FILE_TYPE_INSTALLER_BINARY; else if (strcmp(flag, "UTILITY_BINARY") == 0) p->entries[n].flags |= FILE_TYPE_UTILITY_BINARY; - else if (strcmp(flag, "OPENGL_LIB_CLASSIC_TLS") == 0) - p->entries[n].flags |= (FILE_TYPE_OPENGL_LIB | - FILE_CLASS_CLASSIC_TLS); - else if (strcmp(flag, "OPENGL_LIB_NEW_TLS") == 0) - p->entries[n].flags |= (FILE_TYPE_OPENGL_LIB | - FILE_CLASS_NEW_TLS); - else if (strcmp(flag, "OPENGL_SYMLINK_CLASSIC_TLS") == 0) - p->entries[n].flags |= (FILE_TYPE_OPENGL_SYMLINK | - FILE_CLASS_CLASSIC_TLS); - else if (strcmp(flag, "OPENGL_SYMLINK_NEW_TLS") == 0) - p->entries[n].flags |= (FILE_TYPE_OPENGL_SYMLINK | - FILE_CLASS_NEW_TLS); - else if (strcmp(flag, "OPENGL_LIB_NEW_TLS_32") == 0) - p->entries[n].flags |= (FILE_TYPE_OPENGL_LIB | - FILE_CLASS_NEW_TLS_32); - else if (strcmp(flag, "OPENGL_SYMLINK_NEW_TLS_32") == 0) - p->entries[n].flags |= (FILE_TYPE_OPENGL_SYMLINK | - FILE_CLASS_NEW_TLS_32); - else + else { + nvfree(flag); goto invalid_manifest_file; - - free(flag); - + } + + nvfree(flag); + + /* some libs/symlinks have an arch field */ + + if (p->entries[n].flags & FILE_TYPE_HAVE_ARCH) { + flag = read_next_word(c, &c); + if (!flag) goto invalid_manifest_file; + + if (strcmp(flag, "COMPAT32") == 0) + p->entries[n].flags |= FILE_CLASS_COMPAT32; + else if (strcmp(flag, "NATIVE") == 0) + p->entries[n].flags |= FILE_CLASS_NATIVE; + else { + nvfree(flag); + goto invalid_manifest_file; + } + + nvfree(flag); + } + + /* some libs/symlinks have a class field */ + + if (p->entries[n].flags & FILE_TYPE_HAVE_CLASS) { + flag = read_next_word(c, &c); + if (!flag) goto invalid_manifest_file; + + if (strcmp(flag, "CLASSIC") == 0) + p->entries[n].flags |= FILE_CLASS_CLASSIC_TLS; + else if (strcmp(flag, "NEW") == 0) + p->entries[n].flags |= FILE_CLASS_NEW_TLS; + else { + nvfree(flag); + goto invalid_manifest_file; + } + + nvfree(flag); + } + /* libs and documentation have a path field */ if (p->entries[n].flags & FILE_TYPE_HAVE_PATH) { @@ -629,7 +661,7 @@ static Package *parse_manifest (Options *op) } while (!done); - if (manifest) munmap(manifest, len); + munmap(manifest, len); if (fd != -1) close(fd); return p; @@ -647,7 +679,7 @@ static Package *parse_manifest (Options *op) fail: if (p && p->entries) free(p->entries); if (p) free(p); - if (manifest) munmap(manifest, len); + if (manifest != MAP_FAILED) munmap(manifest, len); if (fd != -1) close(fd); return NULL; diff --git a/kernel.c b/kernel.c index 9e332ff..227f665 100644 --- a/kernel.c +++ b/kernel.c @@ -51,7 +51,6 @@ static int check_for_loaded_kernel_module(Options *op, const char *); static int rmmod_kernel_module(Options *op, const char *); static PrecompiledInfo *download_updated_kernel_interface(Options*, Package*, const char*); -static int cc_version_check(Options *op, Package *p); static int rivafb_check(Options *op, Package *p); static void rivafb_module_check(Options *op, Package *p); @@ -201,13 +200,23 @@ int determine_kernel_source_path(Options *op) /* * I suppose we could ask them here for the kernel source - * path, but we've already given them multiple methods of + * path, but we've already given users multiple methods of * specifying their kernel source tree. */ return FALSE; } - + + /* reject /usr as an invalid kernel source path */ + + if (!strcmp(op->kernel_source_path, "/usr") || + !strcmp(op->kernel_source_path, "/usr/")) { + ui_error (op, "The kernel source path '%s' is invalid. %s", + op->kernel_source_path, install_your_kernel_source); + op->kernel_source_path = NULL; + return FALSE; + } + /* check that the kernel source path exists */ if (!directory_exists(op, op->kernel_source_path)) { @@ -299,7 +308,8 @@ int determine_kernel_source_path(Options *op) int determine_kernel_output_path(Options *op) { - char *str; + char *str, *tmp; + int len; /* check --kernel-output-path */ @@ -307,6 +317,14 @@ int determine_kernel_output_path(Options *op) ui_log(op, "Using the kernel output path '%s' as specified by the " "'--kernel-output-path' commandline option.", op->kernel_output_path); + + if (!directory_exists(op, op->kernel_output_path)) { + ui_error(op, "The kernel output path '%s' does not exist.", + op->kernel_output_path); + op->kernel_output_path = NULL; + return FALSE; + } + return TRUE; } @@ -317,9 +335,38 @@ int determine_kernel_output_path(Options *op) ui_log(op, "Using the kernel output path '%s', as specified by the " "SYSOUT environment variable.", str); op->kernel_output_path = str; + + if (!directory_exists(op, op->kernel_output_path)) { + ui_error(op, "The kernel output path '%s' does not exist.", + op->kernel_output_path); + op->kernel_output_path = NULL; + return FALSE; + } + return TRUE; } + /* check /lib/modules/`uname -r`/{source,build} */ + + tmp = get_kernel_name(op); + + if (tmp) { + str = nvstrcat("/lib/modules/", tmp, "/source", NULL); + len = strlen(str); + + if (!strncmp(op->kernel_source_path, str, len)) { + nvfree(str); + str = nvstrcat("/lib/modules/", tmp, "/build", NULL); + + if (directory_exists(op, str)) { + op->kernel_output_path = str; + return TRUE; + } + } + + nvfree(str); + } + op->kernel_output_path = op->kernel_source_path; return TRUE; } @@ -368,9 +415,6 @@ int link_kernel_module(Options *op, Package *p) * build_kernel_module() - determine the kernel include directory, * copy the kernel module source files into a temporary directory, and * compile nvidia.o. - * - * XXX depends on things like make, gcc, ld, existing. Should we - * check that the user has these before doing this? */ int build_kernel_module(Options *op, Package *p) @@ -378,8 +422,6 @@ int build_kernel_module(Options *op, Package *p) char *result, *cmd, *tmp; int len, ret; - if (!cc_version_check(op, p)) return FALSE; - /* * touch all the files in the build directory to avoid make time * skew messages @@ -625,11 +667,30 @@ int test_kernel_module(Options *op, Package *p) if (!op->expert) ui_log(op, "Kernel module load error: %s", data); ret = FALSE; + + nvfree(cmd); + nvfree(data); + + /* + * display/log the last five lines of the kernel ring buffer + * to provide further details on the load failure. + */ + + cmd = nvstrcat(op->utils[DMESG], " | ", + op->utils[TAIL], " -n 5", NULL); + + if (!run_command(op, cmd, &data, FALSE, 0, TRUE)) + ui_log(op, "Kernel messages:\n%s", data); } else { free(cmd); + + /* + * 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). + */ cmd = nvstrcat(op->utils[RMMOD], " ", p->kernel_module_name, NULL); run_command(op, cmd, NULL, FALSE, 0, TRUE); - /* what if we fail to rmmod? */ ret = TRUE; } @@ -770,12 +831,15 @@ int check_for_unloaded_kernel_module(Options *op, Package *p) if (check_for_loaded_kernel_module(op, p->bad_modules[n])) { ui_error(op, "An NVIDIA kernel module '%s' appears to already " "be loaded in your kernel. This may be because it is " - "in use (for example, by the X server). Please be " - "sure you have exited X before attempting to upgrade " - "your driver. If you have exited X but still receive " - "this message, then an error has occured that has " - "confused the usage count of the kernel module; the " - "simplest remedy is to reboot your computer.", + "in use (for example, by the X server), but may also " + "happen if your kernel was configured without support " + "for module unloading. Please be sure you have exited " + "X before attempting to upgrade your driver. If you " + "have exited X, know that your kernel supports module " + "unloading, and still receive this message, then an " + "error may have occured that has corrupted the NVIDIA " + "kernel module's usage count; the simplest remedy is " + "to reboot your computer.", p->bad_modules[n]); return FALSE; @@ -1063,6 +1127,14 @@ static char *default_kernel_source_path(Options *op) tmp = get_kernel_name(op); if (tmp) { + str = nvstrcat("/lib/modules/", tmp, "/source", NULL); + + if (directory_exists(op, str)) { + return str; + } + + nvfree(str); + str = nvstrcat("/lib/modules/", tmp, "/build", NULL); if (directory_exists(op, str)) { @@ -1315,10 +1387,12 @@ download_updated_kernel_interface(Options *op, Package *p, /* - * cc_version_check() - + * check_cc_version() - check if the selected or default system + * compiler is compatible with the one that was used to build the + * currently running kernel. */ -static int cc_version_check(Options *op, Package *p) +int check_cc_version(Options *op, Package *p) { char *cmd, *CC, *result; int ret; @@ -1338,7 +1412,7 @@ static int cc_version_check(Options *op, Package *p) CC = getenv("CC"); if (!CC) CC = "cc"; - ui_log(op, "Performing cc_version_check with CC=\"%s\".", CC); + ui_log(op, "Performing CC test with CC=\"%s\".", CC); cmd = nvstrcat("sh ", p->kernel_module_build_directory, "/conftest.sh ", CC, " ", @@ -1366,7 +1440,7 @@ static int cc_version_check(Options *op, Package *p) return !ret; -} /* cc_version_check() */ +} /* check_cc_version() */ diff --git a/kernel.h b/kernel.h index e56bd22..686e60c 100644 --- a/kernel.h +++ b/kernel.h @@ -31,6 +31,7 @@ int determine_kernel_module_installation_path (Options*); int determine_kernel_source_path (Options*); int determine_kernel_output_path (Options*); int link_kernel_module (Options*, Package*); +int check_cc_version (Options*, Package*); int build_kernel_module (Options*, Package*); int build_kernel_interface (Options*, Package*); int test_kernel_module (Options*, Package*); diff --git a/log.c b/log.c index 84d8ef6..96a29e9 100644 --- a/log.c +++ b/log.c @@ -48,6 +48,15 @@ static FILE *log_file_stream; #define STRSTR(x) ((x) ? (x) : "(not specified)") +#define TLSSTR(x) ({ \ + const char *__tls_str = NULL; \ + switch (x) { \ + case FORCE_CLASSIC_TLS: __tls_str = "classic"; break; \ + case FORCE_NEW_TLS: __tls_str = "elf-tls"; break; \ + default: __tls_str = "(not specified)"; \ + } \ + __tls_str; \ +}) /* * log_init() - if logging is enabled, initialize the log file; if @@ -105,14 +114,44 @@ void log_init(Options *op) BOOLSTR(op->no_questions)); log_printf(op, TRUE, NULL, " silent : %s", BOOLSTR(op->silent)); + log_printf(op, TRUE, NULL, " no backup : %s", + BOOLSTR(op->no_backup)); + log_printf(op, TRUE, NULL, " kernel module only : %s", + BOOLSTR(op->kernel_module_only)); + log_printf(op, TRUE, NULL, " sanity : %s", + BOOLSTR(op->sanity)); + log_printf(op, TRUE, NULL, " add this kernel : %s", + BOOLSTR(op->add_this_kernel)); + log_printf(op, TRUE, NULL, " no runlevel check : %s", + BOOLSTR(op->no_runlevel_check)); + log_printf(op, TRUE, NULL, " no network : %s", + BOOLSTR(op->no_network)); + log_printf(op, TRUE, NULL, " no ABI note : %s", + BOOLSTR(op->no_abi_note)); + log_printf(op, TRUE, NULL, " no RPMs : %s", + BOOLSTR(op->no_rpms)); + log_printf(op, TRUE, NULL, " force tls : %s", + TLSSTR(op->which_tls)); + log_printf(op, TRUE, NULL, " force compat32 tls : %s", + TLSSTR(op->which_tls_compat32)); log_printf(op, TRUE, NULL, " X install prefix : %s", STRSTR(op->xfree86_prefix)); log_printf(op, TRUE, NULL, " OpenGL install prefix : %s", STRSTR(op->opengl_prefix)); - log_printf(op, TRUE, NULL, " Installer install prefix: %s", + log_printf(op, TRUE, NULL, " compat32 install prefix : %s", + STRSTR(op->compat32_prefix)); + log_printf(op, TRUE, NULL, " installer install prefix: %s", STRSTR(op->installer_prefix)); + log_printf(op, TRUE, NULL, " utility install prefix : %s", + STRSTR(op->utility_prefix)); + log_printf(op, TRUE, NULL, " kernel name : %s", + STRSTR(op->kernel_name)); + log_printf(op, TRUE, NULL, " kernel include path : %s", + STRSTR(op->kernel_include_path)); log_printf(op, TRUE, NULL, " kernel source path : %s", STRSTR(op->kernel_source_path)); + log_printf(op, TRUE, NULL, " kernel output path : %s", + STRSTR(op->kernel_output_path)); log_printf(op, TRUE, NULL, " kernel install path : %s", STRSTR(op->kernel_module_installation_path)); log_printf(op, TRUE, NULL, " proc mount point : %s", @@ -121,8 +160,10 @@ void log_init(Options *op) STRSTR(op->ui_str)); log_printf(op, TRUE, NULL, " tmpdir : %s", STRSTR(op->tmpdir)); - log_printf(op, TRUE, NULL, " ftp site : %s", + log_printf(op, TRUE, NULL, " ftp mirror : %s", STRSTR(op->ftp_site)); + log_printf(op, TRUE, NULL, " RPM file list : %s", + STRSTR(op->rpm_file_list)); log_printf(op, TRUE, NULL, ""); diff --git a/misc.c b/misc.c index dc8bfd6..5fddf54 100644 --- a/misc.c +++ b/misc.c @@ -49,7 +49,7 @@ static int check_symlink(Options*, const char*, const char*, const char*); static int check_file(Options*, const char*, const mode_t, const uint32); -static char *find_system_util(char *util); +static char *find_system_util(const char *util); /* @@ -635,16 +635,16 @@ int run_command(Options *op, const char *cmd, char **data, int output, int find_system_utils(Options *op) { - /* keep in sync with the Utils enum type */ - const struct { char *util, *package; } needed_utils[] = { - { "insmod", "modutils" }, - { "modprobe", "modutils" }, - { "rmmod", "modutils" }, - { "lsmod", "modutils" }, - { "depmod", "modutils" }, - { "ldconfig", "glibc" }, + /* keep in sync with the SystemUtils enum type */ + const struct { const char *util, *package; } needed_utils[] = { + { "ldconfig", "glibc" }, + { "ldd", "glibc" }, { "ld", "binutils" }, { "objcopy", "binutils" }, + { "grep", "grep" }, + { "dmesg", "util-linux" }, + { "tail", "coreutils" }, + { "cut", "coreutils" } }; int i; @@ -653,7 +653,7 @@ int find_system_utils(Options *op) /* search the PATH for each utility */ - for (i = 0; i < MAX_UTILS; i++) { + for (i = 0; i < MAX_SYSTEM_UTILS; i++) { op->utils[i] = find_system_util(needed_utils[i].util); if (!op->utils[i]) { ui_error(op, "Unable to find the system utility `%s`; please " @@ -675,13 +675,115 @@ int find_system_utils(Options *op) +typedef struct { + const char *util; + const char *package; +} Util; + +/* keep in sync with the ModuleUtils enum type */ +static Util __module_utils[] = { + { "insmod", "module-init-tools" }, + { "modprobe", "module-init-tools" }, + { "rmmod", "module-init-tools" }, + { "lsmod", "module-init-tools" }, + { "depmod", "module-init-tools" }, +}; + +/* keep in sync with the ModuleUtils enum type */ +static Util __module_utils_linux24[] = { + { "insmod", "modutils" }, + { "modprobe", "modutils" }, + { "rmmod", "modutils" }, + { "lsmod", "modutils" }, + { "depmod", "modutils" }, +}; + +/* + * find_module_utils() - search the $PATH (as well as some common + * additional directories) for the utilities that the installer will + * need to use. Returns TRUE on success and assigns the util fields + * in the option struct; it returns FALSE on failures. + */ + +int find_module_utils(Options *op) +{ + int i, j = 0; + Util *needed_utils = __module_utils; + + if (strncmp(get_kernel_name(op), "2.4", 3) == 0) + needed_utils = __module_utils_linux24; + + ui_expert(op, "Searching for module utilities:"); + + /* search the PATH for each utility */ + + for (i = MAX_SYSTEM_UTILS; i < MAX_UTILS; i++, j++) { + op->utils[i] = find_system_util(needed_utils[j].util); + if (!op->utils[i]) { + ui_error(op, "Unable to find the module utility `%s`; please " + "make sure you have the package '%s' installed. If " + "you do have %s installed, then please check that " + "`%s` is in your PATH.", + needed_utils[j].util, needed_utils[j].package, + needed_utils[j].package, needed_utils[j].util); + return FALSE; + } + + ui_expert(op, "found `%s` : `%s`", + needed_utils[j].util, op->utils[i]); + }; + + return TRUE; + +} /* find_module_utils() */ + + +/* + * check_development_tools() - check if the development tools needed + * to build custom kernel interfaces are available. + */ + +int check_development_tools(Options *op) +{ +#define MAX_TOOLS 2 + const struct { char *tool, *package; } needed_tools[] = { + { "cc", "gcc" }, + { "make", "make" } + }; + + int i; + char *tool; + + ui_expert(op, "Checking development tools:"); + + for (i = 0; i < MAX_TOOLS; i++) { + tool = find_system_util(needed_tools[i].tool); + if (!tool) { + ui_error(op, "Unable to find the development tool `%s` in " + "your path; please make sure that you have the " + "package '%s' installed. If %s is installed on your " + "system, then please check that `%s` is in your " + "PATH.", + needed_tools[i].tool, needed_tools[i].package, + needed_tools[i].package, needed_tools[i].tool); + return FALSE; + } + + ui_expert(op, "found `%s` : `%s`", needed_tools[i].tool, tool); + } + + return TRUE; + +} /* check_development_tools() */ + + /* * find_system_util() - build a search path and search for the named * utility. If the utility is found, the fully qualified path to the * utility is returned. On failure NULL is returned. */ -static char *find_system_util(char *util) +static char *find_system_util(const char *util) { char *buf, *path, *file, *x, *y; @@ -835,7 +937,10 @@ void should_install_opengl_headers(Options *op, Package *p) */ for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_TYPE_OPENGL_HEADER) have_headers = TRUE; + if (p->entries[i].flags & FILE_TYPE_OPENGL_HEADER) { + have_headers = TRUE; + break; + } } if (!have_headers) return; @@ -863,6 +968,44 @@ void should_install_opengl_headers(Options *op, Package *p) } /* should_install_opengl_headers() */ +/* + * should_install_compat32_files() - ask the user if he/she wishes to + * install 32bit compatibily libraries. + */ + +void should_install_compat32_files(Options *op, Package *p) +{ +#if defined(NV_X86_64) + int i, have_compat32_files = FALSE; + + /* + * first, scan through the package to see if we have any + * 32bit compatibility files to install. + */ + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + have_compat32_files = TRUE; + break; + } + } + + if (!have_compat32_files) + return; + + if (!ui_yes_no(op, TRUE, "Install NVIDIA's 32bit compatibility " + "OpenGL libraries?")) { + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + /* invalidate file */ + p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].dst = NULL; + } + } + } +#endif /* NV_X86_64 */ +} + /* * check_installed_files_from_package() - scan through the entries in @@ -899,12 +1042,8 @@ void check_installed_files_from_package(Options *op, Package *p) } ui_status_end(op, "done."); + ui_log(op, "Post-install sanity check %s.", ret ? "passed" : "failed"); - if (ret) { - ui_log(op, "Sanity check passed."); - } else { - ui_log(op, "The sanity check found some discrepancies."); - } } /* check_installed_files_from_package() */ @@ -1078,7 +1217,7 @@ extern const int tls_test_dso_array_32_size; /* forward prototype */ -static int tls_test_internal(Options *op, +static int tls_test_internal(Options *op, int which_tls, const unsigned char *test_array, const int test_array_size, const unsigned char *dso_test_array, @@ -1091,7 +1230,7 @@ int tls_test(Options *op, int compat_32_libs) if (compat_32_libs) { #if defined(NV_X86_64) - return tls_test_internal(op, + return tls_test_internal(op, op->which_tls_compat32, tls_test_array_32, tls_test_array_32_size, tls_test_dso_array_32, @@ -1101,7 +1240,7 @@ int tls_test(Options *op, int compat_32_libs) #endif /* NV_X86_64 */ } else { - return tls_test_internal(op, + return tls_test_internal(op, op->which_tls, tls_test_array, tls_test_array_size, tls_test_dso_array, @@ -1117,7 +1256,7 @@ int tls_test(Options *op, int compat_32_libs) * just selects which array data is used as the test. */ -static int tls_test_internal(Options *op, +static int tls_test_internal(Options *op, int which_tls, const unsigned char *test_array, const int test_array_size, const unsigned char *test_dso_array, @@ -1128,8 +1267,8 @@ static int tls_test_internal(Options *op, /* allow commandline options to bypass this test */ - if (op->which_tls == FORCE_NEW_TLS) return TRUE; - if (op->which_tls == FORCE_CLASSIC_TLS) return FALSE; + if (which_tls == FORCE_NEW_TLS) return TRUE; + if (which_tls == FORCE_CLASSIC_TLS) return FALSE; /* check that we have the test program */ @@ -1192,6 +1331,201 @@ static int tls_test_internal(Options *op, +/* + * check_runtime_configuration() - In the past, nvidia-installer has + * frequently failed to backup/move all conflicting files prior to + * installing the NVIDIA OpenGL libraries. Consequently, some of the + * installations considered successful by the installer didn't work + * correctly. + * + * This sanity check attemps to verify that the correct libraries are + * picked up by the runtime linker. It returns TRUE on success and + * FALSE on failure. + */ + +/* pull in the array and size from g_rtld_test.c */ + +extern const unsigned char rtld_test_array[]; +extern const int rtld_test_array_size; + +#if defined(NV_X86_64) + +/* pull in the array and size from g_rtld_test_32.c */ + +extern const unsigned char rtld_test_array_32[]; +extern const int rtld_test_array_32_size; + +#endif /* NV_X86_64 */ + + +/* forward prototype */ + +static int rtld_test_internal(Options *op, Package *p, + int which_tls, + const unsigned char *test_array, + const int test_array_size, + int compat_32_libs); + +int check_runtime_configuration(Options *op, Package *p) +{ + int ret = TRUE; + + ui_status_begin(op, "Running runtime sanity check:", "Checking"); + +#if defined(NV_X86_64) + ret = rtld_test_internal(op, p, op->which_tls_compat32, + rtld_test_array_32, + rtld_test_array_32_size, + TRUE); +#endif /* NV_X86_64 */ + + if (ret == TRUE) { + ret = rtld_test_internal(op, p, op->which_tls, + rtld_test_array, + rtld_test_array_size, + FALSE); + } + + ui_status_end(op, "done."); + ui_log(op, "Runtime sanity check %s.", ret ? "passed" : "failed"); + + return ret; + +} /* check_runtime_configuration() */ + + +/* + * rtld_test_internal() - this routine writes the test binaries to a file + * and performs the test; the caller (rtld_test()) selects which array data + * is used (native, compat_32). + */ + +static int rtld_test_internal(Options *op, Package *p, + int which_tls, + const unsigned char *test_array, + const int test_array_size, + int compat_32_libs) +{ + int i, ret = TRUE; + char *name = NULL, *cmd = NULL, *data = NULL; + char *tmpfile, *s; + struct stat stat_buf0, stat_buf1; + + if ((test_array == NULL) || (test_array_size == 0)) { + ui_warn(op, "The runtime configuration test program is not " + "present; assuming successful installation."); + return TRUE; + } + + /* write the rtld_test data to a temporary file */ + + tmpfile = write_temp_file(op, test_array_size, test_array, + S_IRUSR|S_IWUSR|S_IXUSR); + + if (!tmpfile) { + ui_warn(op, "Unable to create a temporary file for the runtime " + "configuration test program (%s); assuming successful " + "installation.", strerror(errno)); + goto done; + } + + /* perform the test(s) */ + + for (i = 0; i < p->num_entries; i++) { + if (!(p->entries[i].flags & FILE_TYPE_RTLD_CHECKED)) + continue; + else if ((which_tls & TLS_LIB_TYPE_FORCED) && + (p->entries[i].flags & FILE_TYPE_TLS_LIB)) + continue; +#if defined(NV_X86_64) + else if ((p->entries[i].flags & FILE_CLASS_NATIVE) + && compat_32_libs) + continue; + else if ((p->entries[i].flags & FILE_CLASS_COMPAT32) + && !compat_32_libs) + continue; +#endif /* NV_X86_64 */ + else if ((which_tls == TLS_LIB_NEW_TLS) && + (p->entries[i].flags & FILE_CLASS_CLASSIC_TLS)) + continue; + else if ((which_tls == TLS_LIB_CLASSIC_TLS) && + (p->entries[i].flags & FILE_CLASS_NEW_TLS)) + continue; + + name = nvstrdup(p->entries[i].name); + if (!name) continue; + + s = strstr(name, ".so.1"); + if (!s) goto next; + *(s + strlen(".so.1")) = '\0'; + + cmd = nvstrcat(op->utils[LDD], " ", tmpfile, " | ", + op->utils[GREP], " ", name, " | ", + op->utils[CUT], " -d \" \" -f 3", NULL); + + if (run_command(op, cmd, &data, FALSE, 0, TRUE)) { + ui_warn(op, "Unable to perform the runtime configuration " + "check (%s); assuming successful installation.", + strerror(errno)); + goto done; + } + + nvfree(name); name = NULL; + name = nvstrdup(p->entries[i].dst); + if (!name) goto next; + + s = strstr(name, ".so.1"); + if (!s) goto next; + *(s + strlen(".so.1")) = '\0'; + + if ((strcmp(data, name) != 0)) { + /* + * XXX Handle the case where the same library is + * referred to, once directly and once via a symbolic + * link. This check is far from perfect, but should + * get the job done. + */ + + if ((stat(data, &stat_buf0) == 0) && + (stat(name, &stat_buf1) == 0) && + (memcmp(&stat_buf0, &stat_buf1, sizeof(struct stat)) == 0)) + goto next; + + ui_error(op, "The runtime configuration check failed for " + "library '%s' (expected: '%s', found: '%s'). " + "The most likely reason for this is that conflicting " + "OpenGL libraries are installed in a location " + "not inspected by nvidia-installer. Please be sure " + "you have uninstalled any third-party OpenGL and " + "third-party graphics driver packages.", + p->entries[i].name, name, + (data && strlen(data)) ? data : "(not found)"); + + ret = FALSE; + goto done; + } + + next: + nvfree(name); name = NULL; + nvfree(cmd); cmd = NULL; + nvfree(data); data = NULL; + } + + done: + if (tmpfile) { + unlink(tmpfile); + nvfree(tmpfile); + } + + nvfree(name); + nvfree(cmd); + nvfree(data); + + return ret; + +} /* rtld_test_internal() */ + + /* * get_distribution() - determine what distribution this is; only used * for several bits of distro-specific behavior requested by @@ -1205,6 +1539,7 @@ Distribution get_distribution(Options *op) { if (access("/etc/SuSE-release", F_OK) == 0) return SUSE; if (access("/etc/UnitedLinux-release", F_OK) == 0) return UNITED_LINUX; + if (access("/etc/debian_version", F_OK) == 0) return DEBIAN; return OTHER; diff --git a/misc.h b/misc.h index d182d13..74b3e4b 100644 --- a/misc.h +++ b/misc.h @@ -51,13 +51,17 @@ char *get_next_line(char *buf, char **e); int run_command(Options *op, const char *cmd, char **data, int output, int status, int redirect); int find_system_utils(Options *op); +int find_module_utils(Options *op); +int check_development_tools(Options *op); int nvid_version (const char *str, int *major, int *minor, int *patch); int continue_after_error(Options *op, const char *fmt, ...); 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 check_installed_files_from_package(Options *op, Package *p); unsigned int get_installable_file_mask(Options *op); int tls_test(Options *op, int compat_32_libs); +int check_runtime_configuration(Options *op, Package *p); Distribution get_distribution(Options *op); int check_for_running_x(Options *op); diff --git a/nvidia-installer.c b/nvidia-installer.c index e40cd35..75c4a8d 100644 --- a/nvidia-installer.c +++ b/nvidia-installer.c @@ -231,6 +231,16 @@ static void print_advanced_options_args_only(int args_only) "mandates this default value.", DEFAULT_OPENGL_INSTALLATION_PREFIX); fmtout(""); + +#if defined(NV_X86_64) + fmtout("--compat32-prefix=[COMPAT32 PREFIX]"); + fmtoutp(TAB, "The path relative to which the 32bit compatibility " + "libraries will be installed on x86-64 systems; this option " + "is unset by default, the OpenGL prefix alone determines " + "the target location. Only under very rare circumstances " + "should this option need to be used."); + fmtout(""); +#endif /* NV_X86_64 */ fmtout("--installer-prefix=[INSTALLER PREFIX]"); fmtoutp(TAB, "The prefix under which the installer binary will be " @@ -348,6 +358,14 @@ static void print_advanced_options_args_only(int args_only) "'classic'."); fmtout(""); +#if defined(NV_X86_64) + fmtout("--force-tls-compat32=[TLS TYPE]"); + fmtoutp(TAB, "This option forces the installer to install a specific " + "32bit compatibility OpenGL TLS library; further details " + "can be found in the description of the '--force-tls' option."); + fmtout(""); +#endif /* NV_X86_64 */ + fmtout("-k, --kernel-name=[KERNELNAME]"); fmtoutp(TAB, "Build and install the NVIDIA kernel module for the " "non-running kernel specified by [KERNELNAME] ([KERNELNAME] " @@ -399,6 +417,13 @@ static void print_advanced_options_args_only(int args_only) "connect to the NVIDIA ftp site (for updated precompiled kernel " "interfaces, for example)."); fmtout(""); + + fmtout("--no-recursion"); + fmtoutp(TAB, "Normally, nvidia-installer will recursively search for " + "potentially conflicting libraries under the default OpenGL " + "and X server installation locations. With this option set, " + "the installer will only search in the top-level directories."); + fmtout(""); fmtout("-K, --kernel-module-only"); fmtoutp(TAB, "Install a kernel module only, and don't uninstall the " @@ -463,6 +488,9 @@ Options *parse_commandline(int argc, char *argv[]) #define NO_RPMS_OPTION 24 #define X_PREFIX_OPTION 25 #define KERNEL_OUTPUT_PATH_OPTION 26 +#define NO_RECURSION_OPTION 27 +#define FORCE_TLS_COMPAT32_OPTION 28 +#define COMPAT32_PREFIX_OPTION 29 static struct option long_options[] = { @@ -486,6 +514,7 @@ Options *parse_commandline(int argc, char *argv[]) { "kernel-module-only", 0, NULL, 'K' }, { "xfree86-prefix", 1, NULL, XFREE86_PREFIX_OPTION }, { "x-prefix", 1, NULL, X_PREFIX_OPTION }, + { "compat32-prefix", 1, NULL, COMPAT32_PREFIX_OPTION }, { "opengl-prefix", 1, NULL, OPENGL_PREFIX_OPTION }, { "installer-prefix", 1, NULL, INSTALLER_PREFIX_OPTION }, { "utility-prefix", 1, NULL, UTILITY_PREFIX_OPTION }, @@ -501,6 +530,7 @@ Options *parse_commandline(int argc, char *argv[]) { "tmpdir", 1, NULL, TMPDIR_OPTION }, { "opengl-headers", 0, NULL, OPENGL_HEADERS_OPTION }, { "force-tls", 1, NULL, FORCE_TLS_OPTION }, + { "force-tls-compat32", 1, NULL, FORCE_TLS_COMPAT32_OPTION }, { "sanity", 0, NULL, SANITY_OPTION }, { "add-this-kernel", 0, NULL, ADD_THIS_KERNEL_OPTION }, { "rpm-file-list", 1, NULL, RPM_FILE_LIST_OPTION }, @@ -508,6 +538,7 @@ Options *parse_commandline(int argc, char *argv[]) { "no-network", 0, NULL, NO_NETWORK_OPTION }, { "no-abi-note", 0, NULL, NO_ABI_NOTE_OPTION }, { "no-rpms", 0, NULL, NO_RPMS_OPTION }, + { "no-recursion", 0, NULL, NO_RECURSION_OPTION }, { "precompiled-kernel-interfaces-path", 1, NULL, PRECOMPILED_KERNEL_INTERFACES_PATH }, { "advanced-options-args-only", 0, NULL, @@ -521,17 +552,18 @@ Options *parse_commandline(int argc, char *argv[]) op->xfree86_prefix = DEFAULT_XFREE86_INSTALLATION_PREFIX; op->opengl_prefix = DEFAULT_OPENGL_INSTALLATION_PREFIX; - op->installer_prefix = NULL; op->utility_prefix = DEFAULT_UTILITY_INSTALLATION_PREFIX; op->proc_mount_point = DEFAULT_PROC_MOUNT_POINT; op->log_file_name = DEFAULT_LOG_FILE_NAME; op->ftp_site = DEFAULT_FTP_SITE; + op->tmpdir = get_tmpdir(op); - op->no_runlevel_check = FALSE; - op->no_backup = FALSE; - op->no_network = FALSE; - op->kernel_module_only = FALSE; - op->no_abi_note = FALSE; + op->distro = get_distribution(op); + +#if defined(NV_X86_64) + if (op->distro == DEBIAN) + op->compat32_prefix = DEBIAN_COMPAT32_INSTALLATION_PREFIX; +#endif op->logging = TRUE; /* log by default */ @@ -579,6 +611,8 @@ Options *parse_commandline(int argc, char *argv[]) op->xfree86_prefix = optarg; break; case OPENGL_PREFIX_OPTION: op->opengl_prefix = optarg; break; + case COMPAT32_PREFIX_OPTION: + op->compat32_prefix = optarg; break; case INSTALLER_PREFIX_OPTION: op->installer_prefix = optarg; break; case UTILITY_PREFIX_OPTION: @@ -618,6 +652,20 @@ Options *parse_commandline(int argc, char *argv[]) exit(1); } break; + case FORCE_TLS_COMPAT32_OPTION: + if (strcasecmp(optarg, "new") == 0) + op->which_tls_compat32 = FORCE_NEW_TLS; + else if (strcasecmp(optarg, "classic") == 0) + op->which_tls_compat32 = FORCE_CLASSIC_TLS; + else { + fmterr(""); + fmterr("Invalid parameter for '--force-tls-compat32'; " + "please run `%s --help` for usage information.", + argv[0]); + fmterr(""); + exit(1); + } + break; case SANITY_OPTION: op->sanity = TRUE; break; @@ -645,6 +693,9 @@ Options *parse_commandline(int argc, char *argv[]) case NO_RPMS_OPTION: op->no_rpms = TRUE; break; + case NO_RECURSION_OPTION: + op->no_recursion = TRUE; + break; default: fmterr(""); @@ -670,8 +721,6 @@ Options *parse_commandline(int argc, char *argv[]) exit(1); } - op->distro = get_distribution(op); - /* * if the installer prefix was not specified, default it to the * utility prefix; this is done so that the installer prefix is @@ -730,6 +779,7 @@ int main(int argc, char *argv[]) */ if (!find_system_utils(op)) goto done; + if (!find_module_utils(op)) goto done; /* get the latest available driver version */ diff --git a/nvidia-installer.h b/nvidia-installer.h index c7cdd87..7914377 100644 --- a/nvidia-installer.h +++ b/nvidia-installer.h @@ -34,21 +34,35 @@ /* * Enumerated type, listing each of the system utilities we'll need. * Keep this enum in sync with the needed_utils string array in - * misc.c:find_system_utils() + * misc.c:find_system_utils(). */ typedef enum { - INSMOD = 0, + LDCONFIG = 0, + LDD, + LD, + OBJCOPY, + GREP, + DMESG, + TAIL, + CUT, + MAX_SYSTEM_UTILS +} SystemUtils; + +/* + * Enumerated type, listing each of the module utilities we'll need. + * Keep this enum in sync with the needed_utils string array in + * misc.c:find_module_utils(). + */ + +typedef enum { + INSMOD = MAX_SYSTEM_UTILS, MODPROBE, RMMOD, LSMOD, DEPMOD, - LDCONFIG, - LD, - OBJCOPY, MAX_UTILS -} Utils; - +} ModuleUtils; /* * Enumerated type of distributions; this isn't an exhaustive list of @@ -59,6 +73,7 @@ typedef enum { typedef enum { SUSE, UNITED_LINUX, + DEBIAN, OTHER } Distribution; @@ -92,6 +107,7 @@ typedef struct __options { int no_questions; int silent; int which_tls; + int which_tls_compat32; int sanity; int add_this_kernel; int no_runlevel_check; @@ -100,9 +116,11 @@ typedef struct __options { int kernel_module_only; int no_abi_note; int no_rpms; + int no_recursion; char *xfree86_prefix; char *opengl_prefix; + char *compat32_prefix; char *installer_prefix; char *utility_prefix; @@ -236,6 +254,8 @@ typedef struct { #define FILE_TYPE_INSTALLER_BINARY 0x00000200 #define FILE_TYPE_UTILITY_BINARY 0x00000400 #define FILE_TYPE_LIBGL_LA 0x00000800 +#define FILE_TYPE_TLS_LIB 0x00001000 +#define FILE_TYPE_TLS_SYMLINK 0x00002000 /* file class: this is used to distinguish OpenGL libraries */ @@ -243,31 +263,52 @@ typedef struct { #define FILE_CLASS_NEW_TLS 0x00010000 #define FILE_CLASS_CLASSIC_TLS 0x00020000 -#define FILE_CLASS_NEW_TLS_32 0x00040000 +#define FILE_CLASS_NATIVE 0x00040000 +#define FILE_CLASS_COMPAT32 0x00080000 #define FILE_TYPE_INSTALLABLE_FILE (FILE_TYPE_OPENGL_LIB | \ FILE_TYPE_XFREE86_LIB | \ + FILE_TYPE_TLS_LIB | \ FILE_TYPE_DOCUMENTATION | \ FILE_TYPE_OPENGL_HEADER | \ FILE_TYPE_KERNEL_MODULE | \ FILE_TYPE_INSTALLER_BINARY | \ - FILE_TYPE_UTILITY_BINARY) + FILE_TYPE_UTILITY_BINARY | \ + FILE_TYPE_LIBGL_LA) #define FILE_TYPE_HAVE_PATH (FILE_TYPE_OPENGL_LIB | \ FILE_TYPE_OPENGL_SYMLINK | \ FILE_TYPE_LIBGL_LA | \ FILE_TYPE_XFREE86_LIB | \ FILE_TYPE_XFREE86_SYMLINK | \ + FILE_TYPE_TLS_LIB | \ + FILE_TYPE_TLS_SYMLINK | \ FILE_TYPE_DOCUMENTATION) -#define FILE_TYPE_SYMLINK (FILE_TYPE_OPENGL_SYMLINK | \ - FILE_TYPE_XFREE86_SYMLINK) +#define FILE_TYPE_HAVE_ARCH (FILE_TYPE_OPENGL_LIB | \ + FILE_TYPE_OPENGL_SYMLINK | \ + FILE_TYPE_LIBGL_LA | \ + FILE_TYPE_TLS_LIB | \ + FILE_TYPE_TLS_SYMLINK) + +#define FILE_TYPE_HAVE_CLASS (FILE_TYPE_TLS_LIB | \ + FILE_TYPE_TLS_SYMLINK) + +#define FILE_TYPE_SYMLINK (FILE_TYPE_OPENGL_SYMLINK | \ + FILE_TYPE_XFREE86_SYMLINK | \ + FILE_TYPE_TLS_SYMLINK) + +#define FILE_TYPE_RTLD_CHECKED (FILE_TYPE_OPENGL_LIB | \ + FILE_TYPE_TLS_LIB) + +#define TLS_LIB_TYPE_FORCED 0x0001 +#define TLS_LIB_NEW_TLS 0x0002 +#define TLS_LIB_CLASSIC_TLS 0x0004 -#define SELECT_TLS 0 -#define FORCE_CLASSIC_TLS 1 -#define FORCE_NEW_TLS 2 +#define FORCE_CLASSIC_TLS (TLS_LIB_CLASSIC_TLS | TLS_LIB_TYPE_FORCED) +#define FORCE_NEW_TLS (TLS_LIB_NEW_TLS | TLS_LIB_TYPE_FORCED) #define PERM_MASK (S_IRWXU|S_IRWXG|S_IRWXO) @@ -277,6 +318,7 @@ typedef struct { #define DEFAULT_OPENGL_INSTALLATION_PREFIX "/usr" #define DEFAULT_INSTALLER_INSTALLATION_PREFIX "/usr" #define DEFAULT_UTILITY_INSTALLATION_PREFIX "/usr" +#define DEBIAN_COMPAT32_INSTALLATION_PREFIX "/emul/ia32-linux" #define DEFAULT_PROC_MOUNT_POINT "/proc" diff --git a/rtld_test.c b/rtld_test.c new file mode 100644 index 0000000..4a7c3ee --- /dev/null +++ b/rtld_test.c @@ -0,0 +1,4 @@ +int main(int argc, char *argv[]) +{ + return 0; +} diff --git a/rtld_test_Linux-x86 b/rtld_test_Linux-x86 new file mode 100755 index 0000000..b60ea47 Binary files /dev/null and b/rtld_test_Linux-x86 differ diff --git a/rtld_test_Linux-x86_64 b/rtld_test_Linux-x86_64 new file mode 100755 index 0000000..be5e874 Binary files /dev/null and b/rtld_test_Linux-x86_64 differ diff --git a/sanity.c b/sanity.c index c5527d3..abd1025 100644 --- a/sanity.c +++ b/sanity.c @@ -162,15 +162,31 @@ static int find_conflicting_libraries(Options *op) /* search for possibly conflicting libraries */ - find_conflicting_xfree86_libraries(DEFAULT_XFREE86_INSTALLATION_PREFIX, l); + find_conflicting_xfree86_libraries(op, DEFAULT_XFREE86_INSTALLATION_PREFIX, l); if (strcmp(DEFAULT_XFREE86_INSTALLATION_PREFIX, op->xfree86_prefix) != 0) - find_conflicting_xfree86_libraries(op->xfree86_prefix, l); + find_conflicting_xfree86_libraries(op, op->xfree86_prefix, l); - find_conflicting_opengl_libraries(DEFAULT_OPENGL_INSTALLATION_PREFIX, l); + find_conflicting_opengl_libraries(op, DEFAULT_OPENGL_INSTALLATION_PREFIX, l); if (strcmp(DEFAULT_OPENGL_INSTALLATION_PREFIX, op->opengl_prefix) != 0) - find_conflicting_opengl_libraries(op->opengl_prefix, l); + find_conflicting_opengl_libraries(op, op->opengl_prefix, l); + +#if defined(NV_X86_64) + if (op->compat32_prefix != NULL) { + char *prefix = nvstrcat(op->compat32_prefix, + DEFAULT_OPENGL_INSTALLATION_PREFIX, NULL); + find_conflicting_opengl_libraries(op, prefix, l); + nvfree(prefix); + + if (strcmp(DEFAULT_OPENGL_INSTALLATION_PREFIX, + op->opengl_prefix) != 0) { + prefix = nvstrcat(op->compat32_prefix, op->opengl_prefix, NULL); + find_conflicting_opengl_libraries(op, prefix, l); + nvfree(prefix); + } + } +#endif /* NV_X86_64 */ /* condense the file list */ diff --git a/user-interface.c b/user-interface.c index 1ef5a1c..4bfed83 100644 --- a/user-interface.c +++ b/user-interface.c @@ -204,14 +204,14 @@ char *ui_get_input(Options *op, const char *def, const char *fmt, ...) va_end(ap); if (op->no_questions) { - ret = nvstrdup(def); - tmp = nvstrcat(msg, " (Answer: '", ret," ')", NULL); + ret = nvstrdup(def ? def : ""); + tmp = nvstrcat(msg, " (Answer: '", ret, "')", NULL); if (!op->silent) { __ui->message(op, NV_MSG_LEVEL_LOG, tmp); } } else { ret = __ui->get_input(op, def, msg); - tmp = nvstrcat(msg, " (Answer: '", ret," ')", NULL); + tmp = nvstrcat(msg, " (Answer: '", ret, "')", NULL); } log_printf(op, TRUE, NV_BULLET_STR, tmp); nvfree(msg); -- cgit v1.2.3