| @@ -36,6 +36,8 @@ · 35 unmodified lines |
| 36 | #include <linux/resource.h> | 36 | #include <linux/resource.h> |
| 37 | #include <asm/uaccess.h> | 37 | #include <asm/uaccess.h> |
| 38 | | 38 | |
| | 39 | extern int delete_module(const char *name, unsigned int flags); |
| | 40 | |
| 39 | extern int max_threads; | 41 | extern int max_threads; |
| 40 | | 42 | |
| 41 | static struct workqueue_struct *khelper_wq; | 43 | static struct workqueue_struct *khelper_wq; |
| @@ -46,6 +48,7 @@ static struct workqueue_struct *khelper_wq; · 4 unmodified lines |
| 46 | modprobe_path is set via /proc/sys. | 48 | modprobe_path is set via /proc/sys. |
| 47 | */ | 49 | */ |
| 48 | char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; | 50 | char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; |
| | 51 | struct module_kobject kmod_mk; |
| 49 | | 52 | |
| 50 | /** | 53 | /** |
| 51 | * request_module - try to load a kernel module | 54 | * request_module - try to load a kernel module |
| @@ -75,6 +78,11 @@ int request_module(const char *fmt, ...) · 23 unmodified lines |
| 75 | static atomic_t kmod_concurrent = ATOMIC_INIT(0); | 78 | static atomic_t kmod_concurrent = ATOMIC_INIT(0); |
| 76 | #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ | 79 | #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ |
| 77 | static int kmod_loop_msg; | 80 | static int kmod_loop_msg; |
| | 81 | char modalias[16 + MODULE_NAME_LEN] = "MODALIAS="; |
| | 82 | char *uevent_envp[2] = { |
| | 83 | modalias, |
| | 84 | NULL |
| | 85 | }; |
| 78 | | 86 | |
| 79 | va_start(args, fmt); | 87 | va_start(args, fmt); |
| 80 | ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); | 88 | ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); |
| @@ -82,6 +90,12 @@ int request_module(const char *fmt, ...) · 1 unmodified line |
| 82 | if (ret >= MODULE_NAME_LEN) | 90 | if (ret >= MODULE_NAME_LEN) |
| 83 | return -ENAMETOOLONG; | 91 | return -ENAMETOOLONG; |
| 84 | | 92 | |
| | 93 | strcpy(&modalias[strlen("MODALIAS=")], module_name); |
| | 94 | kobject_uevent_env(&kmod_mk.kobj, KOBJ_CHANGE, uevent_envp); |
| | 95 | |
| | 96 | if (modprobe_path[0] == '\0') |
| | 97 | goto out; |
| | 98 | |
| 85 | /* If modprobe needs a service that is in a module, we get a recursive | 99 | /* If modprobe needs a service that is in a module, we get a recursive |
| 86 | * loop. Limit the number of running kmod threads to max_threads/2 or | 100 | * loop. Limit the number of running kmod threads to max_threads/2 or |
| 87 | * MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method | 101 | * MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method |
| @@ -108,9 +122,115 @@ int request_module(const char *fmt, ...) · 20 unmodified lines |
| 108 | | 122 | |
| 109 | ret = call_usermodehelper(modprobe_path, argv, envp, 1); | 123 | ret = call_usermodehelper(modprobe_path, argv, envp, 1); |
| 110 | atomic_dec(&kmod_concurrent); | 124 | atomic_dec(&kmod_concurrent); |
| | 125 | out: |
| 111 | return ret; | 126 | return ret; |
| 112 | } | 127 | } |
| 113 | EXPORT_SYMBOL(request_module); | 128 | EXPORT_SYMBOL(request_module); |
| | 129 | |
| | 130 | static ssize_t store_mod_request(struct module_attribute *mattr, |
| | 131 | struct module *mod, |
| | 132 | const char *buffer, size_t count) |
| | 133 | { |
| | 134 | char name[MODULE_NAME_LEN]; |
| | 135 | int ret; |
| | 136 | |
| | 137 | if (count < 1 || count+1 > MODULE_NAME_LEN) |
| | 138 | return -EINVAL; |
| | 139 | memcpy(name, buffer, count); |
| | 140 | name[count] = '\0'; |
| | 141 | if (name[count-1] == '\n') |
| | 142 | name[count-1] = '\0'; |
| | 143 | |
| | 144 | ret = request_module(name); |
| | 145 | if (ret < 0) |
| | 146 | return ret; |
| | 147 | return count; |
| | 148 | } |
| | 149 | |
| | 150 | static struct module_attribute mod_request = { |
| | 151 | .attr = { .name = "mod_request", .mode = S_IWUSR, .owner = THIS_MODULE }, |
| | 152 | .store = store_mod_request, |
| | 153 | }; |
| | 154 | |
| | 155 | #ifdef CONFIG_MODULE_UNLOAD |
| | 156 | static ssize_t store_mod_unload(struct module_attribute *mattr, |
| | 157 | struct module *mod, |
| | 158 | const char *buffer, size_t count) |
| | 159 | { |
| | 160 | char name[MODULE_NAME_LEN]; |
| | 161 | int ret; |
| | 162 | |
| | 163 | if (count < 1 || count+1 > MODULE_NAME_LEN) |
| | 164 | return -EINVAL; |
| | 165 | memcpy(name, buffer, count); |
| | 166 | name[count] = '\0'; |
| | 167 | if (name[count-1] == '\n') |
| | 168 | name[count-1] = '\0'; |
| | 169 | |
| | 170 | ret = delete_module(name, O_NONBLOCK); |
| | 171 | if (ret < 0) |
| | 172 | return ret; |
| | 173 | return count; |
| | 174 | } |
| | 175 | |
| | 176 | static struct module_attribute mod_unload = { |
| | 177 | .attr = { .name = "mod_unload", .mode = S_IWUSR, .owner = THIS_MODULE }, |
| | 178 | .store = store_mod_unload, |
| | 179 | }; |
| | 180 | #endif |
| | 181 | |
| | 182 | static ssize_t show_mod_request_helper(struct module_attribute *mattr, |
| | 183 | struct module *mod, |
| | 184 | char *buffer) |
| | 185 | { |
| | 186 | return sprintf(buffer, "%s\n", modprobe_path); |
| | 187 | } |
| | 188 | |
| | 189 | static ssize_t store_mod_request_helper(struct module_attribute *mattr, |
| | 190 | struct module *mod, |
| | 191 | const char *buffer, size_t count) |
| | 192 | { |
| | 193 | if (count < 1 || count+1 > KMOD_PATH_LEN) |
| | 194 | return -EINVAL; |
| | 195 | memcpy(modprobe_path, buffer, count); |
| | 196 | modprobe_path[count] = '\0'; |
| | 197 | if (modprobe_path[count-1] == '\n') |
| | 198 | modprobe_path[count-1] = '\0'; |
| | 199 | return count; |
| | 200 | } |
| | 201 | |
| | 202 | static struct module_attribute mod_request_helper = { |
| | 203 | .attr = { |
| | 204 | .name = "mod_request_helper", |
| | 205 | .mode = S_IWUSR | S_IRUGO, |
| | 206 | .owner = THIS_MODULE |
| | 207 | }, |
| | 208 | .show = show_mod_request_helper, |
| | 209 | .store = store_mod_request_helper, |
| | 210 | }; |
| | 211 | |
| | 212 | void __init kmod_sysfs_init(void) |
| | 213 | { |
| | 214 | int ret; |
| | 215 | |
| | 216 | kmod_mk.mod = THIS_MODULE; |
| | 217 | kobj_set_kset_s(&kmod_mk, module_subsys); |
| | 218 | kobject_set_name(&kmod_mk.kobj, "kmod"); |
| | 219 | kobject_init(&kmod_mk.kobj); |
| | 220 | ret = kobject_add(&kmod_mk.kobj); |
| | 221 | if (ret < 0) |
| | 222 | goto out; |
| | 223 | |
| | 224 | ret = sysfs_create_file(&kmod_mk.kobj, &mod_request_helper.attr); |
| | 225 | ret = sysfs_create_file(&kmod_mk.kobj, &mod_request.attr); |
| | 226 | #ifdef CONFIG_MODULE_UNLOAD |
| | 227 | ret = sysfs_create_file(&kmod_mk.kobj, &mod_unload.attr); |
| | 228 | #endif |
| | 229 | |
| | 230 | kobject_uevent(&kmod_mk.kobj, KOBJ_ADD); |
| | 231 | out: |
| | 232 | return; |
| | 233 | } |
| 114 | #endif /* CONFIG_KMOD */ | 234 | #endif /* CONFIG_KMOD */ |
| 115 | | 235 | |
| 116 | struct subprocess_info { | 236 | struct subprocess_info { |