modprobe-small: fix and simplify rmmod

"rmmod OUT_OF_TREE_MODULE" was not working, because module is not in depmod file.

In general, rmmod doesn't need scanning, it simply unloads every argv[i].

function                                             old     new   delta
rmmod                                                  -      63     +63
modprobe_main                                        449     465     +16
process_module                                       705     667     -38
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/1 up/down: 79/-38)             Total: 41 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index cafbdc0..6b0a440 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -549,9 +549,23 @@
 	return ret;
 }
 #else
-#define already_loaded(name) is_rmmod
+#define already_loaded(name) 0
 #endif
 
+static int rmmod(const char *filename)
+{
+	int r;
+	char modname[MODULE_NAME_LEN];
+
+	filename2modname(filename, modname);
+	r = delete_module(modname, O_NONBLOCK | O_EXCL);
+	dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r);
+	if (r != 0 && !(option_mask32 & OPT_q)) {
+		bb_perror_msg("remove '%s'", modname);
+	}
+	return r;
+}
+
 /*
  * Given modules definition and module name (or alias, or symbol)
  * load/remove the module respecting dependencies.
@@ -568,26 +582,36 @@
 	module_info **infovec;
 	module_info *info;
 	int infoidx;
-	int is_rmmod = (option_mask32 & OPT_r) != 0;
+	int is_remove = (option_mask32 & OPT_r) != 0;
 
 	dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
 
 	replace(name, '-', '_');
 
-	dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod);
+	dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove);
+
+	if (applet_name[0] == 'r') {
+		/* rmmod.
+		 * Does not remove dependencies, no need to scan, just remove.
+		 * (compat note: this allows and strips .ko suffix)
+		 */
+		rmmod(name);
+		return;
+	}
+
 	/*
-	 * We used to have "is_rmmod != already_loaded(name)" check here, but
+	 * We used to have "is_remove != already_loaded(name)" check here, but
 	 *  modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80
 	 * won't unload modules (there are more than one)
 	 * which have this alias.
 	 */
-	if (!is_rmmod && already_loaded(name)) {
+	if (!is_remove && already_loaded(name)) {
 		dbg1_error_msg("nothing to do for '%s'", name);
 		return;
 	}
 
 	options = NULL;
-	if (!is_rmmod) {
+	if (!is_remove) {
 		char *opt_filename = xasprintf("/etc/modules/%s", name);
 		options = xmalloc_open_read_close(opt_filename, NULL);
 		if (options)
@@ -621,7 +645,7 @@
 			0 /* depth */
 		);
 		dbg1_error_msg("dirscan complete");
-		/* Module was not found, or load failed, or is_rmmod */
+		/* Module was not found, or load failed, or is_remove */
 		if (module_found_idx >= 0) { /* module was found */
 			infovec = xzalloc(2 * sizeof(infovec[0]));
 			infovec[0] = &modinfo[module_found_idx];
@@ -634,7 +658,7 @@
 
 	if (!infovec) {
 		/* both dirscan and find_alias found nothing */
-		if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
+		if (!is_remove && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
 			bb_error_msg("module '%s' not found", name);
 //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
 		goto ret;
@@ -648,29 +672,15 @@
 	 * a *list* of modinfo pointers from find_alias().
 	 */
 
-	/* rmmod or modprobe -r? unload module(s) */
-	if (is_rmmod) {
+	/* modprobe -r? unload module(s) */
+	if (is_remove) {
 		infoidx = 0;
 		while ((info = infovec[infoidx++]) != NULL) {
-			int r;
-			char modname[MODULE_NAME_LEN];
-
-			filename2modname(
-				bb_get_last_path_component_nostrip(info->pathname), modname);
-			r = delete_module(modname, O_NONBLOCK | O_EXCL);
-			dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r);
+			int r = rmmod(bb_get_last_path_component_nostrip(info->pathname));
 			if (r != 0) {
-				if (!(option_mask32 & OPT_q))
-					bb_perror_msg("remove '%s'", modname);
-				goto ret;
+				goto ret; /* error */
 			}
 		}
-
-		if (applet_name[0] == 'r') {
-			/* rmmod: do not remove dependencies, exit */
-			goto ret;
-		}
-
 		/* modprobe -r: we do not stop here -
 		 * continue to unload modules on which the module depends:
 		 * "-r --remove: option causes modprobe to remove a module.
@@ -691,7 +701,7 @@
 		}
 		free(deps);
 
-		if (is_rmmod)
+		if (is_remove)
 			continue;
 
 		/* We are modprobe: load it */
@@ -894,10 +904,10 @@
 	}
 
 #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
-	/* If not rmmod, parse possible module options given on command line.
+	/* If not rmmod/-r, parse possible module options given on command line.
 	 * insmod/modprobe takes one module name, the rest are parameters. */
 	options = NULL;
-	if ('r' != applet0) {
+	if (!(option_mask32 & OPT_r)) {
 		char **arg = argv;
 		while (*++arg) {
 			/* Enclose options in quotes */
@@ -908,7 +918,7 @@
 		}
 	}
 #else
-	if ('r' != applet0)
+	if (!(option_mask32 & OPT_r))
 		argv[1] = NULL;
 #endif
 
@@ -932,10 +942,11 @@
 	}
 
 	/* Try to load modprobe.dep.bb */
-	load_dep_bb();
+	if ('r' != applet0) /* not rmmod */
+		load_dep_bb();
 
 	/* Load/remove modules.
-	 * Only rmmod loops here, modprobe has only argv[0] */
+	 * Only rmmod/modprobe -r loops here, insmod/modprobe has only argv[0] */
 	do {
 		process_module(*argv, options);
 	} while (*++argv);