aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'loader/modules.c')
-rw-r--r--loader/modules.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/loader/modules.c b/loader/modules.c
new file mode 100644
index 0000000..0944b97
--- /dev/null
+++ b/loader/modules.c
@@ -0,0 +1,411 @@
+/*
+ * modules.c - module loading functionality
+ *
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ * 2008, 2009 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Erik Troan <ewt@redhat.com>
+ * Matt Wilson <msw@redhat.com>
+ * Michael Fulbright <msf@redhat.com>
+ * Jeremy Katz <katzj@redhat.com>
+ * David Cantrell <dcantrell@redhat.com>
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <newt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "../isys/log.h"
+
+#include "loader.h"
+#include "modules.h"
+#include "windows.h"
+
+/* boot flags */
+extern uint64_t flags;
+
+static GSList *modopts = NULL;
+static GSList *blacklist = NULL;
+
+static gboolean _isValidModule(gchar *module) {
+ gint fd = -1, i = 0;
+ gchar *path = NULL, *buf = NULL, *modname = NULL;
+ gchar *ends[] = { ".ko.gz:", ".ko:", NULL };
+ struct utsname utsbuf;
+ struct stat sbuf;
+
+ if (uname(&utsbuf) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ return FALSE;
+ }
+
+ if (asprintf(&path, "/lib/modules/%s/modules.dep", utsbuf.release) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ return FALSE;
+ }
+
+ if (stat(path, &sbuf) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ free(path);
+ return FALSE;
+ }
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ free(path);
+ return FALSE;
+ } else {
+ free(path);
+ }
+
+ buf = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!buf || buf == MAP_FAILED) {
+ close(fd);
+ return FALSE;
+ }
+
+ close(fd);
+
+ while (ends[i] != NULL) {
+ if (asprintf(&modname, "/%s%s", module, ends[i]) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ return FALSE;
+ }
+
+ if (g_strstr_len(buf, -1, modname) != NULL) {
+ munmap(buf, sbuf.st_size);
+ free(modname);
+ return TRUE;
+ }
+
+ free(modname);
+ modname = NULL;
+ i++;
+ }
+
+ munmap(buf, sbuf.st_size);
+ return FALSE;
+}
+
+static void _addOption(const gchar *module, const gchar *option) {
+ gboolean found = FALSE;
+ GSList *iterator = modopts;
+ module_t *modopt = NULL;
+ gchar *tmpopt = g_strdup(option);
+
+ while (iterator != NULL) {
+ modopt = (module_t *) iterator->data;
+
+ if (!strncmp(modopt->name, module, strlen(modopt->name))) {
+ found = TRUE;
+ break;
+ }
+
+ iterator = g_slist_next(iterator);
+ }
+
+ if (found) {
+ modopt->options = g_slist_append(modopt->options, tmpopt);
+ } else {
+ if ((modopt = g_malloc0(sizeof(module_t))) == NULL) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ abort();
+ }
+
+ modopt->name = g_strdup(module);
+ modopt->options = NULL;
+ modopt->options = g_slist_append(modopt->options, tmpopt);
+ modopts = g_slist_append(modopts, modopt);
+ }
+
+ return;
+}
+
+static gboolean _writeModulesConf(gchar *conf) {
+ gint fd = -1, rc = 0, len = 0;
+ GSList *iterator = modopts;
+ GString *buf = g_string_new("# Module options and blacklists written by anaconda\n");
+
+ if (conf == NULL) {
+ /* XXX: should this use mkstemp() ? */
+ conf = "/tmp/modprobe.conf";
+ }
+
+ if ((fd = open(conf, O_WRONLY | O_CREAT, 0644)) == -1) {
+ logMessage(ERROR, "error opening to %s: %m", conf);
+ return FALSE;
+ }
+
+ while (iterator != NULL) {
+ module_t *modopt = iterator->data;
+ GSList *optiterator = modopt->options;
+ g_string_append_printf(buf, "options %s", modopt->name);
+
+ while (optiterator != NULL) {
+ gchar *option = (gchar *) optiterator->data;
+ g_string_append_printf(buf, " %s", option);
+ optiterator = g_slist_next(optiterator);
+ }
+
+ g_string_append(buf, "\n");
+ iterator = g_slist_next(iterator);
+ }
+
+ iterator = blacklist;
+
+ while (iterator != NULL) {
+ gchar *module = (gchar *) iterator->data;
+ g_string_append_printf(buf, "blacklist %s\n", module);
+ iterator = g_slist_next(iterator);
+ }
+
+ len = buf->len;
+ rc = write(fd, buf->str, len);
+ close(fd);
+ g_string_free(buf, TRUE);
+
+ return (rc == len);
+}
+
+static gboolean _doLoadModule(const gchar *module, gchar **args) {
+ gint child;
+ gint status;
+
+ if (!(child = fork())) {
+ gint i, rc;
+ gchar **argv = NULL;
+ gint fd = -1;
+
+ if ((argv = g_malloc0(3 * sizeof(*argv))) == NULL) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ abort();
+ }
+
+ if ((fd = open("/dev/tty3", O_RDWR)) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ } else {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+ }
+
+ argv[0] = "/sbin/modprobe";
+ argv[1] = g_strdup(module);
+ argv[2] = NULL;
+
+ if (args) {
+ for (i = 0; args[i] ; i++) {
+ _addOption(module, args[i]);
+ }
+ _writeModulesConf(MODULES_CONF);
+ }
+
+ rc = execv("/sbin/modprobe", argv);
+ g_strfreev(argv);
+ _exit(rc);
+ }
+
+ waitpid(child, &status, 0);
+
+ if (!WIFEXITED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+gboolean mlInitModuleConfig(void) {
+ gint i = 0;
+ gchar *cmdline = NULL;
+ gchar **options = NULL;
+ GError *readErr = NULL;
+
+ /* read module options out of /proc/cmdline and into a structure */
+ if (!g_file_get_contents("/proc/cmdline", &cmdline, NULL, &readErr)) {
+ logMessage(ERROR, "unable to read /proc/cmdline: %s", readErr->message);
+ g_error_free(readErr);
+ return _writeModulesConf(MODULES_CONF);
+ }
+
+ cmdline = g_strchomp(cmdline);
+ options = g_strsplit(cmdline, " ", 0);
+ g_free(cmdline);
+
+ if (options == NULL) {
+ return _writeModulesConf(MODULES_CONF);
+ }
+
+ while (options[i] != NULL) {
+ gchar *tmpmod = NULL;
+ gchar **fields = NULL;
+
+ if (g_strstr_len(options[i], -1, "=") == NULL) {
+ i++;
+ continue;
+ }
+
+ if (!strncmp(options[i], "blacklist=", 10)) {
+ if ((fields = g_strsplit(options[i], "=", 0)) != NULL) {
+ if (g_strv_length(fields) == 2) {
+ tmpmod = g_strdup(fields[1]);
+ blacklist = g_slist_append(blacklist, tmpmod);
+ }
+ }
+ } else if ((fields = g_strsplit(options[i], ".", 0)) != NULL) {
+ if (g_strv_length(fields) == 2) {
+ if (_isValidModule(fields[0])) {
+ _addOption(fields[0], fields[1]);
+ }
+ }
+ }
+
+ if (fields != NULL) {
+ g_strfreev(fields);
+ }
+
+ i++;
+ }
+
+ if (options != NULL) {
+ g_strfreev(options);
+ }
+
+ return _writeModulesConf(MODULES_CONF);
+}
+
+/* load a module with a given list of arguments */
+gboolean mlLoadModule(const gchar *module, gchar **args) {
+ return _doLoadModule(module, args);
+}
+
+/* loads a : separated list of modules */
+gboolean mlLoadModuleSet(const gchar *modNames) {
+ gchar **mods = NULL, **iterator = NULL;
+ gboolean rc = FALSE;
+
+ if (modNames == NULL) {
+ return FALSE;
+ }
+
+ if ((mods = g_strsplit(modNames, ":", 0)) != NULL) {
+ iterator = mods;
+
+ while (*iterator != NULL) {
+ rc |= _doLoadModule(*iterator, NULL);
+ iterator++;
+ }
+ } else {
+ return FALSE;
+ }
+
+ g_strfreev(mods);
+ return rc;
+}
+
+gboolean mlAddBlacklist(gchar *module) {
+ gchar *tmpmod = NULL;
+
+ if (module == NULL) {
+ return FALSE;
+ }
+
+ tmpmod = g_strdup(module);
+ blacklist = g_slist_append(blacklist, tmpmod);
+ return _writeModulesConf(MODULES_CONF);
+}
+
+gboolean mlRemoveBlacklist(gchar *module) {
+ GSList *iterator = blacklist;
+
+ if (module == NULL) {
+ return FALSE;
+ }
+
+ while (iterator != NULL) {
+ if (!strcmp((gchar *) iterator->data, module)) {
+ iterator = g_slist_delete_link(blacklist, iterator);
+ continue;
+ } else {
+ iterator = g_slist_next(iterator);
+ }
+ }
+
+ return TRUE;
+}
+
+void loadKickstartModule(struct loaderData_s * loaderData,
+ int argc, char **argv) {
+ gchar *opts = NULL;
+ gchar *module = NULL;
+ gchar **args = NULL, **remaining = NULL;
+ gboolean rc;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksDeviceOptions[] = {
+ { "opts", 0, 0, G_OPTION_ARG_STRING, &opts, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining,
+ NULL, NULL },
+ { NULL },
+ };
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksDeviceOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to device kickstart method "
+ "command: %s"), optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ return;
+ }
+
+ g_option_context_free(optCon);
+
+ if ((remaining != NULL) && (g_strv_length(remaining) == 1)) {
+ module = remaining[0];
+ }
+
+ if (!module) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("A module name must be specified for "
+ "the kickstart device command."));
+ return;
+ }
+
+ if (opts) {
+ args = g_strsplit(opts, " ", 0);
+ }
+
+ rc = mlLoadModule(module, args);
+ g_strfreev(args);
+ return;
+}