Branch data Line data Source code
1 : : /*
2 : : * kernel userspace event delivery
3 : : *
4 : : * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
5 : : * Copyright (C) 2004 Novell, Inc. All rights reserved.
6 : : * Copyright (C) 2004 IBM, Inc. All rights reserved.
7 : : *
8 : : * Licensed under the GNU GPL v2.
9 : : *
10 : : * Authors:
11 : : * Robert Love <rml@novell.com>
12 : : * Kay Sievers <kay.sievers@vrfy.org>
13 : : * Arjan van de Ven <arjanv@redhat.com>
14 : : * Greg Kroah-Hartman <greg@kroah.com>
15 : : */
16 : :
17 : : #include <linux/spinlock.h>
18 : : #include <linux/string.h>
19 : : #include <linux/kobject.h>
20 : : #include <linux/export.h>
21 : : #include <linux/kmod.h>
22 : : #include <linux/slab.h>
23 : : #include <linux/user_namespace.h>
24 : : #include <linux/socket.h>
25 : : #include <linux/skbuff.h>
26 : : #include <linux/netlink.h>
27 : : #include <net/sock.h>
28 : : #include <net/net_namespace.h>
29 : :
30 : :
31 : : u64 uevent_seqnum;
32 : : char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
33 : : #ifdef CONFIG_NET
34 : : struct uevent_sock {
35 : : struct list_head list;
36 : : struct sock *sk;
37 : : };
38 : : static LIST_HEAD(uevent_sock_list);
39 : : #endif
40 : :
41 : : /* This lock protects uevent_seqnum and uevent_sock_list */
42 : : static DEFINE_MUTEX(uevent_sock_mutex);
43 : :
44 : : /* the strings here must match the enum in include/linux/kobject.h */
45 : : static const char *kobject_actions[] = {
46 : : [KOBJ_ADD] = "add",
47 : : [KOBJ_REMOVE] = "remove",
48 : : [KOBJ_CHANGE] = "change",
49 : : [KOBJ_MOVE] = "move",
50 : : [KOBJ_ONLINE] = "online",
51 : : [KOBJ_OFFLINE] = "offline",
52 : : };
53 : :
54 : : /**
55 : : * kobject_action_type - translate action string to numeric type
56 : : *
57 : : * @buf: buffer containing the action string, newline is ignored
58 : : * @len: length of buffer
59 : : * @type: pointer to the location to store the action type
60 : : *
61 : : * Returns 0 if the action string was recognized.
62 : : */
63 : 0 : int kobject_action_type(const char *buf, size_t count,
64 : : enum kobject_action *type)
65 : : {
66 : : enum kobject_action action;
67 : : int ret = -EINVAL;
68 : :
69 [ + - ][ - + ]: 19 : if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
70 : : count--;
71 : :
72 [ + - ]: 19 : if (!count)
73 : : goto out;
74 : :
75 [ + - ]: 57 : for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
76 [ + + ]: 57 : if (strncmp(kobject_actions[action], buf, count) != 0)
77 : 38 : continue;
78 [ - - ]: 19 : if (kobject_actions[action][count] != '\0')
79 : 0 : continue;
80 : 0 : *type = action;
81 : : ret = 0;
82 : 0 : break;
83 : : }
84 : : out:
85 : 0 : return ret;
86 : : }
87 : :
88 : : #ifdef CONFIG_NET
89 : 0 : static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data)
90 : : {
91 : : struct kobject *kobj = data, *ksobj;
92 : : const struct kobj_ns_type_operations *ops;
93 : :
94 : 325 : ops = kobj_ns_ops(kobj);
95 [ + - ][ + - ]: 325 : if (!ops && kobj->kset) {
96 : : ksobj = &kobj->kset->kobj;
97 [ - + ]: 325 : if (ksobj->parent != NULL)
98 : 0 : ops = kobj_ns_ops(ksobj->parent);
99 : : }
100 : :
101 [ - + ][ # # ]: 325 : if (ops && ops->netlink_ns && kobj->ktype->namespace) {
[ # # ]
102 : : const void *sock_ns, *ns;
103 : 0 : ns = kobj->ktype->namespace(kobj);
104 : 0 : sock_ns = ops->netlink_ns(dsk);
105 : 0 : return sock_ns != ns;
106 : : }
107 : :
108 : : return 0;
109 : : }
110 : : #endif
111 : :
112 : 0 : static int kobj_usermode_filter(struct kobject *kobj)
113 : : {
114 : : const struct kobj_ns_type_operations *ops;
115 : :
116 : 317 : ops = kobj_ns_ops(kobj);
117 [ - + ]: 317 : if (ops) {
118 : : const void *init_ns, *ns;
119 : 0 : ns = kobj->ktype->namespace(kobj);
120 : 0 : init_ns = ops->initial_ns();
121 : 0 : return ns != init_ns;
122 : : }
123 : :
124 : : return 0;
125 : : }
126 : :
127 : : /**
128 : : * kobject_uevent_env - send an uevent with environmental data
129 : : *
130 : : * @action: action that is happening
131 : : * @kobj: struct kobject that the action is happening to
132 : : * @envp_ext: pointer to environmental data
133 : : *
134 : : * Returns 0 if kobject_uevent_env() is completed with success or the
135 : : * corresponding error when it fails.
136 : : */
137 : 0 : int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
138 : : char *envp_ext[])
139 : : {
140 : : struct kobj_uevent_env *env;
141 : 357 : const char *action_string = kobject_actions[action];
142 : : const char *devpath = NULL;
143 : : const char *subsystem;
144 : : struct kobject *top_kobj;
145 : : struct kset *kset;
146 : : const struct kset_uevent_ops *uevent_ops;
147 : : int i = 0;
148 : : int retval = 0;
149 : : #ifdef CONFIG_NET
150 : : struct uevent_sock *ue_sk;
151 : : #endif
152 : :
153 : : pr_debug("kobject: '%s' (%p): %s\n",
154 : : kobject_name(kobj), kobj, __func__);
155 : :
156 : : /* search the kset we belong to */
157 : : top_kobj = kobj;
158 [ + + ][ + - ]: 389 : while (!top_kobj->kset && top_kobj->parent)
159 : : top_kobj = top_kobj->parent;
160 : :
161 [ + - ]: 357 : if (!top_kobj->kset) {
162 : : pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
163 : : "without kset!\n", kobject_name(kobj), kobj,
164 : : __func__);
165 : : return -EINVAL;
166 : : }
167 : :
168 : : kset = top_kobj->kset;
169 : 357 : uevent_ops = kset->uevent_ops;
170 : :
171 : : /* skip the event, if uevent_suppress is set*/
172 [ + - ]: 357 : if (kobj->uevent_suppress) {
173 : : pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
174 : : "caused the event to drop!\n",
175 : : kobject_name(kobj), kobj, __func__);
176 : : return 0;
177 : : }
178 : : /* skip the event, if the filter returns zero. */
179 [ + - ][ + - ]: 357 : if (uevent_ops && uevent_ops->filter)
180 [ + + ]: 357 : if (!uevent_ops->filter(kset, kobj)) {
181 : : pr_debug("kobject: '%s' (%p): %s: filter function "
182 : : "caused the event to drop!\n",
183 : : kobject_name(kobj), kobj, __func__);
184 : : return 0;
185 : : }
186 : :
187 : : /* originating subsystem */
188 [ + - ][ + - ]: 317 : if (uevent_ops && uevent_ops->name)
189 : 317 : subsystem = uevent_ops->name(kset, kobj);
190 : : else
191 : 0 : subsystem = kobject_name(&kset->kobj);
192 [ + - ]: 317 : if (!subsystem) {
193 : : pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
194 : : "event to drop!\n", kobject_name(kobj), kobj,
195 : : __func__);
196 : : return 0;
197 : : }
198 : :
199 : : /* environment buffer */
200 : : env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
201 [ + - ]: 317 : if (!env)
202 : : return -ENOMEM;
203 : :
204 : : /* complete object path */
205 : 317 : devpath = kobject_get_path(kobj, GFP_KERNEL);
206 [ + - ]: 317 : if (!devpath) {
207 : : retval = -ENOENT;
208 : : goto exit;
209 : : }
210 : :
211 : : /* default keys */
212 : 317 : retval = add_uevent_var(env, "ACTION=%s", action_string);
213 [ + - ]: 317 : if (retval)
214 : : goto exit;
215 : 317 : retval = add_uevent_var(env, "DEVPATH=%s", devpath);
216 [ + - ]: 317 : if (retval)
217 : : goto exit;
218 : 317 : retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
219 [ + - ]: 317 : if (retval)
220 : : goto exit;
221 : :
222 : : /* keys passed in from the caller */
223 [ - + ]: 317 : if (envp_ext) {
224 [ # # ]: 0 : for (i = 0; envp_ext[i]; i++) {
225 : 0 : retval = add_uevent_var(env, "%s", envp_ext[i]);
226 [ # # ]: 0 : if (retval)
227 : : goto exit;
228 : : }
229 : : }
230 : :
231 : : /* let the kset specific function add its stuff */
232 [ + - ][ + - ]: 317 : if (uevent_ops && uevent_ops->uevent) {
233 : 317 : retval = uevent_ops->uevent(kset, kobj, env);
234 [ + - ]: 317 : if (retval) {
235 : : pr_debug("kobject: '%s' (%p): %s: uevent() returned "
236 : : "%d\n", kobject_name(kobj), kobj,
237 : : __func__, retval);
238 : : goto exit;
239 : : }
240 : : }
241 : :
242 : : /*
243 : : * Mark "add" and "remove" events in the object to ensure proper
244 : : * events to userspace during automatic cleanup. If the object did
245 : : * send an "add" event, "remove" will automatically generated by
246 : : * the core, if not already done by the caller.
247 : : */
248 [ + + ]: 317 : if (action == KOBJ_ADD)
249 : 124 : kobj->state_add_uevent_sent = 1;
250 [ + + ]: 193 : else if (action == KOBJ_REMOVE)
251 : 12 : kobj->state_remove_uevent_sent = 1;
252 : :
253 : 317 : mutex_lock(&uevent_sock_mutex);
254 : : /* we will send an event, so request a new sequence number */
255 : 317 : retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
256 [ - + ]: 317 : if (retval) {
257 : 0 : mutex_unlock(&uevent_sock_mutex);
258 : 0 : goto exit;
259 : : }
260 : :
261 : : #if defined(CONFIG_NET)
262 : : /* send netlink message */
263 [ + + ]: 634 : list_for_each_entry(ue_sk, &uevent_sock_list, list) {
264 : 317 : struct sock *uevent_sock = ue_sk->sk;
265 : : struct sk_buff *skb;
266 : : size_t len;
267 : :
268 [ - + ]: 317 : if (!netlink_has_listeners(uevent_sock, 1))
269 : 0 : continue;
270 : :
271 : : /* allocate message with the maximum possible size */
272 : 317 : len = strlen(action_string) + strlen(devpath) + 2;
273 : 317 : skb = alloc_skb(len + env->buflen, GFP_KERNEL);
274 [ + - ]: 317 : if (skb) {
275 : : char *scratch;
276 : :
277 : : /* add header */
278 : 317 : scratch = skb_put(skb, len);
279 : 317 : sprintf(scratch, "%s@%s", action_string, devpath);
280 : :
281 : : /* copy keys to our continuous event payload buffer */
282 [ + + ]: 2962 : for (i = 0; i < env->envp_idx; i++) {
283 : 2645 : len = strlen(env->envp[i]) + 1;
284 : 2645 : scratch = skb_put(skb, len);
285 : 2645 : strcpy(scratch, env->envp[i]);
286 : : }
287 : :
288 : 317 : NETLINK_CB(skb).dst_group = 1;
289 : 317 : retval = netlink_broadcast_filtered(uevent_sock, skb,
290 : : 0, 1, GFP_KERNEL,
291 : : kobj_bcast_filter,
292 : : kobj);
293 : : /* ENOBUFS should be handled in userspace */
294 [ - + ]: 317 : if (retval == -ENOBUFS || retval == -ESRCH)
295 : : retval = 0;
296 : : } else
297 : : retval = -ENOMEM;
298 : : }
299 : : #endif
300 : 317 : mutex_unlock(&uevent_sock_mutex);
301 : :
302 : : /* call uevent_helper, usually only enabled during early boot */
303 [ + - ][ + - ]: 317 : if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
304 : : char *argv [3];
305 : :
306 : 317 : argv [0] = uevent_helper;
307 : 317 : argv [1] = (char *)subsystem;
308 : 317 : argv [2] = NULL;
309 : 317 : retval = add_uevent_var(env, "HOME=/");
310 [ + - ]: 317 : if (retval)
311 : : goto exit;
312 : 317 : retval = add_uevent_var(env,
313 : : "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
314 [ + - ]: 317 : if (retval)
315 : : goto exit;
316 : :
317 : 317 : retval = call_usermodehelper(argv[0], argv,
318 : 317 : env->envp, UMH_WAIT_EXEC);
319 : : }
320 : :
321 : : exit:
322 : 317 : kfree(devpath);
323 : 317 : kfree(env);
324 : 317 : return retval;
325 : : }
326 : : EXPORT_SYMBOL_GPL(kobject_uevent_env);
327 : :
328 : : /**
329 : : * kobject_uevent - notify userspace by sending an uevent
330 : : *
331 : : * @action: action that is happening
332 : : * @kobj: struct kobject that the action is happening to
333 : : *
334 : : * Returns 0 if kobject_uevent() is completed with success or the
335 : : * corresponding error when it fails.
336 : : */
337 : 0 : int kobject_uevent(struct kobject *kobj, enum kobject_action action)
338 : : {
339 : 328 : return kobject_uevent_env(kobj, action, NULL);
340 : : }
341 : : EXPORT_SYMBOL_GPL(kobject_uevent);
342 : :
343 : : /**
344 : : * add_uevent_var - add key value string to the environment buffer
345 : : * @env: environment buffer structure
346 : : * @format: printf format for the key=value pair
347 : : *
348 : : * Returns 0 if environment variable was added successfully or -ENOMEM
349 : : * if no space was available.
350 : : */
351 : 0 : int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
352 : : {
353 : : va_list args;
354 : : int len;
355 : :
356 [ - + ]: 7001 : if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
357 : 0 : WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
358 : 0 : return -ENOMEM;
359 : : }
360 : :
361 : 7001 : va_start(args, format);
362 : 7001 : len = vsnprintf(&env->buf[env->buflen],
363 : : sizeof(env->buf) - env->buflen,
364 : : format, args);
365 : 7001 : va_end(args);
366 : :
367 [ - + ]: 7001 : if (len >= (sizeof(env->buf) - env->buflen)) {
368 : 0 : WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
369 : 0 : return -ENOMEM;
370 : : }
371 : :
372 : 7001 : env->envp[env->envp_idx++] = &env->buf[env->buflen];
373 : 7001 : env->buflen += len + 1;
374 : 7001 : return 0;
375 : : }
376 : : EXPORT_SYMBOL_GPL(add_uevent_var);
377 : :
378 : : #if defined(CONFIG_NET)
379 : 0 : static int uevent_net_init(struct net *net)
380 : : {
381 : : struct uevent_sock *ue_sk;
382 : 0 : struct netlink_kernel_cfg cfg = {
383 : : .groups = 1,
384 : : .flags = NL_CFG_F_NONROOT_RECV,
385 : : };
386 : :
387 : : ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
388 [ # # ]: 0 : if (!ue_sk)
389 : : return -ENOMEM;
390 : :
391 : 0 : ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg);
392 [ # # ]: 0 : if (!ue_sk->sk) {
393 : 0 : printk(KERN_ERR
394 : : "kobject_uevent: unable to create netlink socket!\n");
395 : 0 : kfree(ue_sk);
396 : 0 : return -ENODEV;
397 : : }
398 : 0 : mutex_lock(&uevent_sock_mutex);
399 : 0 : list_add_tail(&ue_sk->list, &uevent_sock_list);
400 : 0 : mutex_unlock(&uevent_sock_mutex);
401 : 0 : return 0;
402 : : }
403 : :
404 : 0 : static void uevent_net_exit(struct net *net)
405 : : {
406 : : struct uevent_sock *ue_sk;
407 : :
408 : 0 : mutex_lock(&uevent_sock_mutex);
409 [ # # ]: 0 : list_for_each_entry(ue_sk, &uevent_sock_list, list) {
410 [ # # ]: 0 : if (sock_net(ue_sk->sk) == net)
411 : : goto found;
412 : : }
413 : 0 : mutex_unlock(&uevent_sock_mutex);
414 : 0 : return;
415 : :
416 : : found:
417 : : list_del(&ue_sk->list);
418 : 0 : mutex_unlock(&uevent_sock_mutex);
419 : :
420 : 0 : netlink_kernel_release(ue_sk->sk);
421 : 0 : kfree(ue_sk);
422 : : }
423 : :
424 : : static struct pernet_operations uevent_net_ops = {
425 : : .init = uevent_net_init,
426 : : .exit = uevent_net_exit,
427 : : };
428 : :
429 : 0 : static int __init kobject_uevent_init(void)
430 : : {
431 : 0 : return register_pernet_subsys(&uevent_net_ops);
432 : : }
433 : :
434 : :
435 : : postcore_initcall(kobject_uevent_init);
436 : : #endif
|