diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
commit | 6d2a0069d419975ba01c2c423b18ef7cd2e76a6f (patch) | |
tree | 130e177fc9fa77d4c7d0788ba07c3b5af42dd84f /install-from-cwd.c |
1.0-61061.0-6106
Diffstat (limited to 'install-from-cwd.c')
-rw-r--r-- | install-from-cwd.c | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/install-from-cwd.c b/install-from-cwd.c new file mode 100644 index 0000000..6b78f60 --- /dev/null +++ b/install-from-cwd.c @@ -0,0 +1,653 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * install_from_cwd.c - + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "nvidia-installer.h" +#include "user-interface.h" +#include "kernel.h" +#include "command-list.h" +#include "backup.h" +#include "files.h" +#include "misc.h" +#include "sanity.h" + +/* local prototypes */ + + +static Package *parse_manifest(Options *op); + + + + +/* + * install_from_cwd() - perform an installation from the current + * working directory; we first ensure that we have a .manifest file in + * the cwd, and the files listed in the manifest exist and have + * correct checksums (to ensure the package hasn't been corrupted, not + * for anything security related). + * + * Second, make sure the user accepts the license. + * + * Then, optionally override the OpenGL and XFree86 installation + * prefixes. + * + * Determine the currently installed NVIDIA driver version (if any). + * + */ + +int install_from_cwd(Options *op) +{ + Package *p; + CommandList *c; + const char *msg; + + static const char edit_your_xf86config[] = + "Please update your XF86Config or xorg.conf file as " + "appropriate; see the file /usr/share/doc/" + "NVIDIA_GLX-1.0/README for details."; + + static const char suse_edit_your_xf86config[] = + "On SuSE Linux/United Linux please use SaX2 now to enable " + "the NVIDIA driver."; + + /* + * validate the manifest file in the cwd, and process it, building + * a Package struct + */ + + if ((p = parse_manifest(op)) == NULL) goto failed; + + ui_set_title(op, "%s (%d.%d-%d)", p->description, + p->major, p->minor, p->patch); + + /* make sure the kernel module is unloaded */ + + if (!check_for_unloaded_kernel_module(op, p)) goto failed; + + /* check that we are not running any X server */ + + if (!check_for_running_x(op)) goto failed; + + /* ask the user to accept the license */ + + if (!get_license_acceptance(op)) return FALSE; + + /* + * determine the current NVIDIA version (if any); ask the user if + * they really want to overwrite the existing installation + */ + + if (!check_for_existing_driver(op, p)) return FALSE; + + /* determine where to install the kernel module */ + + if (!determine_kernel_module_installation_path(op)) goto failed; + + /* + * do nvchooser-style logic to decide if we have a prebuilt kernel + * module for their kernel + * + * XXX One could make the argument that we should not actually do + * the building/linking now, but just add this to the list of + * operations and do it when we execute the operation list. I + * think it's better to make sure we have a kernel module early on + * -- a common problem for users will be not having a prebuilt + * kernel interface for their kernel, and not having the kernel + * headers installed, so it's better to catch that earlier on. + */ + + if (find_precompiled_kernel_interface(op, p)) { + + /* + * we have a prebuild kernel interface, so now link the kernel + * interface with the binary portion of the kernel module. + * + * XXX if linking fails, maybe we should fall through and + * attempt to build the kernel module? No, if linking fails, + * then there is something pretty seriously wrong... better to + * abort. + */ + + if (!link_kernel_module(op, p)) goto failed; + + } else { + + /* XXX we need to do a cc version check */ + + /* + * we do not have a prebuilt kernel interface; thus we'll need + * to compile the kernel interface, so determine where the + * kernel header files are + */ + + if (!determine_kernel_source_path(op)) goto failed; + + /* and now, build the kernel interface */ + + if (!build_kernel_module(op, p)) goto failed; + } + + /* + * if we got this far, we have a complete kernel module; test it + * to be sure it's OK + */ + + if (!test_kernel_module(op, p)) goto failed; + + /* add the kernel module to the list of things to install */ + + if (!add_kernel_module_to_package(op, p)) goto failed; + + /* + * if we are only installing the kernel module, then remove + * everything else from the package; otherwise do some + * OpenGL-specific stuff + */ + + if (op->kernel_module_only) { + remove_non_kernel_module_files_from_package(op, p); + } else { + + /* ask for the XFree86 and OpenGL installation prefixes. */ + + if (!get_prefixes(op)) goto failed; + + /* ask if we should install the OpenGL header files */ + + should_install_opengl_headers(op, p); + + /* + * select the appropriate TLS class, modifying the package as + * necessary. + */ + + select_tls_class(op, p); + + /* + * if the package contains any libGL.la files, process them + * (perform some search and replacing so that they reflect the + * correct installation path, etc) and add them to the + * packagelist to be installed. + */ + + process_libGL_la_files(op, p); + } + + /* + * now that we have the installation prefixes, build the + * destination for each file to be installed + */ + + if (!set_destinations(op, p)) goto failed; + + /* + * uninstall the existing driver; this needs to be done before + * building the command list. + * + * XXX if we uninstall now, then build the command list, and + * then ask the user if they really want to execute the + * command list, if the user decides not to execute the + * command list, they'll be left with no driver installed. + */ + + if (!op->kernel_module_only) { + if (!uninstall_existing_driver(op, FALSE)) goto failed; + } + + /* build a list of operations to execute to do the install */ + + if ((c = build_command_list(op, p)) == NULL) goto failed; + + /* call the ui to get approval for the list of commands */ + + if (!ui_approve_command_list(op, c, p->description)) return FALSE; + + /* initialize the backup log file */ + + if (!op->kernel_module_only) { + if (!init_backup(op, p)) goto failed; + } + + /* execute the command list */ + + if (!do_install(op, p, c)) goto failed; + + /* + * XXX IMPLEMENT ME: generate an XF86Config file? + */ + + /* + * check that everything is installed properly (post-install + * sanity check) + */ + + check_installed_files_from_package(op, p); + + if (!check_sysvipc(op)) goto failed; + + /* done */ + + if ((op->distro == SUSE) || (op->distro == UNITED_LINUX)) { + msg = suse_edit_your_xf86config; + } else { + msg = edit_your_xf86config; + } + + if (op->kernel_module_only) { + ui_message(op, "Installation of the kernel module for the %s " + "(version %s) is now complete.", + p->description, p->version_string); + } else { + ui_message(op, "Installation of the %s (version: %s) is now " + "complete. %s", p->description, p->version_string, msg); + } + + return TRUE; + + failed: + + if (op->logging) { + ui_error(op, "Installation has failed. Please see the file '%s' " + "for details. You may find suggestions on fixing " + "installation problems in the README available on the " + "Linux driver download page at www.nvidia.com.", + op->log_file_name); + } else { + ui_error(op, "Installation has failed. You may find suggestions " + "on fixing installation problems in the README available " + "on the Linux driver download page at www.nvidia.com."); + } + + return FALSE; + +} /* install_from_cwd() */ + + + +/* + * add_this_kernel() - build a precompiled kernel interface for the + * running kernel, and repackage the .run file to include the new + * precompiled kernel interface. + */ + +int add_this_kernel(Options *op) +{ + Package *p; + + /* parse the manifest */ + + if ((p = parse_manifest(op)) == NULL) goto failed; + + /* find the kernel header files */ + + if (!determine_kernel_source_path(op)) goto failed; + + /* build the precompiled kernel interface */ + + if (!build_kernel_interface(op, p)) goto failed; + + /* pack the precompiled kernel interface */ + + if (!pack_precompiled_kernel_interface(op, p)) goto failed; + + return TRUE; + + failed: + + ui_error(op, "Unable to add a precompiled kernel interface for the " + "running kernel."); + + return FALSE; + +} /* add_this_kernel() */ + + + +/* + * parse_manifest() - open and read the .manifest file in the current + * directory. + * + * The first nine lines of the .manifest file are: + * + * - a description string + * - a version string of the form "major.minor-patch" + * - the kernel module file name + * - the kernel interface file name + * - the kernel module name (what `rmmod` and `modprobe` should use) + * - a whitespace-separated list of module names that should be + * removed before installing a new kernel module + * - a whitespace-separated list of kernel module filenames that + * should be uninstalled before installing a new kernel module + * - kernel module build directory + * - directory containing precompiled kernel interfaces + * + * The rest of the manifest file is file entries. A file entry is a + * whitespace-separated list containing: + * + * - a filename (relative to the cwd) + * - an octal value describing the permissions + * - a flag describing the file type + * - certain file types will have a path + * - symbolic links will name the target of the link + */ + +static Package *parse_manifest (Options *op) +{ + char *buf, *c, *flag , *tmpstr; + int done, n, line; + int fd, len = 0; + struct stat stat_buf; + Package *p; + char *manifest = NULL, *ptr; + + p = (Package *) nvalloc(sizeof (Package)); + + /* open the manifest file */ + + if ((fd = open(".manifest", O_RDONLY)) == -1) { + ui_error(op, "No package found for installation. Please run " + "this utility with the '--help' option for usage " + "information."); + goto fail; + } + + if (fstat(fd, &stat_buf) == -1) goto cannot_open; + + len = stat_buf.st_size; + + manifest = mmap(0, len, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); + if (manifest == (void *) -1) goto cannot_open; + + /* the first line is the description */ + + line = 1; + p->description = get_next_line(manifest, &ptr); + if (!p->description) goto invalid_manifest_file; + + /* the second line is the version */ + + line++; + p->version_string = get_next_line(ptr, &ptr); + if (!p->version_string) goto invalid_manifest_file; + 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 */ + + 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 */ + + 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 + * to be unloaded before installing the new kernel module + */ + + line++; + tmpstr = get_next_line(ptr, &ptr); + if (!tmpstr) goto invalid_manifest_file; + + p->bad_modules = NULL; + c = tmpstr; + n = 0; + + do { + n++; + p->bad_modules = (char **) + nvrealloc(p->bad_modules, n * sizeof(char *)); + p->bad_modules[n-1] = read_next_word(c, &c); + } while (p->bad_modules[n-1]); + + /* + * the seventh line is a whitespace-separated list of kernel module + * filenames to be uninstalled before installing the new kernel + * module + */ + + line++; + tmpstr = get_next_line(ptr, &ptr); + if (!tmpstr) goto invalid_manifest_file; + + p->bad_module_filenames = NULL; + c = tmpstr; + n = 0; + + do { + n++; + p->bad_module_filenames = (char **) + nvrealloc(p->bad_module_filenames, n * sizeof(char *)); + 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 */ + + line++; + p->kernel_module_build_directory = get_next_line(ptr, &ptr); + if (!p->kernel_module_build_directory) goto invalid_manifest_file; + remove_trailing_slashes(p->kernel_module_build_directory); + + /* + * the nineth line is the directory containing precompiled kernel + * interfaces + */ + + line++; + p->precompiled_kernel_interface_directory = get_next_line(ptr, &ptr); + if (!p->precompiled_kernel_interface_directory) + goto invalid_manifest_file; + remove_trailing_slashes(p->precompiled_kernel_interface_directory); + + /* the rest of the file is file entries */ + + done = FALSE; + line++; + + do { + buf = get_next_line(ptr, &ptr); + if ((!buf) || (buf[0] == '\0')) { + done = TRUE; + } else { + + p->num_entries++; + n = p->num_entries - 1; + + /* extend the PackageEntry array */ + + if ((p->entries = (PackageEntry *) nvrealloc + (p->entries, sizeof(PackageEntry) * + p->num_entries)) == NULL) { + ui_error(op, "Memory allocation failure."); + goto fail; + } + + /* read the file name and permissions */ + + c = buf; + + p->entries[n].file = read_next_word(buf, &c); + tmpstr = read_next_word(c, &c); + + /* if any of them were NULL, fail */ + + if (!p->entries[n].file || !tmpstr) goto invalid_manifest_file; + + /* translate the mode string into an octal mode */ + + if (!mode_string_to_mode(op, tmpstr, &p->entries[n].mode)) { + goto invalid_manifest_file; + } + free(tmpstr); + + /* now, parse the flags */ + + p->entries[n].flags = 0x0; + + flag = read_next_word(c, &c); + if (!flag) goto invalid_manifest_file; + + if (strcmp(flag, "KERNEL_MODULE_SRC") == 0) + p->entries[n].flags |= FILE_TYPE_KERNEL_MODULE_SRC; + else if (strcmp(flag, "KERNEL_MODULE_CMD") == 0) + p->entries[n].flags |= FILE_TYPE_KERNEL_MODULE_CMD; + else if (strcmp(flag, "OPENGL_HEADER") == 0) + p->entries[n].flags |= FILE_TYPE_OPENGL_HEADER; + else if (strcmp(flag, "OPENGL_LIB") == 0) + p->entries[n].flags |= FILE_TYPE_OPENGL_LIB; + else if (strcmp(flag, "LIBGL_LA") == 0) + 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, "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, "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 + goto invalid_manifest_file; + + free(flag); + + /* libs and documentation have a path field */ + + if (p->entries[n].flags & FILE_TYPE_HAVE_PATH) { + p->entries[n].path = read_next_word(c, &c); + if (!p->entries[n].path) goto invalid_manifest_file; + } else { + p->entries[n].path = NULL; + } + + /* symlinks have a target */ + + if (p->entries[n].flags & FILE_TYPE_SYMLINK) { + p->entries[n].target = read_next_word(c, &c); + if (!p->entries[n].target) goto invalid_manifest_file; + } else { + p->entries[n].target = NULL; + } + + /* + * as a convenience for later, set the 'name' pointer to + * the basename contained in 'file' (ie the portion of + * 'file' without any leading directory components + */ + + p->entries[n].name = strrchr(p->entries[n].file, '/'); + if (p->entries[n].name) p->entries[n].name++; + + if (!p->entries[n].name) p->entries[n].name = p->entries[n].file; + + /* free the line */ + + free(buf); + } + + line++; + + } while (!done); + + if (manifest) munmap(manifest, len); + if (fd != -1) close(fd); + + return p; + + cannot_open: + ui_error(op, "Failure opening package's .manifest file (%s).", + strerror(errno)); + goto fail; + + invalid_manifest_file: + + ui_error(op, "Invalid .manifest file; error on line %d.", line); + goto fail; + + fail: + if (p && p->entries) free(p->entries); + if (p) free(p); + if (manifest) munmap(manifest, len); + if (fd != -1) close(fd); + return NULL; + +} /* parse_manifest() */ |