Files
nvidia-installer/misc.c
Aaron Plattner 6765fcac47 346.16
2014-11-13 08:51:28 -08:00

3172 lines
96 KiB
C

/*
* nvidia-installer: A tool for installing NVIDIA software packages on
* Unix and Linux systems.
*
* Copyright (C) 2003 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
*
* misc.c - this source file contains miscellaneous routines for use
* by the nvidia-installer.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/mman.h>
#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
#endif
#include "nvidia-installer.h"
#include "user-interface.h"
#include "kernel.h"
#include "files.h"
#include "misc.h"
#include "crc.h"
#include "nvLegacy.h"
#include "manifest.h"
static int check_symlink(Options*, const char*, const char*, const char*);
/*
* read_next_word() - given a string buf, skip any whitespace, and
* then copy the next set of characters until more white space is
* encountered. A new string containing this next word is returned.
* The passed-by-reference parameter e, if not NULL, is set to point
* at the where the end of the word was, to facilitate multiple calls
* of read_next_word().
*/
char *read_next_word (char *buf, char **e)
{
char *c = buf;
char *start, *ret;
int len;
while ((*c) && (isspace (*c)) && (*c != '\n')) c++;
start = c;
while ((*c) && (!isspace (*c)) && (*c != '\n')) c++;
len = c - start;
if (len == 0) return NULL;
ret = (char *) nvalloc (len + 1);
strncpy (ret, start, len);
ret[len] = '\0';
if (e) *e = c;
return ret;
} /* read_next_word() */
/*
* check_euid() - this function checks that the effective uid of this
* application is root, and calls the ui to print an error if it's not
* root.
*/
int check_euid(Options *op)
{
uid_t euid;
euid = geteuid();
if (euid != 0) {
ui_error(op, "nvidia-installer must be run as root");
return FALSE;
}
return TRUE;
} /* check_euid() */
/*
* adjust_cwd() - this function scans through program_name (ie
* argv[0]) for any possible relative paths, and chdirs into the
* relative path it finds. The point of all this is to make the
* directory with the executed binary the cwd.
*
* It is assumed that the user interface has not yet been initialized
* by the time this function is called.
*/
int adjust_cwd(Options *op, const char *program_name)
{
char *c;
int success = TRUE;
/*
* extract any pathname portion out of the program_name and chdir
* to it
*/
c = strrchr(program_name, '/');
if (c) {
int len;
char *path;
len = c - program_name + 1;
path = (char *) nvalloc(len + 1);
strncpy(path, program_name, len);
path[len] = '\0';
if (op->expert) log_printf(op, NULL, "chdir(\"%s\")", path);
if (chdir(path)) {
fprintf(stderr, "Unable to chdir to %s (%s)",
path, strerror(errno));
success = FALSE;
}
free(path);
}
return success;
}
/*
* get_next_line() - this function scans for the next newline,
* carriage return, NUL terminator, or EOF in buf. If non-NULL, the
* passed-by-reference parameter 'end' is set to point to the next
* printable character in the buffer, or NULL if EOF is encountered.
*
* If the parameter 'start' is non-NULL, then that is interpretted as
* the start of the buffer string, and we check that we never walk
* 'length' bytes past 'start'.
*
* On success, a newly allocated buffer is allocated containing the
* next line of text (with a NULL terminator in place of the
* newline/carriage return).
*
* On error, NULL is returned.
*/
char *get_next_line(char *buf, char **end, char *start, int length)
{
char *c, *retbuf;
int len;
if (start && (length < 1)) return NULL;
#define __AT_END(_start, _current, _length) \
((_start) && (((_current) - (_start)) >= (_length)))
if (end) *end = NULL;
// Cast all char comparisons to EOF to signed char in order to
// allow proper sign extension on platforms like GCC ARM where
// char is unsigned char
if ((!buf) ||
__AT_END(start, buf, length) ||
(*buf == '\0') ||
(((signed char)*buf) == EOF)) return NULL;
c = buf;
while ((!__AT_END(start, c, length)) &&
(*c != '\0') &&
(((signed char)*c) != EOF) &&
(*c != '\n') &&
(*c != '\r')) c++;
len = c - buf;
retbuf = nvalloc(len + 1);
strncpy(retbuf, buf, len);
retbuf[len] = '\0';
if (end) {
while ((!__AT_END(start, c, length)) &&
(*c != '\0') &&
(((signed char)*c) != EOF) &&
(!isprint(*c))) c++;
if (__AT_END(start, c, length) ||
(*c == '\0') ||
(((signed char)*c) == EOF)) *end = NULL;
else *end = c;
}
return retbuf;
} /* get_next_line() */
/*
* run_command() - this function runs the given command and assigns
* the data parameter to a malloced buffer containing the command's
* output, if any. The caller of this function should free the data
* string. The return value of the command is returned from this
* function.
*
* The output parameter controls whether command output is sent to the
* ui; if this is TRUE, then everyline of output that is read is sent
* to the ui.
*
* If the status parameter is greater than 0, it is interpretted as a
* rough estimate of how many lines of output will be generated by the
* command. This is used to compute the value that should be passed
* to ui_status_update() for every line of output that is received.
*
* The redirect argument tells run_command() to redirect stderr to
* stdout so that all output is collected, or just stdout.
*
* XXX maybe we should do something to cap the time we allow the
* command to run?
*/
int run_command(Options *op, const char *cmd, char **data, int output,
int status, int redirect)
{
int n, len, buflen, ret;
char *cmd2, *buf, *tmpbuf;
FILE *stream = NULL;
struct sigaction act, old_act;
float percent;
if (data) *data = NULL;
/*
* if command output is requested, print the command that we will
* execute
*/
if (output) ui_command_output (op, "executing: '%s'...", cmd);
/* redirect stderr to stdout */
if (redirect) {
cmd2 = nvstrcat(cmd, " 2>&1", NULL);
} else {
cmd2 = nvstrdup(cmd);
}
/*
* XXX: temporarily ignore SIGWINCH; our child process inherits
* this disposition and will likewise ignore it (by default).
* This fixes cases where child processes abort after receiving
* SIGWINCH when its caught in the parent process.
*/
if (op->sigwinch_workaround) {
act.sa_handler = SIG_IGN;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(SIGWINCH, &act, &old_act) < 0)
old_act.sa_handler = NULL;
}
/*
* Open a process by creating a pipe, forking, and invoking the
* command.
*/
stream = popen(cmd2, "r");
nvfree(cmd2);
if (stream == NULL) {
ui_error(op, "Failure executing command '%s' (%s).",
cmd, strerror(errno));
return errno;
}
/*
* read from the stream, filling and growing buf, until we hit
* EOF. Send each line to the ui as it is read.
*/
len = 0; /* length of what has actually been read */
buflen = 0; /* length of destination buffer */
buf = NULL;
n = 0; /* output line counter */
while (1) {
if ((buflen - len) < NV_MIN_LINE_LEN) {
buflen += NV_LINE_LEN;
tmpbuf = (char *) nvalloc(buflen);
if (buf) {
memcpy(tmpbuf, buf, len);
free(buf);
}
buf = tmpbuf;
}
if (fgets(buf + len, buflen - len, stream) == NULL) break;
if (output) ui_command_output(op, "%s", buf + len);
len += strlen(buf + len);
if (status) {
n++;
if (n > status) n = status;
percent = (float) n / (float) status;
/*
* XXX: manually call the SIGWINCH handler, if set, to
* handle window resizes while we ignore the signal.
*/
if (op->sigwinch_workaround)
if (old_act.sa_handler) old_act.sa_handler(SIGWINCH);
ui_status_update(op, percent, NULL);
}
} /* while (1) */
/* Close the popen()'ed stream. */
ret = pclose(stream);
/*
* Restore the SIGWINCH signal disposition and handler, if any,
* to their original values.
*/
if (op->sigwinch_workaround)
sigaction(SIGWINCH, &old_act, NULL);
/* if the last character in the buffer is a newline, null it */
if ((len > 0) && (buf[len-1] == '\n')) buf[len-1] = '\0';
if (data) *data = buf;
else free(buf);
return ret;
} /* run_command() */
/*
* read_text_file() - open a text file, read its contents and return
* them to the caller in a newly allocated buffer. Returns TRUE on
* success and FALSE on failure.
*/
int read_text_file(const char *filename, char **buf)
{
FILE *fp;
int index = 0, buflen = 0;
int eof = FALSE;
char *line, *tmpbuf;
*buf = NULL;
fp = fopen(filename, "r");
if (!fp)
return FALSE;
while (((line = fget_next_line(fp, &eof)) != NULL)) {
if ((index + strlen(line) + 2) > buflen) {
buflen = 2 * (index + strlen(line) + 2);
tmpbuf = (char *)nvalloc(buflen);
if (!tmpbuf) {
if (*buf) nvfree(*buf);
fclose(fp);
return FALSE;
}
if (*buf) {
memcpy(tmpbuf, *buf, index);
nvfree(*buf);
}
*buf = tmpbuf;
}
index += sprintf(*buf + index, "%s\n", line);
nvfree(line);
if (eof) {
break;
}
}
fclose(fp);
return TRUE;
} /* read_text_file() */
/*
* find_system_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 failure.
*/
#define EXTRA_PATH "/bin:/usr/bin:/sbin:/usr/sbin:/usr/X11R6/bin:/usr/bin/X11"
/*
* Utils list; keep in sync with SystemUtils, SystemOptionalUtils, ModuleUtils
* and DevelopUtils enum types
*/
typedef struct {
const char *util;
const char *package;
} Util;
static const Util __utils[] = {
/* SystemUtils */
[LDCONFIG] = { "ldconfig", "glibc" },
[LDD] = { "ldd", "glibc" },
[GREP] = { "grep", "grep" },
[DMESG] = { "dmesg", "util-linux" },
[TAIL] = { "tail", "coreutils" },
[CUT] = { "cut", "coreutils" },
[TR] = { "tr", "coreutils" },
[SED] = { "sed", "sed" },
/* SystemOptionalUtils */
[OBJCOPY] = { "objcopy", "binutils" },
[CHCON] = { "chcon", "selinux" },
[SELINUX_ENABLED] = { "selinuxenabled", "selinux" },
[GETENFORCE] = { "getenforce", "selinux" },
[EXECSTACK] = { "execstack", "selinux" },
[PKG_CONFIG] = { "pkg-config", "pkg-config" },
[XSERVER] = { "X", "xserver" },
[OPENSSL] = { "openssl", "openssl" },
[DKMS] = { "dkms", "dkms" },
/* ModuleUtils */
[INSMOD] = { "insmod", "module-init-tools' or 'kmod" },
[MODPROBE] = { "modprobe", "module-init-tools' or 'kmod" },
[RMMOD] = { "rmmod", "module-init-tools' or 'kmod" },
[LSMOD] = { "lsmod", "module-init-tools' or 'kmod" },
[DEPMOD] = { "depmod", "module-init-tools' or 'kmod" },
/* DevelopUtils */
[CC] = { "cc", "gcc" },
[MAKE] = { "make", "make" },
[LD] = { "ld", "binutils" },
};
int find_system_utils(Options *op)
{
int i;
ui_expert(op, "Searching for system utilities:");
/* search the PATH for each utility */
for (i = MIN_SYSTEM_UTILS; i < MAX_SYSTEM_UTILS; i++) {
op->utils[i] = find_system_util(__utils[i].util);
if (!op->utils[i]) {
ui_error(op, "Unable to find the system 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.",
__utils[i].util, __utils[i].package,
__utils[i].package, __utils[i].util);
return FALSE;
}
ui_expert(op, "found `%s` : `%s`", __utils[i].util, op->utils[i]);
}
for (i = MIN_SYSTEM_OPTIONAL_UTILS; i < MAX_SYSTEM_OPTIONAL_UTILS; i++) {
op->utils[i] = find_system_util(__utils[i].util);
if (op->utils[i]) {
ui_expert(op, "found `%s` : `%s`", __utils[i].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() */
/*
* 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;
ui_expert(op, "Searching for module utilities:");
/* search the PATH for each utility */
for (i = MIN_MODULE_UTILS; i < MAX_MODULE_UTILS; i++) {
op->utils[i] = find_system_util(__utils[i].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.",
__utils[i].util, __utils[i].package,
__utils[i].package, __utils[i].util);
return FALSE;
}
ui_expert(op, "found `%s` : `%s`", __utils[i].util, op->utils[i]);
};
return TRUE;
} /* find_module_utils() */
/*
* check_proc_modprobe_path() - check if the modprobe path reported
* via /proc matches the one determined earlier; also check if it can
* be accessed/executed.
*/
#define PROC_MODPROBE_PATH_FILE "/proc/sys/kernel/modprobe"
#define DEFAULT_MODPROBE "/sbin/modprobe"
int check_proc_modprobe_path(Options *op)
{
FILE *fp;
char *proc_modprobe = NULL, *found_modprobe;
struct stat st;
int ret, success = FALSE;
found_modprobe = op->utils[MODPROBE];
fp = fopen(PROC_MODPROBE_PATH_FILE, "r");
if (fp) {
proc_modprobe = fget_next_line(fp, NULL);
fclose(fp);
}
/* If the modprobe found by find_system_utils() is a symlink, resolve it */
ret = lstat(found_modprobe, &st);
if (ret == 0 && S_ISLNK(st.st_mode)) {
char *target = get_resolved_symlink_target(op, found_modprobe);
if (target && access(target, F_OK | X_OK) == 0) {
found_modprobe = target;
} else {
nvfree(target);
}
}
if (proc_modprobe) {
/* If the modprobe reported by the kernel is a symlink, resolve it */
ret = lstat(proc_modprobe, &st);
if (ret == 0 && S_ISLNK(st.st_mode)) {
char *target = get_resolved_symlink_target(op, proc_modprobe);
if (target && access(target, F_OK | X_OK) == 0) {
nvfree(proc_modprobe);
proc_modprobe = target;
} else {
nvfree(target);
}
}
/* Check to see if the modprobe reported by the kernel and the
* modprobe found by nvidia-installer match. */
if (strcmp(proc_modprobe, found_modprobe) == 0) {
success = TRUE;
} else {
if (access(proc_modprobe, F_OK | X_OK) == 0) {
ui_warn(op, "The path to the `modprobe` utility reported by "
"'%s', `%s`, differs from the path determined by "
"`nvidia-installer`, `%s`. Please verify that `%s` "
"works correctly and correct the path in '%s' if "
"it does not.",
PROC_MODPROBE_PATH_FILE, proc_modprobe, found_modprobe,
proc_modprobe, PROC_MODPROBE_PATH_FILE);
success = TRUE;
} else {
ui_error(op, "The path to the `modprobe` utility reported by "
"'%s', `%s`, differs from the path determined by "
"`nvidia-installer`, `%s`, and does not appear to "
"point to a valid `modprobe` binary. Please correct "
"the path in '%s'.",
PROC_MODPROBE_PATH_FILE, proc_modprobe, found_modprobe,
PROC_MODPROBE_PATH_FILE);
}
}
} else {
/* We failed to read from /proc/sys/kernel/modprobe, possibly because
* it doesn't exist or /proc isn't mounted. Assume a default modprobe
* path of /sbin/modprobe. */
char * found_mismatch;
if (strcmp(DEFAULT_MODPROBE, found_modprobe) == 0) {
found_mismatch = nvstrdup("");
} else {
found_mismatch = nvstrcat("This path differs from the one "
"determined by `nvidia-installer`, ",
found_modprobe, ". ", NULL);
}
if (access(DEFAULT_MODPROBE, F_OK | X_OK) == 0) {
ui_warn(op, "The file '%s' is unavailable; the X server will "
"use `" DEFAULT_MODPROBE "` as the path to the `modprobe` "
"utility. %sPlease verify that `" DEFAULT_MODPROBE
"` works correctly or mount the /proc file system and "
"verify that '%s' reports the correct path.",
PROC_MODPROBE_PATH_FILE, found_mismatch,
PROC_MODPROBE_PATH_FILE);
success = TRUE;
} else {
ui_error(op, "The file '%s' is unavailable; the X server will "
"use `" DEFAULT_MODPROBE "` as the path to the `modprobe` "
"utility. %s`" DEFAULT_MODPROBE "` does not appear to "
"point to a valid `modprobe` binary. Please create a "
"symbolic link from `" DEFAULT_MODPROBE "` to `%s` or "
"mount the /proc file system and verify that '%s' reports "
"the correct path.",
PROC_MODPROBE_PATH_FILE, found_mismatch,
found_modprobe, PROC_MODPROBE_PATH_FILE);
}
nvfree(found_mismatch);
}
nvfree(proc_modprobe);
if (found_modprobe != op->utils[MODPROBE]) {
nvfree(found_modprobe);
}
return success;
} /* check_proc_modprobe_path() */
/*
* check_development_tools() - check if the development tools needed
* to build custom kernel interfaces are available.
*/
static int check_development_tool(Options *op, int idx)
{
if (!op->utils[idx]) {
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.",
__utils[idx].util, __utils[idx].package,
__utils[idx].package, __utils[idx].util);
return FALSE;
}
ui_expert(op, "found `%s` : `%s`", __utils[idx].util, op->utils[idx]);
return TRUE;
}
int check_development_tools(Options *op, Package *p)
{
int i, ret;
char *cmd, *result;
op->utils[CC] = getenv("CC");
ui_expert(op, "Checking development tools:");
/*
* Check if the required toolchain components are installed on
* the system. Note that we skip the check for `cc` if the
* user specified the CC environment variable; we do this because
* `cc` may not be present in the path, nor the compiler named
* in $CC, but the installation may still succeed. $CC is sanity
* checked below.
*/
for (i = (op->utils[CC] != NULL) ? MIN_DEVELOP_UTILS + 1 : MIN_DEVELOP_UTILS;
i < MAX_DEVELOP_UTILS; i++) {
op->utils[i] = find_system_util(__utils[i].util);
if (!check_development_tool(op, i)) {
return FALSE;
}
}
/*
* Check if the libc development headers are installed; we need
* these to build the CC version check utility.
*/
if (access("/usr/include/stdio.h", F_OK) == -1) {
ui_error(op, "You do not appear to have libc header files "
"installed on your system. Please install your "
"distribution's libc development package.");
return FALSE;
}
if (!op->utils[CC]) op->utils[CC] = "cc";
ui_log(op, "Performing CC sanity check with CC=\"%s\".", op->utils[CC]);
cmd = nvstrcat("sh ", p->kernel_module_build_directory,
"/conftest.sh ", op->utils[CC], " ", op->utils[CC], " ",
"DUMMY_SOURCE DUMMY_OUTPUT ",
"cc_sanity_check just_msg", NULL);
ret = run_command(op, cmd, &result, FALSE, 0, TRUE);
nvfree(cmd);
if (ret == 0) return TRUE;
ui_error(op, "The CC sanity check failed:\n\n%s\n", result);
nvfree(result);
return FALSE;
} /* check_development_tools() */
/*
* check_precompiled_kernel_interface_tools() - check if the development tools
* needed to link precompiled kernel interfaces are available.
*/
int check_precompiled_kernel_interface_tools(Options *op)
{
/*
* If precompiled info has been found we only need to check for
* a linker
*/
op->utils[LD] = find_system_util(__utils[LD].util);
return check_development_tool(op, LD);
} /* check_precompiled_kernel_interface_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.
*/
char *find_system_util(const char *util)
{
char *buf, *path, *file, *x, *y, c;
/* build the search path */
buf = getenv("PATH");
if (buf) {
path = nvstrcat(buf, ":", EXTRA_PATH, NULL);
} else {
path = nvstrdup(EXTRA_PATH);
}
/* search the PATH for the utility */
for (x = y = path; ; x++) {
if (*x == ':' || *x == '\0') {
c = *x;
*x = '\0';
file = nvstrcat(y, "/", util, NULL);
*x = c;
if ((access(file, F_OK | X_OK)) == 0) {
nvfree(path);
return file;
}
nvfree(file);
y = x + 1;
if (*x == '\0') break;
}
}
nvfree(path);
return NULL;
} /* find_system_util() */
/*
* continue_after_error() - tell the user that an error has occured,
* and ask them if they would like to continue.
*
* Returns TRUE if the installer should continue.
*/
int continue_after_error(Options *op, const char *fmt, ...)
{
char *msg;
int ret;
NV_VSNPRINTF(msg, fmt);
ret = (ui_multiple_choice(op, CONTINUE_ABORT_CHOICES,
NUM_CONTINUE_ABORT_CHOICES,
CONTINUE_CHOICE, /* Default choice */
"The installer has encountered the following "
"error during installation: '%s'. Would you "
"like to continue installation anyway?",
msg) == CONTINUE_CHOICE);
nvfree(msg);
return ret;
} /* continue_after_error() */
/*
* do_install()
*/
int do_install(Options *op, Package *p, CommandList *c)
{
char *msg;
int len, ret;
len = strlen(p->description) + strlen(p->version) + 64;
msg = (char *) nvalloc(len);
snprintf(msg, len, "Installing '%s' (%s):",
p->description, p->version);
ret = execute_command_list(op, c, msg, "Installing");
free(msg);
if (!ret) return FALSE;
ui_log(op, "Driver file installation is complete.");
return TRUE;
} /* do_install() */
/*
* extract_version_string() - extract the NVIDIA driver version string
* from the given string. On failure, return NULL; on success, return
* a malloced string containing just the version string.
*
* The version string can have one of two forms: either the old
* "X.Y.ZZZZ" format (e.g., "1.0-9742"), or the new format where it is
* just a collection of period-separated numbers (e.g., "105.17.2").
* The length and number of periods in the newer format is arbitrary.
*
* Furthermore, we expect the new version format to be enclosed either
* in parenthesis or whitespace (or be at the start or end of the
* input string) and be atleast 5 characters long. This allows us to
* distinguish the version string from other numbers such as the year
* or the old version format in input strings like this:
*
* "NVIDIA UNIX x86 Kernel Module 105.17.2 Fri Dec 15 09:54:45 PST 2006"
* "1.0-105917 (105.9.17)"
*/
char *extract_version_string(const char *str)
{
char c, *copiedString, *start, *end, *x, *version = NULL;
int state;
if (!str) return NULL;
copiedString = strdup(str);
x = copiedString;
/*
* look for a block of only numbers and periods; the version
* string must be surrounded by either whitespace, or the
* start/end of the string; we use a small state machine to parse
* the string
*/
start = NULL;
end = NULL;
#define STATE_IN_VERSION 0
#define STATE_NOT_IN_VERSION 1
#define STATE_LOOKING_FOR_VERSION 2
#define STATE_FOUND_VERSION 3
state = STATE_LOOKING_FOR_VERSION;
while (*x) {
c = *x;
switch (state) {
/*
* if we are LOOKING_FOR_VERSION, then finding a digit
* will put us inside the version, whitespace (or open
* parenthesis) will allow us to continue to look for the
* version, and any other character will cause us to stop
* looking for the version string
*/
case STATE_LOOKING_FOR_VERSION:
if (isdigit(c)) {
start = x;
state = STATE_IN_VERSION;
} else if (isspace(c) || (c == '(')) {
state = STATE_LOOKING_FOR_VERSION;
} else {
state = STATE_NOT_IN_VERSION;
}
break;
/*
* if we are IN_VERSION, then a digit or period will keep
* us in the version, space (or close parenthesis) and
* more than 4 characters of version means we found the
* entire version string. If we find any other character,
* then what we thought was the version string wasn't, so
* move to NOT_IN_VERSION.
*/
case STATE_IN_VERSION:
if (isdigit(c) || (c == '.')) {
state = STATE_IN_VERSION;
} else if ((isspace(c) || (c == ')')) && ((x - start) >= 5)) {
end = x;
state = STATE_FOUND_VERSION;
goto exit_while_loop;
} else {
state = STATE_NOT_IN_VERSION;
}
break;
/*
* if we are NOT_IN_VERSION, then space or open
* parenthesis will make us start looking for the version,
* and any other character just leaves us in the
* NOT_IN_VERSION state
*/
case STATE_NOT_IN_VERSION:
if (isspace(c) || (c == '(')) {
state = STATE_LOOKING_FOR_VERSION;
} else {
state = STATE_NOT_IN_VERSION;
}
break;
}
x++;
}
/*
* the NULL terminator that broke us out of the while loop could
* be the end of the version string
*/
if ((state == STATE_IN_VERSION) && ((x - start) >= 5)) {
end = x;
state = STATE_FOUND_VERSION;
}
exit_while_loop:
/* if we found a version string above, copy it */
if (state == STATE_FOUND_VERSION) {
*end = '\0';
version = strdup(start);
goto done;
}
/*
* we did not find a version string with the new format; look for
* a version of the old X.Y-ZZZZ format
*/
x = copiedString;
while (*x) {
if (((x[0]) && isdigit(x[0])) &&
((x[1]) && (x[1] == '.')) &&
((x[2]) && isdigit(x[2])) &&
((x[3]) && (x[3] == '-')) &&
((x[4]) && isdigit(x[4])) &&
((x[5]) && isdigit(x[5])) &&
((x[6]) && isdigit(x[6])) &&
((x[7]) && isdigit(x[7]))) {
x[8] = '\0';
version = strdup(x);
goto done;
}
x++;
}
done:
free(copiedString);
return version;
} /* extract_version_string() */
/*
* should_install_opengl_headers() - if in expert mode, ask the user
* if they want to install OpenGL header files.
*/
void should_install_opengl_headers(Options *op, Package *p)
{
int i, have_headers = FALSE;
if (!op->expert) return;
/*
* first, scan through the package to see if we have any header
* files to install
*/
for (i = 0; i < p->num_entries; i++) {
if (p->entries[i].type == FILE_TYPE_OPENGL_HEADER) {
have_headers = TRUE;
break;
}
}
if (!have_headers) return;
/*
* If we're to provide more verbose descriptions, we could present
* something like this:
*
* ("The %s provides OpenGL header files; these are used when
* compiling OpenGL applications. Most Linux distributions
* already have OpenGL header files installed (normally in the
* /usr/include/GL/ directory). If you don't have OpenGL header
* files installed and would like to, or if you want to develop
* OpenGL applications that take advantage of NVIDIA OpenGL
* extensions, then you can install NVIDIA's OpenGL header files
* at this time.", p->description);
*/
op->opengl_headers = ui_yes_no(op, op->opengl_headers,
"Install NVIDIA's OpenGL header files?");
ui_expert(op, "Installation %s install the OpenGL header files.",
op->opengl_headers ? "will" : "will not");
} /* 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)
/* If there are no compat32 files, there is nothing to do */
if (!op->compat32_files_packaged) {
op->install_compat32_libs = NV_OPTIONAL_BOOL_FALSE;
return;
}
/* Determine where the compatibility libraries should be installed */
get_compat32_path(op);
/*
* If the user hasn't explicitly specified whether to install compat32
* files, ask if the 32-bit compatibility libraries are to be installed.
* If yes, check if the chosen prefix exists. If not, notify the user and
* ask him/her if the files are to be installed anyway.
*/
if (op->install_compat32_libs == NV_OPTIONAL_BOOL_DEFAULT) {
int ret;
ret = ui_yes_no(op, TRUE,
"Install NVIDIA's 32-bit compatibility libraries?");
op->install_compat32_libs = ret ? NV_OPTIONAL_BOOL_TRUE :
NV_OPTIONAL_BOOL_FALSE;
}
if (op->install_compat32_libs == NV_OPTIONAL_BOOL_FALSE) {
int i;
for (i = 0; i < p->num_entries; i++) {
if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) {
/* invalidate file */
invalidate_package_entry(&(p->entries[i]));
}
}
}
#endif /* NV_X86_64 */
}
/*
* 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)
{
int i, vdpau_packaged = FALSE;
/*
* 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;
}
}
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) {
vdpau_packaged = TRUE;
if (op->install_vdpau_wrapper != NV_OPTIONAL_BOOL_TRUE) {
invalidate_package_entry(&(p->entries[i]));
}
}
}
if (!vdpau_packaged) {
return;
}
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 {
ui_log(op, "Skipping installation of the libvdpau wrapper library.");
}
}
/*
* should_install_uvm() - ask the user if he/she wishes to install UVM
*/
void should_install_uvm(Options *op, Package *p)
{
/* if the package does not include UVM, it can't be installed. */
if (!op->uvm_files_packaged) {
op->install_uvm = FALSE;
return;
}
/* ask expert users whether they want to install UVM */
if (op->expert) {
op->install_uvm = ui_yes_no(op, op->install_uvm, "Would you like to "
"install the NVIDIA Unified Memory kernel "
"module? You must install this module in "
"order to use CUDA.");
}
if (!op->install_uvm) {
ui_warn(op, "The NVIDIA Unified Memory kernel module will not be "
"installed. As a result, CUDA applications will not be able to "
"run with this installation of the NVIDIA driver.");
}
}
/*
* check_installed_files_from_package() - scan through the entries in
* the package, making sure that all symbolic links and files are
* properly installed.
*/
void check_installed_files_from_package(Options *op, Package *p)
{
int i, ret = TRUE;
float percent;
PackageEntryFileTypeList installable_files;
ui_status_begin(op, "Running post-install sanity check:", "Checking");
get_installable_file_type_list(op, &installable_files);
for (i = 0; i < p->num_entries; i++) {
percent = (float) i / (float) p->num_entries;
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
* installed them. */
p->entries[i].type != FILE_TYPE_XMODULE_NEWSYM) {
if (!check_symlink(op, p->entries[i].target,
p->entries[i].dst,
p->description)) {
ret = FALSE;
}
} else if (installable_files.types[p->entries[i].type]) {
if (!check_installed_file(op, p->entries[i].dst,
p->entries[i].mode, 0, ui_warn)) {
ret = FALSE;
}
}
}
ui_status_end(op, "done.");
ui_log(op, "Post-install sanity check %s.", ret ? "passed" : "failed");
} /* check_installed_files_from_package() */
/*
* check_symlink() - check that the specified symbolic link exists and
* point to the correct target. Print descriptive warnings if
* anything about the symbolic link doesn't appear as it should.
*
* Returns FALSE if the symbolic link appeared wrong; returns TRUE if
* everything appears in order.
*/
static int check_symlink(Options *op, const char *target, const char *link,
const char *descr)
{
int success = TRUE;
char *actual_target;
actual_target = get_symlink_target(op, link);
if (!actual_target) {
ui_warn(op, "The symbolic link '%s' does not exist. This is "
"necessary for correct operation of the %s. You can "
"create this symbolic link manually by executing "
"`ln -sf %s %s`.",
link,
descr,
target,
link);
return FALSE;
}
if (strcmp(actual_target, target) != 0) {
ui_warn(op, "The symbolic link '%s' does not point to '%s' "
"as is necessary for correct operation of the %s. "
"It is possible that `ldconfig` has created this "
"incorrect symbolic link because %s's "
"\"soname\" conflicts with that of %s. It is "
"recommended that you remove or rename the file "
"'%s' and create the necessary symbolic link by "
"running `ln -sf %s %s`.",
link,
target,
descr,
actual_target,
target,
actual_target,
target,
link);
success = FALSE;
}
nvfree(actual_target);
return success;
}
/*
* unprelink() - attempt to run `prelink -u` on a file to restore it to
* its pre-prelinked state.
*/
static int unprelink(Options *op, const char *filename)
{
char *cmd;
int ret = ENOENT;
cmd = find_system_util("prelink");
if (cmd) {
char *cmdline;
cmdline = nvstrcat(cmd, " -u ", filename, NULL);
ret = run_command(op, cmdline, NULL, FALSE, 0, TRUE);
nvfree(cmd);
nvfree(cmdline);
}
return ret;
} /* unprelink() */
/*
* verify_crc() - Compute the CRC of a file and compare it against an
* expected value. Returns TRUE if the values match, FALSE otherwise.
*
*/
int verify_crc(Options *op, const char *filename, unsigned int crc,
unsigned int *actual_crc)
{
/* only check the crc if we were handed a non-emtpy crc */
if (crc == 0) {
return TRUE;
}
*actual_crc = compute_crc(op, filename);
return crc == *actual_crc;
} /* verify_crc() */
/*
* check_installed_file() - check that the specified installed file exists,
* has the correct permissions, and has the correct crc. Takes a function
* pointer to either ui_log() or ui_warn() depending on how errors should
* be reported.
*
* If anything is incorrect, print a warning and return FALSE,
* otherwise return TRUE.
*/
int check_installed_file(Options *op, const char *filename,
const mode_t mode, const uint32 crc,
ui_message_func *logwarn)
{
struct stat stat_buf;
uint32 actual_crc;
if (lstat(filename, &stat_buf) == -1) {
logwarn(op, "Unable to find installed file '%s' (%s).",
filename, strerror(errno));
return FALSE;
}
if (!S_ISREG(stat_buf.st_mode)) {
logwarn(op, "The installed file '%s' is not of the correct filetype.",
filename);
return FALSE;
}
/* Don't check the mode if we don't have one: backup log entries for
installed files don't preserve the mode. */
if (mode && ((stat_buf.st_mode & PERM_MASK) != (mode & PERM_MASK))) {
logwarn(op, "The installed file '%s' has permissions %04o, but it "
"was installed with permissions %04o.", filename,
(stat_buf.st_mode & PERM_MASK),
(mode & PERM_MASK));
return FALSE;
}
if (!verify_crc(op, filename, crc, &actual_crc)) {
int ret;
/* If this is not an ELF file, we should not try to unprelink it. */
if (get_elf_architecture(filename) == ELF_INVALID_FILE) {
logwarn(op, "The installed file '%s' has a different checksum "
"(%ul) than when it was installed (%ul).", filename,
actual_crc, crc);
return FALSE;
}
/* Otherwise, unprelinking may be able to restore the original file. */
ui_expert(op, "The installed file '%s' has a different checksum (%ul) "
"than when it was installed (%ul). This may be due to "
"prelinking; attemping `prelink -u %s` to restore the file.",
filename, actual_crc, crc, filename);
ret = unprelink(op, filename);
if (ret != 0) {
logwarn(op, "The installed file '%s' seems to have changed, but "
"`prelink -u` failed; unable to restore '%s' to an "
"un-prelinked state.", filename, filename);
return FALSE;
}
if (!verify_crc(op, filename, crc, &actual_crc)) {
logwarn(op, "The installed file '%s' has a different checksum "
"(%ul) after running `prelink -u` than when it was "
"installed (%ul).",
filename, actual_crc, crc);
return FALSE;
}
ui_expert(op, "Un-prelinking successful: %s was restored to its "
"original state.", filename);
}
return TRUE;
}
#if defined(NV_TLS_TEST)
/*
* tls_test() - Starting with glibc 2.3, there is a new thread local
* storage mechanism. To accomodate this, NVIDIA's OpenGL libraries
* are built both the "classic" way, and the new way. To determine
* which set of OpenGL libraries to install, execute the test program
* stored in tls_test_array. If the program returns 0 we should
* install the new tls libraries; if it returns anything else, we
* should install the "classic" libraries.
*
* So as to avoid any risk of not being able to find the tls_test
* binary at run time, the test program is stored as static data
* inside the installer binary (in the same way that the user
* interface shared libraries are)... see
* user_interface.c:extract_user_interface() for details.
*
* Return TRUE if the new tls libraries should be installed; FALSE if
* the old libraries should be used.
*/
/* pull in the array and size from g_tls_test.c */
extern const unsigned char tls_test_array[];
extern const int tls_test_array_size;
/* pull in the array and size from g_tls_test_dso.c */
extern const unsigned char tls_test_dso_array[];
extern const int tls_test_dso_array_size;
#if defined(NV_X86_64)
/* pull in the array and size from g_tls_test_32.c */
extern const unsigned char tls_test_array_32[];
extern const int tls_test_array_32_size;
/* pull in the array and size from g_tls_test_dso_32.c */
extern const unsigned char tls_test_dso_array_32[];
extern const int tls_test_dso_array_32_size;
#endif /* NV_X86_64 */
/* forward prototype */
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,
const int dso_test_array_size);
int tls_test(Options *op, int compat_32_libs)
{
if (compat_32_libs) {
#if defined(NV_X86_64)
return tls_test_internal(op, op->which_tls_compat32,
tls_test_array_32,
tls_test_array_32_size,
tls_test_dso_array_32,
tls_test_dso_array_32_size);
#else
return FALSE;
#endif /* NV_X86_64 */
} else {
return tls_test_internal(op, op->which_tls,
tls_test_array,
tls_test_array_size,
tls_test_dso_array,
tls_test_dso_array_size);
}
} /* tls_test */
/*
* tls_test_internal() - this is the routine that does all the work to
* write the tests to file and execute them; the caller (tls_test())
* just selects which array data is used as the test.
*/
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,
const int test_dso_array_size)
{
int ret = FALSE;
char *tmpfile = NULL, *dso_tmpfile = NULL, *cmd = NULL;
/* allow commandline options to bypass this test */
if (which_tls == FORCE_NEW_TLS) return TRUE;
if (which_tls == FORCE_CLASSIC_TLS) return FALSE;
/* check that we have the test program */
if ((test_array == NULL) ||
(test_array_size == 0) ||
(test_dso_array == NULL) ||
(test_dso_array_size == 0)) {
ui_warn(op, "The thread local storage test program is not "
"present; assuming classic tls.");
return FALSE;
}
/* write the tls_test data to tmp files */
tmpfile = write_temp_file(op, test_array_size, test_array,
S_IRUSR|S_IWUSR|S_IXUSR);
if (!tmpfile) {
ui_warn(op, "Unable to create temporary file for thread local "
"storage test program (%s); assuming classic tls.",
strerror(errno));
goto done;
}
dso_tmpfile = write_temp_file(op, test_dso_array_size,
test_dso_array,
S_IRUSR|S_IWUSR|S_IXUSR);
if (!dso_tmpfile) {
ui_warn(op, "Unable to create temporary file for thread local "
"storage test program (%s); assuming classic tls.",
strerror(errno));
goto done;
}
if (set_security_context(op, dso_tmpfile) == FALSE) {
/* We are on a system with SELinux and the chcon command failed.
* Assume that the system is recent enough to have the new TLS
*/
ui_warn(op, "Unable to set the security context on file %s; "
"assuming new tls.",
dso_tmpfile);
ret = TRUE;
goto done;
}
/* run the test */
cmd = nvstrcat(tmpfile, " ", dso_tmpfile, NULL);
ret = run_command(op, cmd, NULL, FALSE, 0, TRUE);
ret = ((ret == 0) ? TRUE : FALSE);
done:
if (tmpfile) {
unlink(tmpfile);
nvfree(tmpfile);
}
if (dso_tmpfile) {
unlink(dso_tmpfile);
nvfree(dso_tmpfile);
}
if (cmd) nvfree(cmd);
return ret;
} /* test_tls_internal() */
#endif /* defined(NV_TLS_TEST) */
/*
* 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, which_tls, which_tls_compat32;
#if defined(NV_TLS_TEST)
which_tls = op->which_tls;
which_tls_compat32 = op->which_tls_compat32;
#else
/* Platforms that don't need the TLS test only support "new" ELF TLS. */
which_tls = which_tls_compat32 = TLS_LIB_NEW_TLS;
#endif /* NV_TLS_TEST */
ui_status_begin(op, "Running runtime sanity check:", "Checking");
#if defined(NV_X86_64)
ret = rtld_test_internal(op, p, 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, 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() */
/*
* collapse_multiple_slashes() - remove any/all occurances of "//" from the
* argument string.
*/
void collapse_multiple_slashes(char *s)
{
char *p;
unsigned int i, len;
while ((p = strstr(s, "//")) != NULL) {
p++; /* advance to second '/' */
while (*p == '/') {
len = strlen(p);
for (i = 0; i < len; i++) p[i] = p[i+1];
}
}
}
/*
* is_symbolic_link_to() - check if the file with path 'path' is
* a symbolic link pointing to 'dest'. Returns TRUE if this is
* the case; if the file is not a symbolic link if it doesn't point
* to 'dest', is_symbolic_link_to() returns FALSE.
*/
int is_symbolic_link_to(const char *path, const char *dest)
{
struct stat stat_buf0, stat_buf1;
if ((lstat(path, &stat_buf0) != 0)
|| !S_ISLNK(stat_buf0.st_mode))
return FALSE;
if ((stat(path, &stat_buf0) == 0) &&
(stat(dest, &stat_buf1) == 0) &&
(stat_buf0.st_dev == stat_buf1.st_dev) &&
(stat_buf0.st_ino == stat_buf1.st_ino))
return TRUE;
return FALSE;
} /* is_symbolic_link_to() */
/*
* 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 fd, i, found = TRUE, ret = TRUE;
char *name = NULL, *cmd = NULL, *data = NULL;
char *tmpfile, *s;
char *tmpfile1 = NULL;
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;
}
/* create another temporary file */
tmpfile1 = nvstrcat(op->tmpdir, "/nv-tmp-XXXXXX", NULL);
fd = mkstemp(tmpfile1);
if (fd == -1) {
ui_warn(op, "Unable to create a temporary file for the runtime "
"configuration test program (%s); assuming successful "
"installation.", strerror(errno));
goto done;
}
close(fd);
/* perform the test(s) */
for (i = 0; i < p->num_entries; i++) {
if ((p->entries[i].type != FILE_TYPE_OPENGL_LIB) &&
(p->entries[i].type != FILE_TYPE_TLS_LIB)) {
continue;
} else if ((which_tls & TLS_LIB_TYPE_FORCED) &&
(p->entries[i].type == FILE_TYPE_TLS_LIB)) {
continue;
#if defined(NV_X86_64)
} else if ((p->entries[i].compat_arch == FILE_COMPAT_ARCH_NATIVE)
&& compat_32_libs) {
continue;
} else if ((p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32)
&& !compat_32_libs) {
continue;
#endif /* NV_X86_64 */
} else if ((which_tls == TLS_LIB_NEW_TLS) &&
(p->entries[i].tls_class == FILE_TLS_CLASS_CLASSIC)) {
continue;
} else if ((which_tls == TLS_LIB_CLASSIC_TLS) &&
(p->entries[i].tls_class == FILE_TLS_CLASS_NEW)) {
continue;
}
name = nvstrdup(p->entries[i].name);
if (!name) continue;
s = strstr(name, ".so.1");
if (!s || s[strlen(".so.1")] != '\0') goto next;
cmd = nvstrcat(op->utils[LDD], " ", tmpfile, " > ", tmpfile1, NULL);
if (run_command(op, cmd, NULL, FALSE, 0, TRUE)) {
/* running ldd on a 32-bit SO will fail without a 32-bit loader */
if (compat_32_libs) {
ui_warn(op, "Unable to perform the runtime configuration "
"check for 32-bit library '%s' ('%s'); this is "
"typically caused by the lack of a 32-bit "
"compatibility environment. Assuming successful "
"installation.", name, p->entries[i].dst);
} else {
ui_warn(op, "Unable to perform the runtime configuration "
"check for library '%s' ('%s'); assuming successful "
"installation.", name, p->entries[i].dst);
}
goto done;
}
cmd = nvstrcat(op->utils[GREP], " ", name, " ", tmpfile1,
" | ", op->utils[CUT], " -d \" \" -f 3", NULL);
if (run_command(op, cmd, &data, FALSE, 0, TRUE) ||
(data == NULL)) {
ui_warn(op, "Unable to perform the runtime configuration "
"check for library '%s' ('%s'); assuming successful "
"installation.", name, p->entries[i].dst);
goto done;
}
if (!strcmp(data, "not") || !strlen(data)) {
/*
* If the library didn't show up in ldd's output or
* wasn't found, set 'found' to false and notify the
* user with a more meaningful message below.
*/
free(data); data = NULL;
found = FALSE;
} else {
/*
* Double slashes in /etc/ld.so.conf make it all the
* way to ldd's output on some systems. Strip them
* here to make sure they don't cause a false failure.
*/
collapse_multiple_slashes(data);
}
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 (!found || (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) &&
(stat_buf0.st_dev == stat_buf1.st_dev) &&
(stat_buf0.st_ino == stat_buf1.st_ino))
goto next;
if (!found && !compat_32_libs) {
ui_error(op, "The runtime configuration check failed for "
"library '%s' (expected: '%s', found: (not found)). "
"The most likely reason for this is that the library "
"was installed to the wrong location or that your "
"system's dynamic loader configuration needs to be "
"updated. Please check the OpenGL library installation "
"prefix and/or the dynamic loader configuration.",
p->entries[i].name, name);
ret = FALSE;
goto done;
#if defined(NV_X86_64)
} else if (!found) {
ui_warn(op, "The runtime configuration check failed for "
"library '%s' (expected: '%s', found: (not found)). "
"The most likely reason for this is that the library "
"was installed to the wrong location or that your "
"system's dynamic loader configuration needs to be "
"updated. Please check the 32-bit OpenGL compatibility "
"library installation prefix and/or the dynamic loader "
"configuration.",
p->entries[i].name, name);
goto next;
#endif /* NV_X86_64 */
} else {
ui_error(op, "The runtime configuration check failed for the "
"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/or "
"third-party graphics driver packages.",
p->entries[i].name, name, data);
ret = FALSE;
goto done;
}
}
next:
nvfree(name); name = NULL;
nvfree(cmd); cmd = NULL;
nvfree(data); data = NULL;
}
done:
if (tmpfile) {
unlink(tmpfile);
nvfree(tmpfile);
}
if (tmpfile1) {
unlink(tmpfile1);
nvfree(tmpfile1);
}
nvfree(name);
nvfree(cmd);
nvfree(data);
return ret;
} /* rtld_test_internal() */
/*
* get_xserver_information() - parse the versionString (from `X
* -version`) and assign relevant information that we infer from the X
* server version.
*/
static int get_xserver_information(const char *versionString,
int *isModular,
int *supportsOutputClassSection)
{
#define XSERVER_VERSION_FORMAT_1 "X Window System Version"
#define XSERVER_VERSION_FORMAT_2 "X.Org X Server"
int major, minor, found;
const char *ptr;
/* check if this is an XFree86 X server */
if (strstr(versionString, "XFree86 Version")) {
*isModular = FALSE;
return TRUE;
}
/*
* This must be an X.Org X server. Attempt to parse the major.minor version
* out of the string
*/
found = FALSE;
if (((ptr = strstr(versionString, XSERVER_VERSION_FORMAT_1)) != NULL) &&
(sscanf(ptr, XSERVER_VERSION_FORMAT_1 " %d.%d", &major, &minor) == 2)) {
found = TRUE;
}
if (!found &&
((ptr = strstr(versionString, XSERVER_VERSION_FORMAT_2)) != NULL) &&
(sscanf(ptr, XSERVER_VERSION_FORMAT_2 " %d.%d", &major, &minor) == 2)) {
found = TRUE;
}
/* if we can't parse the version, give up */
if (!found) return FALSE;
/*
* isModular: X.Org X11R6.x X servers are monolithic, all others
* are modular
*/
if (major == 6) {
*isModular = FALSE;
} else {
*isModular = TRUE;
}
/*
* support for using OutputClass sections to automatically match drivers to
* platform devices was added in X.Org xserver 1.16.
*/
if ((major == 6) || (major == 7) || ((major == 1) && (minor < 16))) {
*supportsOutputClassSection = FALSE;
} else {
*supportsOutputClassSection = TRUE;
}
return TRUE;
} /* get_xserver_information() */
/*
* query_xorg_version() - run the X binary with the '-version'
* command line option and extract the version.
*
* Using the version, try to infer if it's part of a modular Xorg release. If
* the version can't be determined, we assume it's not.
*
* This function assigns the following fields:
* op->modular_xorg
*/
#define OLD_VERSION_FORMAT "(protocol Version %d, revision %d, vendor release %d)"
#define NEW_VERSION_FORMAT "X Protocol Version %d, Revision %d, Release %d."
void query_xorg_version(Options *op)
{
char *cmd = NULL, *data = NULL;
int ret = FALSE;
if (!op->utils[XSERVER])
goto done;
cmd = nvstrcat(op->utils[XSERVER], " -version", NULL);
if (run_command(op, cmd, &data, FALSE, 0, TRUE) ||
(data == NULL)) {
goto done;
}
/*
* process the `X -version` output to infer if this X server is
* modular
*/
ret = get_xserver_information(data, &op->modular_xorg,
&op->xorg_supports_output_class);
/* fall through */
done:
/*
* if no X server was found, or querying the version on the command line
* failed, or get_xserver_information() failed, assume the X server is
* modular, but does not support OutputClass sections
*/
if (!ret) {
op->modular_xorg = TRUE;
op->xorg_supports_output_class = FALSE;
}
nvfree(data);
nvfree(cmd);
}
/*
* check_for_running_x() - running any X server (even with a
* non-NVIDIA driver) can cause stability problems, so check that
* there is no X server running. To do this, scan for any
* /tmp/.X[n]-lock files, where [n] is the number of the X Display
* (we'll just check for 0-7). Get the pid contained in this X lock file,
* this is the pid of the running X server. If any X server is running,
* print an error message and return FALSE. If no X server is running,
* return TRUE.
*/
int check_for_running_x(Options *op)
{
char path[14], *buf;
char procpath[17]; /* contains /proc/%d, accounts for 32-bit values of pid */
int i, pid;
/*
* If we are installing for a non-running kernel *and* we are only
* installing a kernel module, then skip this check.
*/
if (op->kernel_module_only && op->kernel_name) {
ui_log(op, "Only installing a kernel module for a non-running "
"kernel; skipping the \"is an X server running?\" test.");
return TRUE;
}
for (i = 0; i < 8; i++) {
snprintf(path, 14, "/tmp/.X%1d-lock", i);
if (read_text_file(path, &buf) == TRUE) {
int num = sscanf(buf, "%d", &pid);
nvfree(buf);
if (num != 1) {
ui_warn(op, "Failed to read a pid from X lock file '%s'", path);
return TRUE;
}
snprintf(procpath, 17, "/proc/%d", pid);
if (access(procpath, F_OK) == 0) {
ui_log(op, "The file '%s' exists and appears to contain the "
"process ID '%d' of a runnning X server.", path, pid);
if (op->no_x_check) {
ui_log(op, "Continuing per the '--no-x-check' option.");
} else {
ui_error(op, "You appear to be running an X server; please "
"exit X before installing. For further details, "
"please see the section INSTALLING THE NVIDIA "
"DRIVER in the README available on the Linux driver "
"download page at www.nvidia.com.");
return FALSE;
}
}
}
}
return TRUE;
} /* check_for_running_x() */
/*
* check_for_nvidia_graphics_devices() - check if there are supported
* NVIDIA graphics devices installed in this system. If one or more
* supported devices are found, the function returns TRUE, else it prints
* a warning message and returns FALSE. If legacy devices are detected
* in the system, a warning message is printed for each one.
*/
static void ignore_libpci_output(char *fmt, ...)
{
}
int check_for_nvidia_graphics_devices(Options *op, Package *p)
{
struct pci_access *pacc;
struct pci_dev *dev;
int i, found_supported_device = FALSE;
int found_vga_device = FALSE;
uint16 class;
pacc = pci_alloc();
if (!pacc) return TRUE;
pacc->error = ignore_libpci_output;
pacc->warning = ignore_libpci_output;
pci_init(pacc);
if (!pacc->methods) return TRUE;
pci_scan_bus(pacc);
for (dev = pacc->devices; dev != NULL; dev = dev->next) {
if ((pci_fill_info(dev, PCI_FILL_IDENT) & PCI_FILL_IDENT) == 0)
continue;
class = pci_read_word(dev, PCI_CLASS_DEVICE);
if ((class == PCI_CLASS_DISPLAY_VGA || class == PCI_CLASS_DISPLAY_3D) &&
(dev->vendor_id == 0x10de) /* NVIDIA */ &&
(dev->device_id >= 0x0020) /* TNT or later */) {
/*
* First check if this GPU is a "legacy" GPU; if it is, print a
* warning message and point the user to the NVIDIA Linux
* driver download page for.
*/
int found_legacy_device = FALSE;
for (i = 0; i < sizeof(LegacyList) / sizeof(LEGACY_INFO); i++) {
if (dev->device_id == LegacyList[i].uiDevId) {
int j, nstrings;
const char *branch_string = "";
nstrings = sizeof(LegacyStrings) / sizeof(LEGACY_STRINGS);
for (j = 0; j < nstrings; j++) {
if (LegacyStrings[j].branch == LegacyList[i].branch) {
branch_string = LegacyStrings[j].description;
break;
}
}
ui_warn(op, "The NVIDIA %s GPU installed in this system is supported "
"through the NVIDIA %s legacy Linux graphics drivers. Please "
"visit http://www.nvidia.com/object/unix.html for more "
"information. The %s NVIDIA Linux graphics driver will "
"ignore this GPU.",
LegacyList[i].AdapterString,
branch_string,
p->version);
found_legacy_device = TRUE;
}
}
if (!found_legacy_device) {
found_supported_device = TRUE;
if (class == PCI_CLASS_DISPLAY_VGA)
found_vga_device = TRUE;
}
}
}
dev = pacc->devices;
pci_cleanup(pacc);
if (!dev) return TRUE;
if (!found_supported_device) {
ui_warn(op, "You do not appear to have an NVIDIA GPU supported by the "
"%s NVIDIA Linux graphics driver installed in this system. "
"For further details, please see the appendix SUPPORTED "
"NVIDIA GRAPHICS CHIPS in the README available on the Linux "
"driver download page at www.nvidia.com.", p->version);
return FALSE;
}
if (!found_vga_device)
op->no_nvidia_xconfig_question = TRUE;
return TRUE;
} /* check_for_nvidia_graphics_devices() */
/*
* check_selinux() - check if selinux is available.
* Sets the variable op->selinux_enabled.
* Returns TRUE on success, FALSE otherwise.
*/
int check_selinux(Options *op)
{
int selinux_available = TRUE;
if (op->utils[CHCON] == NULL ||
op->utils[SELINUX_ENABLED] == NULL ||
op->utils[GETENFORCE] == NULL) {
selinux_available = FALSE;
}
switch (op->selinux_option) {
case SELINUX_FORCE_YES:
if (selinux_available == FALSE) {
/* We have set the option --force-selinux=yes but SELinux
* is not available on this system */
ui_error(op, "Invalid option '--force-selinux=yes'; "
"SELinux is not available on this system");
return FALSE;
}
op->selinux_enabled = TRUE;
break;
case SELINUX_FORCE_NO:
if (selinux_available == TRUE) {
char *data = NULL;
int ret = run_command(op, op->utils[GETENFORCE], &data,
FALSE, 0, TRUE);
if ((ret != 0) || (!data)) {
ui_warn(op, "Cannot check the current mode of SELinux; "
"Command getenforce() failed");
} else if (!strcmp(data, "Enforcing")) {
/* We have set the option --force-selinux=no but SELinux
* is enforced on this system */
ui_warn(op, "The option '--force-selinux' has been set to 'no', "
"but SELinux is enforced on this system; "
"The X server may not start correctly ");
}
nvfree(data);
}
op->selinux_enabled = FALSE;
break;
case SELINUX_DEFAULT:
op->selinux_enabled = FALSE;
if (selinux_available == TRUE) {
int ret = run_command(op, op->utils[SELINUX_ENABLED], NULL,
FALSE, 0, TRUE);
if (ret == 0) {
op->selinux_enabled = TRUE;
}
}
break;
}
/* Figure out which chcon type we need if the user didn't supply one. */
if (op->selinux_enabled && !op->selinux_chcon_type) {
unsigned char foo = 0;
char *tmpfile;
static const char* chcon_types[] = {
"textrel_shlib_t", /* Shared library with text relocations */
"texrel_shlib_t", /* Obsolete synonym for the above */
"shlib_t", /* Generic shared library */
NULL
};
/* Create a temporary file */
tmpfile = write_temp_file(op, 1, &foo, S_IRUSR);
if (!tmpfile) {
ui_warn(op, "Couldn't test chcon. Assuming shlib_t.");
op->selinux_chcon_type = "shlib_t";
} else {
int i, ret;
char *cmd;
/* Try each chcon command */
for (i = 0; chcon_types[i]; i++) {
cmd = nvstrcat(op->utils[CHCON], " -t ", chcon_types[i], " ",
tmpfile, NULL);
ret = run_command(op, cmd, NULL, FALSE, 0, TRUE);
nvfree(cmd);
if (ret == 0) break;
}
if (!chcon_types[i]) {
/* None of them work! */
ui_warn(op, "Couldn't find a working chcon argument. "
"Defaulting to shlib_t.");
op->selinux_chcon_type = "shlib_t";
} else {
op->selinux_chcon_type = chcon_types[i];
}
unlink(tmpfile);
nvfree(tmpfile);
}
}
if (op->selinux_enabled) {
ui_log(op, "Tagging shared libraries with chcon -t %s.",
op->selinux_chcon_type);
}
return TRUE;
} /* check_selinux */
/*
* run_nvidia_xconfig() - run the `nvidia-xconfig` utility. Without
* any options, this will just make sure the X config file uses the
* NVIDIA driver by default.
*
* Parameters:
*
* restore: controls whether the --restore-original-backup option is added,
* which attempts to restore the original backed up X config file.
* question: if this is non-NULL, the user will be asked 'question' as a
* yes or no question, to determine whether to run nvidia-xconfig.
* answer: the default answer to 'question'.
*
* Returns TRUE if nvidia-xconfig ran successfully; returns FALSE if
* nvidia-xconfig ran unsuccessfully, or did not run at all.
*/
int run_nvidia_xconfig(Options *op, int restore, const char *question,
int default_answer)
{
int ret = FALSE;
char *nvidia_xconfig;
nvidia_xconfig = find_system_util("nvidia-xconfig");
if (nvidia_xconfig == NULL) {
/* nvidia-xconfig not found: don't run it or ask any questions */
goto done;
}
ret = question ? ui_yes_no(op, default_answer, "%s", question) : TRUE;
if (ret) {
int cmd_ret;
char *data, *cmd, *args;
args = restore ? " --restore-original-backup" : "";
cmd = nvstrcat(nvidia_xconfig, args, NULL);
cmd_ret = run_command(op, cmd, &data, FALSE, 0, TRUE);
if (cmd_ret != 0) {
ui_error(op, "Failed to run `%s`:\n%s", cmd, data);
ret = FALSE;
}
nvfree(cmd);
nvfree(data);
}
done:
nvfree(nvidia_xconfig);
return ret;
} /* run_nvidia_xconfig() */
#define DISTRO_HOOK_DIRECTORY "/usr/lib/nvidia/"
/*
* run_distro_hook() - run a distribution-provided hook script
*/
HookScriptStatus run_distro_hook(Options *op, const char *hook)
{
int ret, status, shouldrun = op->run_distro_scripts;
char *cmd = nvstrcat(DISTRO_HOOK_DIRECTORY, hook, NULL);
if (op->kernel_module_only) {
ui_expert(op,
"Not running distribution-provided %s script %s because "
"--kernel-module-only was specified.",
hook, cmd);
ret = HOOK_SCRIPT_NO_RUN;
goto done;
}
if (access(cmd, X_OK) < 0) {
ui_expert(op, "No distribution %s script found.", hook);
ret = HOOK_SCRIPT_NO_RUN;
goto done;
}
/* in expert mode, ask before running distro hooks */
if (op->expert) {
shouldrun = ui_yes_no(op, shouldrun,
"Run distribution-provided %s script %s?",
hook, cmd);
}
if (!shouldrun) {
ui_expert(op,
"Not running distribution-provided %s script %s",
hook, cmd);
ret = HOOK_SCRIPT_NO_RUN;
goto done;
}
ui_status_begin(op, "Running distribution scripts", "Executing %s", cmd);
status = run_command(op, cmd, NULL, TRUE, 0, TRUE);
ui_status_end(op, "done.");
ret = (status == 0) ? HOOK_SCRIPT_SUCCESS : HOOK_SCRIPT_FAIL;
done:
nvfree(cmd);
return ret;
}
/*
* prompt_for_user_cancel() - print a caller-supplied message and ask the
* user whether to cancel the installation. If the file at the caller-supplied
* path is readable, include any text from that file as additional detail for
* the message. Returns TRUE if the user decides to cancel the installation;
* returns FALSE if the user decides not to cancel.
*/
static int prompt_for_user_cancel(Options *op, const char *file,
int default_choice, const char *text)
{
int ret, file_read, msglen;
char *message = NULL, *prompt;
file_read = read_text_file(file, &message);
if (!file_read || !message) {
message = nvstrdup("");
}
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 installation method" : "", message,
CONTINUE_ABORT_CHOICES, NUM_CONTINUE_ABORT_CHOICES,
default_choice);
nvfree(message);
nvfree(prompt);
if (ret == ABORT_CHOICE) {
ui_error(op, "The installation was canceled due to the availability "
"or presence of an alternate driver installation. Please "
"see %s for more details.", op->log_file_name);
return TRUE;
}
return FALSE;
}
#define INSTALL_PRESENT_FILE "alternate-install-present"
#define INSTALL_AVAILABLE_FILE "alternate-install-available"
/*
* check_for_alternate_install() - check to see if an alternate install is
* available or present. If present, recommend updating via the alternate
* mechanism or uninstalling first before proceeding with an nvidia-installer
* installation; if available, but not present, inform the user about it.
* Returns TRUE if no alternate installation is available or present, or if
* checking for alternate installs is skipped, or if the user decides not to
* cancel the installation. Returns FALSE if the user decides to cancel the
* installation.
*/
int check_for_alternate_install(Options *op)
{
int shouldcheck = op->check_for_alternate_installs;
const char *alt_inst_present = DISTRO_HOOK_DIRECTORY INSTALL_PRESENT_FILE;
const char *alt_inst_avail = DISTRO_HOOK_DIRECTORY INSTALL_AVAILABLE_FILE;
if (op->expert) {
shouldcheck = ui_yes_no(op, shouldcheck,
"Check for the availability or presence of "
"alternate driver installs?");
}
if (!shouldcheck) {
return TRUE;
}
if (access(alt_inst_present, F_OK) == 0) {
const char *msg;
msg = "The NVIDIA driver appears to have been installed previously "
"using a different installer. To prevent potential conflicts, it "
"is recommended either to update the existing installation using "
"the same mechanism by which it was originally installed, or to "
"uninstall the existing installation before installing this "
"driver.";
return !prompt_for_user_cancel(op, alt_inst_present, ABORT_CHOICE, msg);
}
if (access(alt_inst_avail, F_OK) == 0) {
const char *msg;
msg = "An alternate method of installing the NVIDIA driver was "
"detected. (This is usually a package provided by your "
"distributor.) A driver installed via that method may integrate "
"better with your system than a driver installed by "
"nvidia-installer.";
return !prompt_for_user_cancel(op, alt_inst_avail, CONTINUE_CHOICE, msg);
}
return TRUE;
}
/*
* Determine if the nouveau driver is currently in use. We do the
* equivalent of:
*
* ls -l /sys/bus/pci/devices/ /driver | grep nouveau
*
* The directory structure under /sys/bus/pci/devices/ should contain
* a directory for each PCI device, and for those devices with a
* kernel driver there will be a "driver" symlink.
*
* This appears to be consistent with how libpciaccess works.
*
* Returns TRUE if nouveau is found; returns FALSE if not.
*/
#define SYSFS_DEVICES_PATH "/sys/bus/pci/devices"
static int nouveau_is_present(void)
{
DIR *dir;
struct dirent * ent;
int found = FALSE;
dir = opendir(SYSFS_DEVICES_PATH);
if (!dir) {
return FALSE;
}
while ((ent = readdir(dir)) != NULL) {
char driver_path[PATH_MAX];
char symlink_target[PATH_MAX];
char *name;
ssize_t ret;
if ((strcmp(ent->d_name, ".") == 0) ||
(strcmp(ent->d_name, "..") == 0)) {
continue;
}
snprintf(driver_path, PATH_MAX,
SYSFS_DEVICES_PATH "/%s/driver", ent->d_name);
driver_path[PATH_MAX - 1] = '\0';
ret = readlink(driver_path, symlink_target, PATH_MAX);
if (ret < 0) {
continue;
}
/* readlink(3) does not nul-terminate its returned string */
ret = NV_MIN(ret, PATH_MAX - 1);
symlink_target[ret] = '\0';
name = basename(symlink_target);
if (strcmp(name, "nouveau") == 0) {
found = TRUE;
break;
}
}
closedir(dir);
return found;
}
static const char* modprobe_directories[] = { "/etc/modprobe.d",
"/usr/lib/modprobe.d" };
#define DISABLE_NOUVEAU_FILE "/nvidia-installer-disable-nouveau.conf"
/*
* this checksum is the result of compute_crc() for the file contents
* written in blacklist_nouveau()
*/
#define DISABLE_NOUVEAU_FILE_CKSUM 3728279991U
/*
* blacklist_filename() - generate the filename of a blacklist file. The
* caller should ensure that the directory exists, or be able to handle
* failures correctly if the directory does not exist.
*/
static char *blacklist_filename(const char *directory)
{
return nvstrcat(directory, DISABLE_NOUVEAU_FILE, NULL);
}
static char *write_blacklist_file(const char *directory)
{
int ret;
struct stat stat_buf;
FILE *file;
char *filename;
ret = stat(directory, &stat_buf);
if (ret != 0 || !S_ISDIR(stat_buf.st_mode)) {
return NULL;
}
filename = blacklist_filename(directory);
file = fopen(filename, "w+");
if (!file) {
nvfree(filename);
return NULL;
}
fprintf(file, "# generated by nvidia-installer\n");
fprintf(file, "blacklist nouveau\n");
fprintf(file, "options nouveau modeset=0\n");
ret = fclose(file);
if (ret != 0) {
nvfree(filename);
return NULL;
}
return filename;
}
/*
* Write modprobe configuration fragments to disable loading of
* nouveau:
*
* for directory in /etc/modprobe.d /usr/lib/modprobe.d; do
* if [ -d $directory ]; then
* name=$directory/nvidia-installer-nouveau-blacklist.conf
* echo "# generated by nvidia-installer" > $name
* echo "blacklist nouveau" >> $name
* echo "options nouveau modeset=0" >> $name
* fi
* done
*
* Returns a list of written configuration files if successful;
* returns NULL if there was a failure.
*/
static char *blacklist_nouveau(void)
{
int i;
char *filelist = NULL;
for (i = 0; i < ARRAY_LEN(modprobe_directories); i++) {
char *filename = write_blacklist_file(modprobe_directories[i]);
if (filename) {
filelist = nv_prepend_to_string_list(filelist, filename, ", ");
nvfree(filename);
}
}
return filelist;
}
/*
* Check if any nouveau blacklist file is already present with the
* contents that we expect, and return the paths to any found files,
* or NULL if no matching files were found
*/
static char *nouveau_blacklist_file_is_present(Options *op)
{
int i;
char *filelist = NULL;
for (i = 0; i < ARRAY_LEN(modprobe_directories); i++) {
char *filename = blacklist_filename(modprobe_directories[i]);
if ((access(filename, R_OK) == 0) &&
(compute_crc(op, filename) == DISABLE_NOUVEAU_FILE_CKSUM)) {
filelist = nv_prepend_to_string_list(filelist, filename, ", ");
}
nvfree(filename);
}
return filelist;
}
/*
* Check if the nouveau kernel driver is in use. If it is, provide an
* appropriate error message and offer to try to disable nouveau.
*
* Returns FALSE if the nouveau kernel driver is in use (cause
* installation to abort); returns TRUE if the nouveau driver is not
* in use, or if the nouveau check is to be skipped.
*/
int check_for_nouveau(Options *op)
{
int ret, nouveau_detected;
char *blacklist_files;
#define NOUVEAU_POINTER_MESSAGE \
"Please consult the NVIDIA driver README and your Linux " \
"distribution's documentation for details on how to correctly " \
"disable the Nouveau kernel driver."
if (op->no_nouveau_check) return TRUE;
nouveau_detected = nouveau_is_present();
if (nouveau_detected) {
ui_error(op, "The Nouveau kernel driver is currently in use "
"by your system. This driver is incompatible with the NVIDIA "
"driver, and must be disabled before proceeding. "
NOUVEAU_POINTER_MESSAGE);
} else if (!op->disable_nouveau) {
/* If nouveau isn't loaded, we can return early, unless the user
* explicitly requested for the blacklist file to be written. */
return !nouveau_detected;
}
blacklist_files = nouveau_blacklist_file_is_present(op);
if (blacklist_files) {
ui_warn(op, "One or more modprobe configuration files to disable "
"Nouveau are already present at: %s. Please be "
"sure you have rebooted your system since these files were "
"written. If you have rebooted, then Nouveau may be enabled "
"for other reasons, such as being included in the system "
"initial ramdisk or in your X configuration file. "
NOUVEAU_POINTER_MESSAGE, blacklist_files);
nvfree(blacklist_files);
if (!op->disable_nouveau) {
/* If the user explicitly requested that the blacklist files be
* written, don't return early, so that the files can be written
* again, e.g. in case a file is present, but not in the right
* place for this particular system. */
return !nouveau_detected;
}
}
ret = ui_yes_no(op, op->disable_nouveau, "For some distributions, Nouveau "
"can be disabled by adding a file in the modprobe "
"configuration directory. Would you like nvidia-installer "
"to attempt to create this modprobe file for you?");
if (ret) {
blacklist_files = blacklist_nouveau();
if (blacklist_files) {
ui_message(op, "One or more modprobe configuration files to "
"disable Nouveau have been written. "
"For some distributions, this may be sufficient to "
"disable Nouveau; other distributions may require "
"modification of the initial ramdisk. Please reboot "
"your system and attempt NVIDIA driver installation "
"again. Note if you later wish to reenable Nouveau, "
"you will need to delete these files: %s",
blacklist_files);
nvfree(blacklist_files);
} else {
ui_warn(op, "Unable to alter the nouveau modprobe configuration. "
NOUVEAU_POINTER_MESSAGE);
}
}
/* Allow installation to continue if nouveau was not detected. */
return !nouveau_detected;
}
#define DKMS_STATUS " status"
#define DKMS_ADD " add"
#define DKMS_BUILD " build"
#define DKMS_INSTALL " install"
#define DKMS_REMOVE " remove"
/*
* Run the DKMS tool with the provided arguments. The following operations
* are supported:
*
* DKMS_STATUS:
* Check the status of the specified module.
* DKMS_ADD: requires version
* Adds the module to the DKMS database.
* DKMS_BUILD: requires version
* Builds the module against the currently running kernel.
* DKMS_INSTALL: requires version
* Installs the module for the currently running kernel.
* DKMS_REMOVE: reqires version
* Removes the module from all kernels.
*
* run_dkms returns TRUE if dkms is found and exits with status 0 when run;
* FALSE if dkms can't be found, or exits with non-0 status.
*/
static int run_dkms(Options *op, const char* verb, const char *version,
const char *kernel, char** out)
{
char *cmdline, *veropt, *kernopt = NULL, *kernopt_all = "";
const char *modopt = " -m nvidia"; /* XXX real name is in the Package */
char *output;
int ret;
/* Fail if DKMS not found */
if (!op->utils[DKMS]) {
if (strcmp(verb, DKMS_STATUS) != 0) {
ui_error(op, "Failed to find dkms on the system!");
}
return FALSE;
}
/* Convert function parameters into commandline arguments. Optional
* arguments may be NULL, in which case nvstrcat() will end early. */
veropt = version ? nvstrcat(" -v ", version, NULL) : NULL;
if (strcmp(verb, DKMS_REMOVE) == 0) {
/* Always remove DKMS modules from all kernels to avoid confusion. */
kernopt_all = " --all";
} else {
kernopt = kernel ? nvstrcat(" -k ", kernel, NULL) : NULL;
}
cmdline = nvstrcat(op->utils[DKMS], verb, modopt, veropt,
kernopt_all, kernopt, NULL);
/* Run DKMS */
ret = run_command(op, cmdline, &output, FALSE, 0, TRUE);
if (ret != 0) {
ui_error(op, "Failed to run `%s`: %s", cmdline, output);
}
nvfree(cmdline);
nvfree(veropt);
nvfree(kernopt);
if (out) {
*out = output;
} else {
nvfree(output);
}
return ret == 0;
}
/*
* Check to see whether the module is installed via DKMS.
* (The version parameter is optional: if NULL, check for any version; if
* non-NULL, check for the specified version only.)
*
* Returns TRUE if DKMS is found, and dkms commandline output is non-empty.
* Returns FALSE if DKMS not found, or dkms commandline output is empty.
*/
int dkms_module_installed(Options* op, const char *version)
{
int ret, bRet = FALSE;
char *output = NULL;
ret = run_dkms(op, DKMS_STATUS, version, NULL, &output);
if (output) bRet = strcmp("", output) != 0;
nvfree(output);
return ret && bRet;
}
/*
* Install the given version of the module for the currently running kernel
*/
int dkms_install_module(Options *op, const char *version, const char *kernel)
{
ui_status_begin(op, "Installing DKMS kernel module:", "Adding to DKMS");
if (!run_dkms(op, DKMS_ADD, version, kernel, NULL)) goto failed;
ui_status_update(op, .05, "Building module (This may take a moment)");
if (!run_dkms(op, DKMS_BUILD, version, kernel, NULL)) goto failed;
ui_status_update(op, .9, "Installing module");
if(!run_dkms(op, DKMS_INSTALL, version, kernel, NULL)) goto failed;
ui_status_end(op, "done.");
return TRUE;
failed:
ui_status_end(op, "error.");
ui_error(op, "Failed to install the kernel module through DKMS. No kernel "
"module was installed; please try installing again without "
"DKMS, or check the DKMS logs for more information.");
return FALSE;
}
/*
* Remove the given version of the module on all available kernels.
*/
int dkms_remove_module(Options *op, const char *version)
{
return run_dkms(op, DKMS_REMOVE, version, NULL, NULL);
}
/*
* Test the last bit of the given file. Return 1 if the bit is set, 0 if it is
* not set, and < 0 on error.
*
*/
static int test_last_bit(const char *file) {
char buf;
int ret, data_read = FALSE;
FILE *fp = fopen(file, "r");
if (!fp) {
return -errno;
}
/* XXX Using fseek(3) could make this more efficient for larger files, but
* trying to read after an fseek(stream, -1, SEEK_END) call on a UEFI
* variable file in sysfs hits a premature EOF. */
while(fread(&buf, 1, 1, fp)) {
data_read = TRUE;
}
if (ferror(fp)) {
ret = -ferror(fp);
} else if (data_read) {
ret = buf & 1;
} else {
ret = -EIO;
}
fclose(fp);
return ret;
}
static const char* secure_boot_files[] = {
"/sys/firmware/efi/vars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c/data",
"/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c",
};
/*
* secure_boot_enabled() - Check the known paths where secure boot status is
* exposed. If secure boot is enabled, return 1. If secure boot is disabled,
* return 0. On failure to detect whether secure boot is enabled, return < 0.
*/
int secure_boot_enabled(void) {
int i, ret = -ENOENT;
for (i = 0; i < ARRAY_LEN(secure_boot_files); i++) {
if (access(secure_boot_files[i], R_OK) == 0) {
ret = test_last_bit(secure_boot_files[i]);
if (ret >= 0) {
break;
}
}
}
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;
}
}
/*
* set_concurrency_level() - automatically determine the concurrency level,
* if the user has not specified it.
*/
void set_concurrency_level(Options *op)
{
int detected_cpus;
if (op->concurrency_level) {
ui_log(op, "Concurrency level set to %d on the command line.",
op->concurrency_level);
} else {
#if defined _SC_NPROCESSORS_ONLN
detected_cpus = sysconf(_SC_NPROCESSORS_ONLN);
if (detected_cpus >= 1) {
ui_log(op, "Detected %d CPUs online; setting concurrency level "
"to %d.", detected_cpus, detected_cpus);
} else
#else
#warning _SC_NPROCESSORS_ONLN not defined; nvidia-installer will not be able \
to detect the number of processors.
#endif
{
ui_log(op, "Unable to detect the number of processors: setting "
"concurrency level to 1.");
detected_cpus = 1;
}
op->concurrency_level = detected_cpus;
}
if (op->expert) {
int val = op->concurrency_level;
do {
char *strval = nvasprintf("%d", val);
val = atoi(ui_get_input(op, strval, "Concurrency level"));
nvfree(strval);
} while (val < 1);
op->concurrency_level = val;
}
}