/* Remove a module from a running kernel. Copyright 1996, 1997 Linux International. New implementation contributed by Richard Henderson <rth@tamu.edu> Based on original work by Bjorn Eckwall <bj0rn@blox.se> 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: rmmod.c,v 1.1.1.1 1998/01/06 20:51:07 ewt Exp $" #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include "module.h" #include "util.h" #include "version.h" #include "logger.h" /*======================================================================*/ #define WANT_TO_REMOVE 1 #define CAN_REMOVE 2 struct extmodule { char *name; struct extmodule **refs; int nrefs; int status; /* WANT_TO_REMOVE | CAN_REMOVE */ }; static struct extmodule *modules; static size_t nmodules; /* If we don't have query_module ... */ static int old_get_modules(void) { int fd, nmod, len, bufsize; char *buffer, *p; struct extmodule *mod; /* Read the module information from /proc in one go. */ if ((fd = open("/proc/modules", O_RDONLY)) < 0) { error("/proc/modules: %m"); return 0; } buffer = xmalloc(bufsize = 8*1024); retry_read: len = read(fd, buffer, bufsize); if (len < 0) { error("/proc/modules: %m"); return 0; } else if (len == sizeof(buffer)) { lseek(fd, 0, SEEK_SET); buffer = xrealloc(buffer, bufsize *= 2); goto retry_read; } close(fd); /* Break the buffer into lines and deal with them individually. */ mod = NULL; nmod = 0; buffer[len+1] = '\0'; p = buffer; while (p < buffer+len) { char *e; struct extmodule *m; mod = xrealloc(mod, ++nmod * sizeof(struct extmodule)); m = &mod[nmod-1]; m->refs = NULL; m->nrefs = 0; m->status = 0; /* Save the end of the line. */ e = strchr(p, '\n'); *e = '\0'; /* Save the module name. */ m->name = p; p = strchr(p, ' '); *p = '\0'; if ((p = strchr(p+1, '[')) != NULL) { *strrchr(++p, ']') = '\0'; for (p = strtok(p, " "); p ; p = strtok(NULL, " ")) { struct extmodule *rm; int i; for (i = 0, rm = mod; i < nmod; ++i, ++rm) if (strcmp(rm->name, p) == 0) goto found_ref; error("strange module reference: %s used by %s?\n", m->name, p); return 0; found_ref: m->refs = xrealloc(m->refs, ++m->nrefs * sizeof(struct extmodule *)); m->refs[m->nrefs-1] = rm; } } p = e+1; } modules = mod; nmodules = nmod; return 1; } /* If we do have query_module ... */ static int new_get_modules(void) { char *module_names, *mn, *refs; struct extmodule *mod, *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; mod = xmalloc(nmod * sizeof(struct extmodule)); memset(mod, 0, nmod * sizeof(struct extmodule)); for (i = 0, mn = module_names, m = mod; i < nmod; ++i, mn += strlen(mn)+1, ++m) m->name = mn; /* Fetch the module references. */ refs = xmalloc(bufsize = 1024); for (i = 0, m = mod; i < nmod; ++i, ++m) { size_t j, nrefs; char *r; retry_ref_load: if (query_module(m->name, QM_REFS, refs, bufsize, &ret)) { if (errno == ENOSPC) { refs = xrealloc(refs, bufsize = ret); goto retry_ref_load; } error("QM_REFS: %m"); return 0; } m->nrefs = nrefs = ret; m->refs = xmalloc(nrefs * sizeof(struct extmodule *)); for (j = 0, r = refs; j < ret; ++j, r += strlen(r)+1) { struct extmodule *rm; size_t k; for (k = 0, rm = mod; k < nmod; ++k, ++rm) if (strcmp(rm->name, r) == 0) goto found_ref; error("strange module reference: %s used by %s?\n", m->name, r); return 0; found_ref: m->refs[j] = rm; } } free(refs); modules = mod; nmodules = nmod; return 1; } int main(int argc, char **argv) { int i, j, ret = 0; error_file = "rmmod"; while ((i = getopt(argc, argv, "asV")) != EOF) switch (i) { case 'a': /* Remove all unused modules and stacks. */ if (delete_module(NULL)) { perror("rmmod"); return 1; } return 0; case 's': /* Start syslogging. */ setsyslog("rmmod"); break; case 'V': fputs("rmmod version " MODUTILS_VERSION "\n", stderr); break; default: usage: fputs("Usage: rmmod [-a] [-s] module ...\n", stderr); return 1; } if (optind >= argc) goto usage; /* Fetch all of the currently loaded modules and their dependencies. */ if (query_module(NULL, 0, NULL, 0, NULL) == 0 ? !new_get_modules() : !old_get_modules()) return 1; /* Find out which ones we want to remove. */ for (i = optind; i < argc; ++i) { for (j = 0; j < nmodules; ++j) if (strcmp(modules[j].name, argv[i]) == 0) goto found_module; error("module %s not loaded", argv[i]); ret = 1; continue; found_module: modules[j].status |= WANT_TO_REMOVE; } if (ret) return ret; /* Remove them if we can. */ for (i = 0; i < nmodules ; ++i) { struct extmodule *m = &modules[i]; if (m->nrefs == 0 && m->status == WANT_TO_REMOVE) m->status |= CAN_REMOVE; for (j = 0; j < m->nrefs; ++j) { struct extmodule *r = m->refs[j]; switch (r->status) { case CAN_REMOVE: case WANT_TO_REMOVE | CAN_REMOVE: break; case WANT_TO_REMOVE: if (r->nrefs == 0) break; default: m->status &= ~CAN_REMOVE; break; } } switch (m->status) { case CAN_REMOVE: case WANT_TO_REMOVE | CAN_REMOVE: if (delete_module(m->name) < 0) { error("%s: %m", m->name); ret = 1; } break; case WANT_TO_REMOVE: error("%s is in use", m->name); ret = 1; break; } } return ret; }