home *** CD-ROM | disk | FTP | other *** search
- /* Copyright 1997 Free Software Foundation, Inc.
- Contributed by Marcin Dalecki <dalecki@sub994.sub.uni-goettingen.de>
-
- This file is part of the Linux modutils.
-
- 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. */
-
- #ident "$Id: modprobe.c,v 1.1.1.1 1998/01/06 20:51:07 ewt Exp $"
-
- #include <stdio.h>
- #include <stdarg.h>
- #include <string.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <sys/param.h>
- #include <getopt.h>
- #include <sys/stat.h>
-
- #include "module.h"
- #include "util.h"
-
- #include "misc.h"
- #include "conf_file.h"
-
- /*
- * This is the actual modprobe specific part.
- *
- * The general convention throughout this file is that functions ALWAYS
- * return 0 on success and some different value otherwise.
- */
-
- struct dep_node
- {
- struct dep_node *next; /* modules */
- struct dep_node *deps; /* dependences */
- char *name;
- };
-
- /*
- * Command line flags
- */
- static char flag_verbose = 0;
- static int flag_by_kerneld = 0;
-
- #ifndef NO_COMPAT
- static int flag_new_syscalls = 0;
- #endif
-
- /*
- * Dependence information from the configuration file.
- */
- static struct dep_node *file_deps = NULL;
-
- static void
- verbose (const char *ctl,...)
- {
- if (flag_verbose)
- {
- va_list list;
- va_start (list, ctl);
- vprintf (ctl, list);
- va_end (list);
- fflush (stdout);
- }
- }
-
- /*
- * Free the memory associated with an dependency list
- */
- static void
- deps_free (struct dep_node *node)
- {
- if (!node)
- return;
-
- free (node->name);
- deps_free (node->next);
- deps_free (node->deps);
- free (node);
- }
-
- static struct dep_node *
- dep_lookup (struct dep_node *a, const char *name)
- {
- if (!a)
- return NULL;
-
- if (strcmp (name, a->name) == 0)
- return a;
-
- return dep_lookup (a->next, name);
- }
-
- static struct dep_node *
- dep_add (struct dep_node *node, const char *name)
- {
- struct dep_node *tmp = (struct dep_node *)
- xmalloc (sizeof (struct dep_node));
-
- tmp->name = xstrdup (name);
- tmp->deps = NULL;
- tmp->next = node;
-
- return tmp;
- }
-
- /*
- * Check if a given module is already in the kernel space.
- */
-
- #ifndef NO_COMPAT
-
- static int
- old_in_kernel (char *mod)
- {
- FILE *f;
- char linebuf[256], *c;
-
- if (!(f = fopen ("/proc/modules", "r")))
- error ("/proc/modules: %m");
-
- while (fgets (linebuf, 256, f))
- {
- c = strchr (linebuf, ' ');
- if (!c)
- error ("/proc/modules: Syntax error");
- *c = 0;
- if (!strcmp (mod, linebuf))
- {
- fclose (f);
- c = strchr (c + 1, '\t');
- if (!c || c[1] != '[')
- return 1;
- else
- return 2;
- }
- }
-
- fclose (f);
- return 0;
- }
-
- #endif
-
- static int
- new_in_kernel (char *mod)
- {
- char *module_names, *m;
- size_t bufsize, ret, nmod, i;
-
- /* Fetch the list of modules. */
-
- module_names = xmalloc (bufsize = 1024);
- retry_mod_load:
- if (query_module (NULL, QM_MODULES, module_names, bufsize, &ret))
- {
- if (errno == ENOSPC)
- {
- module_names = xrealloc (module_names, bufsize = ret);
- goto retry_mod_load;
- }
- error ("QM_MODULES: %m");
- return 0;
- }
- nmod = ret;
-
- for (i = 0, m = module_names; i < nmod; ++i, m += strlen (m) + 1)
- if (!strcmp (m, mod))
- {
- free (mod);
- free (module_names);
- return 1;
- }
-
- free (mod);
- free (module_names);
- return 0;
- }
-
- static int
- in_kernel (char *mod)
- {
- mod = strip_o (mod);
- #ifndef NO_COMPAT
- if (!flag_new_syscalls)
- return old_in_kernel (mod);
- else
- #endif
- return new_in_kernel (mod);
- }
-
- /*
- * Read the dependancy file.
- */
- static int
- read_deps_file (const char *cfgfile)
- {
- int line = 0;
- char *buf;
- char *end;
- char *tmp;
- char linebuf [8192];
-
- if (!(buf = read_and_preprocess_file (cfgfile)))
- return 1;
-
- /*
- * And now parse the buffer.
- */
- for (tmp = buf; (end = get_concat_line (tmp, &line)); tmp = end)
- {
- char *cp;
-
- if (!*tmp) /* ignore blank lines */
- continue;
-
- tmp = resolve_string (tmp, linebuf, sizeof (linebuf));
-
- if (!(cp = strchr (tmp, ':')))
- {
- free (buf);
- error (":%d:parsing error in dependeny file\n", line);
- return 1; /* parsing error */
- }
- if ((cp > tmp) && *(cp - 1) == ' ')
- *(cp - 1) = '\0';
- *cp = '\0';
-
- /* name found */
- file_deps = dep_add (file_deps, tmp);
-
- tmp = cp + 1;
- while ((cp = strrchr (tmp, ' ')))
- {
- *cp = '\0';
- file_deps->deps = dep_add (file_deps->deps, cp + 1);
- }
- }
- free (buf);
- return 0;
- }
-
- static int
- exec_rmmod_cmd (char *mod)
- {
- int ret = 0;
- char *ex;
- mod = strip_o (mod);
-
- if ((ex = find_assoc_cmd (PRE_REMOVE, mod)) != NULL)
- if (system (ex))
- lprintf ("pre-remove %s failed\n", mod);
-
- if ((ex = find_assoc_cmd (REMOVE, mod)) != NULL)
- {
- if ((ret = system (ex)))
- lprintf ("remove %s failed\n", mod);
- }
- else if ((ret = delete_module (mod)) < 0)
- {
- ret = 1;
- perror (mod);
- }
-
- if (!ret && (ex = find_assoc_cmd (POST_REMOVE, mod)) != NULL)
- {
- if (system (ex) != 0)
- lprintf ("post-remove %s failed\n", mod);
- }
-
- free (mod);
- return ret;
- }
-
- /*
- * Unload all sub-modules in reverse order.
- */
- static int
- rm_sub_mods (struct dep_node *nod)
- {
- int ret = 0;
- if (nod != NULL)
- if (!(ret = rm_sub_mods (nod->next)))
- ret = exec_rmmod_cmd (nod->name);
-
- return ret;
- }
-
- /*
- * Return the options associated with a module.
- * Return NULL if there are none.
- */
- static char *
- any_options (char *mod)
- {
- char *modname = strip_o (mod);
- struct mod_option *opts;
-
- for (opts = mod_options; opts; opts = opts->next)
- if (!strcmp (opts->module, modname))
- return opts->args;
-
- return NULL;
- }
-
- int
- may_unload(char *mod)
- {
- char *modname = strip_o (mod);
- struct mod_option *opts;
-
- for (opts = mod_options; opts; opts = opts->next)
- if (!strcmp (opts->module, modname))
- return opts->may_unload;
-
- return 1;
- }
-
- /*
- * Try to load a module and the sub-modules it needs.
- */
- static int
- insmod (char *mod, struct dep_node **newin_kernel, char *options[])
- {
- int err = 0;
- struct dep_node *dep;
- struct dep_node *nod;
- char *load_cmd;
- int cmd_len;
- char *op;
- char *ex;
- int i;
-
- if (!mod)
- return 0;
-
- if (in_kernel (mod))
- return 0;
-
- if (!(nod = dep_lookup (file_deps, mod)))
- {
- lprintf ("no dependency information for module: \"%s\"", mod);
- return 1;
- }
-
- dep = nod->deps;
- while (dep && !err)
- {
- err = insmod (dep->name, newin_kernel, NULL);
- dep = dep->next;
- }
-
- if (err)
- {
- rm_sub_mods (nod->deps); /* revert everything */
- return 1;
- }
-
- /*
- * First determine the length of the command we will use.
- */
- cmd_len = 64 + strlen (mod);
- if (options && options[1] && strchr (options[1], '='))
- for (i = 1; options[i]; ++i)
- if (strchr (options[i], '='))
- cmd_len += strlen (options[i]) + 2;
- else
- break;
- else
- {
- if ((op = any_options (mod)))
- cmd_len += strlen (op) + 2;
- if (options && options[0] && (op = any_options (options[0])))
- cmd_len += strlen (op) + 2;
- }
-
- /*
- * And now actually compose the command!
- */
- load_cmd = (char *) xmalloc (cmd_len);
-
- strcpy (load_cmd, "/sbin/insmod ");
- if (flag_by_kerneld && may_unload(mod))
- strcat (load_cmd, "-k ");
- if (log)
- strcat (load_cmd, "-s ");
- if (insmod_opts)
- {
- strcat (load_cmd, insmod_opts);
- strcat (load_cmd, " ");
- }
- strcat (load_cmd, mod);
-
- if (options && options[1] && strchr (options[1], '='))
- for (i = 1; options[i]; ++i)
- {
- if (strchr (options[i], '='))
- {
- strcat (load_cmd, " ");
- strcat (load_cmd, options[i]);
- }
- else
- break;
- }
- else
- {
- if ((op = any_options (mod)))
- {
- strcat (load_cmd, " ");
- strcat (load_cmd, op);
- }
- if (options && options[0] &&
- (op = any_options (options[0])))
- {
- strcat (load_cmd, " ");
- strcat (load_cmd, op);
- }
- }
- verbose ("\r%s\n\t\t", load_cmd);
-
- if ((ex = find_assoc_cmd (PRE_INSTALL, mod)) != NULL)
- if ((err = system (ex)) != 0)
- lprintf ("pre-install %s failed\n", mod);
-
- if (!err)
- {
- if ((ex = find_assoc_cmd (INSTALL, mod)) != NULL)
- err = system (ex);
- else
- err = system (load_cmd);
- }
-
- if (!err && (ex = find_assoc_cmd (POST_INSTALL, mod)) != NULL)
- if ((err = system (ex)) != 0)
- lprintf ("post-install %s failed\n", mod);
-
- free (load_cmd);
-
- if (err)
- {
- rm_sub_mods (nod->deps);
- return 1;
- }
-
- *newin_kernel = dep_add (*newin_kernel, mod);
-
- return 0;
- }
-
- /*
- * Check if a module is referenced by something else
- */
-
- static int
- is_removable (char *mod)
- {
- mod = strip_o (mod);
-
- #ifndef NO_COMPAT
- if (!flag_new_syscalls)
- return (old_in_kernel (mod) == 1);
- else
- #endif
- {
- size_t ret;
-
- if (!in_kernel (mod))
- return 0;
- query_module (mod, QM_REFS, NULL, 0, &ret);
- return ret == 0;
- }
- }
-
- /*
- * Unload a module and whichever modules where required by this module.
- */
- static int
- unload (char *mod)
- {
- int ret = 0;
- struct mod_path *objs = NULL;
- struct mod_path *tmp;
-
- /*
- * Ignore if the module doesn't exist or it's used by someone else.
- */
-
- if (!mod || !is_removable (mod))
- return 0;
-
- /*
- * If there is no information about a module in
- * the dependancy file, we remove it without further checking.
- */
- if (!(objs = locate_mod_obj (mod, NULL)) || objs == (void *) (-1))
- return delete_module (strip_o (mod));
-
- /*
- * Otherwise we try to kill all instantations of it.
- */
- ret = 0;
- for (tmp = objs; tmp; tmp = tmp->next)
- if (in_kernel (tmp->path))
- {
- struct dep_node *nod;
-
- if (!(nod = dep_lookup (file_deps, tmp->path)))
- lprintf ("no dependency information for module %s", mod);
- else
- {
- struct dep_node *deps;
-
- ret = exec_rmmod_cmd (tmp->path);
-
- for (deps = nod->deps; deps; deps = deps->next)
- unload (deps->name);
- }
- }
-
- while ((tmp = objs))
- {
- objs = objs->next;
- free (tmp->path);
- free (tmp);
- }
- return 0;
- }
-
- /*
- * Load modules specified on the command line
- */
- static int
- load_from_list (char *list[], int n, char *type, int loadall)
- {
- int ret = 1;
- int i;
-
- for (i = 0; i < n; i++)
- {
- struct dep_node *kernel_deps = NULL;
-
- /*
- * We can pass options to the module via modprobe's command line.
- * It goes like this:
- * /sbin/modprobe module opt1=value opt2=value [ othermodule ...]
- * An option is a keyword followed by an equal sign and a value.
- * No spaces are allowed in the sequence, unless it is quoted.
- *
- * The option list ends at the end of the list or at the
- * first non-option argument (a module).
- */
- if (strchr (list[i], '=') == NULL)
- {
- struct mod_path *objs = NULL;
- struct mod_path *tmp;
-
- objs = locate_mod_obj (list[i], type);
- if (!objs)
- lprintf ("can't locate module %s", list[i]);
- else if (objs != (void *) (-1))
- {
- for (tmp = objs; tmp; tmp = tmp->next)
- {
- if (insmod (tmp->path, &kernel_deps, &list[i]) == 0)
- {
- ret = 0;
- if (!loadall) /* stop after fist success */
- break;
- }
- }
- while ((tmp = objs))
- {
- objs = objs->next;
- free (tmp->path);
- free (tmp);
- }
- }
- }
-
- deps_free (kernel_deps);
- kernel_deps = NULL;
-
- if (ret == 0 && !loadall)
- break;
- }
-
- return ret;
- }
-
- /*
- * Print all available modules matching "pattern" and of a certain type.
- */
- static void
- print_list (const char *pattern, const char *type)
- {
- struct mod_path *mods = NULL;
- struct mod_path *tmp;
-
- mods = find_matching_mods (pattern, type, 1);
- while ((tmp = mods))
- {
- printf ("%s\n", mods->path);
- mods = mods->next;
- free (tmp->path);
- free (tmp);
- }
- }
-
- /*
- * Print usage information and exit.
- */
- static void
- usage (void)
- {
- printf ("Usage: modprobe [-a] [ -t TYPE ] MODULE [opt=val ...] ...\n"
- " modprobe -c\n"
- "Load MODULE_1 MODULE_2, and modules needed by them\n"
- "with the specified options.\n\n"
- " -a, --all load all modules\n"
- " -c, --show-conf show the current modules configuration and exit\n"
- " -d, --debug run in debug mode\n"
- " -k, --kernel-daemon only used by the kernel daemon\n"
- " -l, --list list currently available modules\n"
- " -r, --remove unload modules from the kernel\n"
- " -s, --system-log use the system logging for error reporting\n"
- " -t TYPE,\n"
- " -type TYPE restrict actions to modules of the TYPE\n"
- " --help display this help and exit\n"
- " -v, --verbose run in verbose mode\n"
- " -V, --version output version information and exit\n"
- "\n"
- );
- }
-
- static void
- nothing (const char *str)
- {
- lprintf ("argument missing for %s\n"
- "Please specify at least one module or a wildcard like \\*.", str);
- }
-
-
- int
- main (int argc, char *argv[])
- {
- int ret = 0;
- char *type = NULL; /* Search in all path[] */
-
- int flag_remove = 0;
- int flag_list = 0;
- int flag_load_all = 0; /* Load only one module out of a list */
-
- int opt_tag;
-
- if (argc == 1)
- {
- usage ();
- return 1;
- }
-
- if (read_config_file (NULL))
- return 1;
-
- if (read_deps_file (depfile))
- return 1;
-
- /*
- * Yes we are using getopts!
- */
- while (1)
- {
- static struct option long_opts[] =
- {
- {"all", 0, 0, 'a'},
- {"show-conf", 0, 0, 'c'},
- {"debug", 0, 0, 'd'},
- {"kernel-daemon", 0, 0, 'k'},
- {"list", 0, 0, 'l'},
- {"remove", 0, 0, 'r'},
- {"system-log", 0, 0, 's'},
- {"type", 1, 0, 't'},
- {"verbose", 0, 0, 'v'},
- {"version", 0, 0, 'V'},
- {"help", 0, 0, 'h'},
- {0, 0, 0, 0} /* Table end tag */
- };
- int opt_ind = 0;
-
- opt_tag = getopt_long (argc, argv, "acklrst:vV", long_opts,
- &opt_ind);
- if (opt_tag == -1)
- break;
-
- switch (opt_tag)
- {
- case 'a':
- flag_load_all = 1;
- break;
-
- case 'c':
- if (argc != 2)
- { /* allow it only beeing used exclusive */
- usage ();
- exit (1);
- }
- print_active_config (); /* show configuration */
- exit (0);
-
- case 'd':
- flag_debug = 1;
- break;
-
- case 'k':
- flag_by_kerneld = 1; /* called by kernel daemon */
- break;
-
- case 'l':
- flag_list = 1; /* list modules of certain kind */
- break;
-
- case 'r':
- flag_remove = 1;
- break;
-
- case 's':
- setsyslog ("modprobe"); /* use the syslog for reporting */
- break;
-
- case 't':
- type = xstrdup (optarg);
- break;
-
- case 'v':
- flag_verbose = 1; /* give terse informations during run */
- break;
-
- case '?':
- case 'h':
- usage ();
- exit (opt_tag == 'h' ? 0 : 1);
- break;
-
- case 'V':
- puts ("modprobe (Linux modutils) " MODUTILS_VERSION);
- if (argc != 2)
- putchar ('\n');
- break;
-
- default:
- abort ();
- }
- }
-
- if (ret == -1)
- return 1;
-
- /*
- * Skip all automatically processed options.
- */
- argc -= optind;
- argv += optind;
-
- /*
- * argv now points to the first non-option argument
- * argc is the remaining argument count
- */
-
- #ifndef NO_COMPAT
- flag_new_syscalls = !query_module (NULL, 0, NULL, 0, NULL);
- #endif
-
- if (flag_remove)
- {
- if (argc > 0)
- for (; argc > 0 && ret == 0; ++argv, --argc)
- ret = unload (*argv);
- else
- nothing ("remove");
- }
- else if (flag_list)
- {
- if (argc > 0)
- for (; argc > 0 && ret == 0; ++argv, --argc)
- print_list (*argv, type);
- else
- print_list ("*", type);
- }
- else
- {
- if (argc > 0)
- ret = load_from_list (argv, argc, type, flag_load_all);
- else
- nothing ("load");
- }
-
- verbose ("\r");
- return ret;
- }
-