| @@ -36,6 +36,8 @@ · 35 unmodified lines |
| 36 | 36 | #include <linux/resource.h> |
| 37 | 37 | #include <asm/uaccess.h> |
| 38 | 38 | |
| 39 | +extern int delete_module(const char *name, unsigned int flags); |
| 40 | + |
| 39 | 41 | extern int max_threads; |
| 40 | 42 | |
| 41 | 43 | static struct workqueue_struct *khelper_wq; |
| @@ -46,6 +48,7 @@ static struct workqueue_struct *khelper_wq; · 4 unmodified lines |
| 46 | 48 | modprobe_path is set via /proc/sys. |
| 47 | 49 | */ |
| 48 | 50 | char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; |
| 51 | +struct module_kobject kmod_mk; |
| 49 | 52 | |
| 50 | 53 | /** |
| 51 | 54 | * request_module - try to load a kernel module |
| @@ -75,6 +78,11 @@ int request_module(const char *fmt, ...) · 23 unmodified lines |
| 75 | 78 | static atomic_t kmod_concurrent = ATOMIC_INIT(0); |
| 76 | 79 | #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ |
| 77 | 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 | 87 | va_start(args, fmt); |
| 80 | 88 | ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); |
| @@ -82,6 +90,12 @@ int request_module(const char *fmt, ...) · 1 unmodified line |
| 82 | 90 | if (ret >= MODULE_NAME_LEN) |
| 83 | 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 | 99 | /* If modprobe needs a service that is in a module, we get a recursive |
| 86 | 100 | * loop. Limit the number of running kmod threads to max_threads/2 or |
| 87 | 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 | 123 | ret = call_usermodehelper(modprobe_path, argv, envp, 1); |
| 110 | 124 | atomic_dec(&kmod_concurrent); |
| 125 | +out: |
| 111 | 126 | return ret; |
| 112 | 127 | } |
| 113 | 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 | 234 | #endif /* CONFIG_KMOD */ |
| 115 | 235 | |
| 116 | 236 | struct subprocess_info { |