mirror of
https://github.com/AuxXxilium/kmod.git
synced 2024-11-23 15:00:52 +07:00
b5a2cd070d
Drop the lengthy license from each file and just use SPDX like most projects nowadays. This doesn't have any change to license, just how they are recorded in each file. This follows the kernel approach: header files use '/*' for comments while .c files use '//'. For .m4, use "#". Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com> Link: https://lore.kernel.org/r/20240723185921.1005569-2-lucas.de.marchi@gmail.com Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
192 lines
4.0 KiB
C
192 lines
4.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* kmod-rmmod - remove modules from linux kernel using libkmod.
|
|
*
|
|
* Copyright (C) 2011-2013 ProFUSION embedded systems
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <shared/macro.h>
|
|
|
|
#include <libkmod/libkmod.h>
|
|
|
|
#include "kmod.h"
|
|
|
|
#define DEFAULT_VERBOSE LOG_ERR
|
|
static int verbose = DEFAULT_VERBOSE;
|
|
static int use_syslog;
|
|
|
|
static const char cmdopts_s[] = "fsvVwh";
|
|
static const struct option cmdopts[] = {
|
|
{"force", no_argument, 0, 'f'},
|
|
{"syslog", no_argument, 0, 's'},
|
|
{"verbose", no_argument, 0, 'v'},
|
|
{"version", no_argument, 0, 'V'},
|
|
{"help", no_argument, 0, 'h'},
|
|
{NULL, 0, 0, 0}
|
|
};
|
|
|
|
static void help(void)
|
|
{
|
|
printf("Usage:\n"
|
|
"\t%s [options] modulename ...\n"
|
|
"Options:\n"
|
|
"\t-f, --force forces a module unload and may crash your\n"
|
|
"\t machine. This requires Forced Module Removal\n"
|
|
"\t option in your kernel. DANGEROUS\n"
|
|
"\t-s, --syslog print to syslog, not stderr\n"
|
|
"\t-v, --verbose enables more messages\n"
|
|
"\t-V, --version show version\n"
|
|
"\t-h, --help show this help\n",
|
|
program_invocation_short_name);
|
|
}
|
|
|
|
static int check_module_inuse(struct kmod_module *mod) {
|
|
struct kmod_list *holders;
|
|
int state, ret;
|
|
|
|
state = kmod_module_get_initstate(mod);
|
|
|
|
if (state == KMOD_MODULE_BUILTIN) {
|
|
ERR("Module %s is builtin.\n", kmod_module_get_name(mod));
|
|
return -ENOENT;
|
|
} else if (state < 0) {
|
|
ERR("Module %s is not currently loaded\n",
|
|
kmod_module_get_name(mod));
|
|
return -ENOENT;
|
|
}
|
|
|
|
holders = kmod_module_get_holders(mod);
|
|
if (holders != NULL) {
|
|
struct kmod_list *itr;
|
|
|
|
ERR("Module %s is in use by:", kmod_module_get_name(mod));
|
|
|
|
kmod_list_foreach(itr, holders) {
|
|
struct kmod_module *hm = kmod_module_get_module(itr);
|
|
fprintf(stderr, " %s", kmod_module_get_name(hm));
|
|
kmod_module_unref(hm);
|
|
}
|
|
fputc('\n', stderr);
|
|
|
|
kmod_module_unref_list(holders);
|
|
return -EBUSY;
|
|
}
|
|
|
|
ret = kmod_module_get_refcnt(mod);
|
|
if (ret > 0) {
|
|
ERR("Module %s is in use\n", kmod_module_get_name(mod));
|
|
return -EBUSY;
|
|
} else if (ret == -ENOENT) {
|
|
ERR("Module unloading is not supported\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int do_rmmod(int argc, char *argv[])
|
|
{
|
|
struct kmod_ctx *ctx;
|
|
const char *null_config = NULL;
|
|
int flags = 0;
|
|
int i, err, r = 0;
|
|
|
|
for (;;) {
|
|
int c, idx = 0;
|
|
c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
|
|
if (c == -1)
|
|
break;
|
|
switch (c) {
|
|
case 'f':
|
|
flags |= KMOD_REMOVE_FORCE;
|
|
break;
|
|
case 's':
|
|
use_syslog = 1;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 'h':
|
|
help();
|
|
return EXIT_SUCCESS;
|
|
case 'V':
|
|
puts(PACKAGE " version " VERSION);
|
|
puts(KMOD_FEATURES);
|
|
return EXIT_SUCCESS;
|
|
case '?':
|
|
return EXIT_FAILURE;
|
|
default:
|
|
ERR("unexpected getopt_long() value '%c'.\n", c);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
log_open(use_syslog);
|
|
|
|
if (optind >= argc) {
|
|
ERR("missing module name.\n");
|
|
r = EXIT_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
ctx = kmod_new(NULL, &null_config);
|
|
if (!ctx) {
|
|
ERR("kmod_new() failed!\n");
|
|
r = EXIT_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
log_setup_kmod_log(ctx, verbose);
|
|
|
|
for (i = optind; i < argc; i++) {
|
|
struct kmod_module *mod;
|
|
const char *arg = argv[i];
|
|
struct stat st;
|
|
if (stat(arg, &st) == 0)
|
|
err = kmod_module_new_from_path(ctx, arg, &mod);
|
|
else
|
|
err = kmod_module_new_from_name(ctx, arg, &mod);
|
|
|
|
if (err < 0) {
|
|
ERR("could not use module %s: %s\n", arg,
|
|
strerror(-err));
|
|
break;
|
|
}
|
|
|
|
if (!(flags & KMOD_REMOVE_FORCE) && check_module_inuse(mod) < 0) {
|
|
r++;
|
|
goto next;
|
|
}
|
|
|
|
err = kmod_module_remove_module(mod, flags);
|
|
if (err < 0) {
|
|
ERR("could not remove module %s: %s\n", arg,
|
|
strerror(-err));
|
|
r++;
|
|
}
|
|
next:
|
|
kmod_module_unref(mod);
|
|
}
|
|
|
|
kmod_unref(ctx);
|
|
|
|
done:
|
|
log_close();
|
|
|
|
return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|
|
|
|
const struct kmod_cmd kmod_cmd_compat_rmmod = {
|
|
.name = "rmmod",
|
|
.cmd = do_rmmod,
|
|
.help = "compat rmmod command",
|
|
};
|