modprobe: abort on dependency loop that cannot be broken
diff --git a/tools/kmod-modprobe.c b/tools/kmod-modprobe.c
index 33510f0..29b7088 100644
--- a/tools/kmod-modprobe.c
+++ b/tools/kmod-modprobe.c
@@ -1,5 +1,5 @@
 /*
- * kmod-modprob - manage linux kernel modules using libkmod.
+ * kmod-modprobe - manage linux kernel modules using libkmod.
  *
  * Copyright (C) 2011-2012  ProFUSION embedded systems
  *
@@ -31,6 +31,7 @@
 #include <limits.h>
 
 #include "libkmod.h"
+#include "libkmod-array.h"
 
 static int log_priority = LOG_CRIT;
 static int use_syslog = 0;
@@ -487,6 +488,8 @@
 	return err;
 }
 
+#define INSMOD_RECURSION_STEP 15
+
 static int concat_options(const char *conf_opts, const char *extra_opts,
 								char **opts)
 {
@@ -541,18 +544,64 @@
 
 }
 
+static bool insmod_recursion_has_loop(struct kmod_module *mod,
+						struct array *recursion)
+{
+	unsigned int i;
+
+	/*
+	 * Don't check every time to not impact normal use cases. If the
+	 * recursion hits INSMOD_RECURSION_STEP, then search loop in
+	 * @recursion.
+	 */
+	if ((recursion->count + 1) % INSMOD_RECURSION_STEP != 0)
+		return false;
+
+	for (i = 0; i < recursion->count; i++) {
+		if (recursion->array[i] != mod)
+			continue;
+
+		ERR("Dependency loop detected while inserting '%s'. Operation aborted.\n",
+						kmod_module_get_name(mod));
+
+		for (; i < recursion->count; i++)
+			ERR("\t%s\n", kmod_module_get_name(recursion->array[i]));
+
+		return true;
+	}
+
+	return false;
+}
+
 static int insmod_do_module(struct kmod_module *mod, const char *extra_opts,
-							bool do_dependencies);
+				bool do_dependencies, struct array *recursion);
 
 static int insmod_do_deps_list(struct kmod_list *list,
-						bool stop_on_errors)
+				bool stop_on_errors, struct array *recursion)
 {
 	struct kmod_list *l;
+	struct kmod_module *m;
+	int r;
+
+	if (list == NULL)
+		return 0;
+
+	m = kmod_module_get_module(list);
+	r = insmod_recursion_has_loop(m, recursion);
+	kmod_module_unref(m);
+
+	if (r)
+		return -ELOOP;
 
 	kmod_list_foreach(l, list) {
-		struct kmod_module *m = kmod_module_get_module(l);
-		int r = insmod_do_module(m, NULL, false);
+		m = kmod_module_get_module(l);
+		array_append(recursion, m);
+		r = insmod_do_module(m, NULL, false, recursion);
 		kmod_module_unref(m);
+		array_pop(recursion);
+
+		if (r == -ELOOP)
+			return r;
 
 		if (r < 0 && stop_on_errors)
 			return r;
@@ -562,7 +611,7 @@
 }
 
 static int insmod_do_module(struct kmod_module *mod, const char *extra_opts,
-							bool do_dependencies)
+				bool do_dependencies, struct array *recursion)
 {
 	const char *modname = kmod_module_get_name(mod);
 	const char *conf_opts = kmod_module_get_options(mod);
@@ -612,12 +661,14 @@
 		goto error;
 	}
 
-	insmod_do_deps_list(pre, false);
+	err = insmod_do_deps_list(pre, false, recursion);
+	if (err == -ELOOP)
+		goto error;
 
 	if (do_dependencies) {
 		struct kmod_list *deps = kmod_module_get_dependencies(mod);
 
-		err = insmod_do_deps_list(deps, true);
+		err = insmod_do_deps_list(deps, true, recursion);
 		if (err < 0)
 			goto error;
 	}
@@ -633,7 +684,7 @@
 	if (err < 0)
 		goto error;
 
-	insmod_do_deps_list(post, false);
+	err = insmod_do_deps_list(post, false, recursion);
 
 error:
 	kmod_module_unref_list(pre);
@@ -647,6 +698,7 @@
 						const char *extra_options)
 {
 	struct kmod_module *mod;
+	struct array recursion;
 	int err;
 
 	err = kmod_module_new_from_path(ctx, path, &mod);
@@ -654,8 +706,12 @@
 		LOG("Module %s not found.\n", path);
 		return err;
 	}
-	err = insmod_do_module(mod, extra_options, true);
+
+	array_init(&recursion, INSMOD_RECURSION_STEP);
+	err = insmod_do_module(mod, extra_options, true, &recursion);
 	kmod_module_unref(mod);
+	array_free_array(&recursion);
+
 	return err;
 }
 
@@ -714,10 +770,17 @@
 
 	kmod_list_foreach(l, list) {
 		struct kmod_module *mod = kmod_module_get_module(l);
+
 		if (lookup_only)
 			printf("%s\n", kmod_module_get_name(mod));
-		else
-			err = insmod_do_module(mod, extra_options, true);
+		else {
+			struct array recursion;
+
+			array_init(&recursion, INSMOD_RECURSION_STEP);
+			err = insmod_do_module(mod, extra_options, true,
+								&recursion);
+			array_free_array(&recursion);
+		}
 		kmod_module_unref(mod);
 		if (err < 0)
 			break;