Blame view

lib/kobject_uevent.c 18.8 KB
d9d16e16a   Greg Kroah-Hartman   kobject: add SPDX...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
  /*
   * kernel userspace event delivery
   *
   * Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
   * Copyright (C) 2004 Novell, Inc.  All rights reserved.
   * Copyright (C) 2004 IBM, Inc. All rights reserved.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
15
16
   * Authors:
   *	Robert Love		<rml@novell.com>
   *	Kay Sievers		<kay.sievers@vrfy.org>
   *	Arjan van de Ven	<arjanv@redhat.com>
   *	Greg Kroah-Hartman	<greg@kroah.com>
   */
  
  #include <linux/spinlock.h>
2d38f9a4f   Denis V. Lunev   [NETNS]: Do no in...
17
18
  #include <linux/string.h>
  #include <linux/kobject.h>
8bc3bcc93   Paul Gortmaker   lib: reduce the u...
19
20
  #include <linux/export.h>
  #include <linux/kmod.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
  #include <linux/socket.h>
  #include <linux/skbuff.h>
  #include <linux/netlink.h>
26045a7b1   Christian Brauner   uevent: add alloc...
25
  #include <linux/uidgid.h>
f36776faf   Peter Rajnoha   kobject: support ...
26
27
  #include <linux/uuid.h>
  #include <linux/ctype.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
  #include <net/sock.h>
692ec06d7   Christian Brauner   netns: send ueven...
29
  #include <net/netlink.h>
07e98962f   Eric W. Biederman   kobject: Send hot...
30
  #include <net/net_namespace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32

cd030c4cb   Cornelia Huck   kobject: fix link...
33
  u64 uevent_seqnum;
86d56134f   Michael Marineau   kobject: Make sup...
34
  #ifdef CONFIG_UEVENT_HELPER
6a8d8abb6   Kay Sievers   Driver core: add ...
35
  char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
86d56134f   Michael Marineau   kobject: Make sup...
36
  #endif
94e5e3087   Christian Brauner   net: add uevent s...
37

07e98962f   Eric W. Biederman   kobject: Send hot...
38
39
40
41
  struct uevent_sock {
  	struct list_head list;
  	struct sock *sk;
  };
94e5e3087   Christian Brauner   net: add uevent s...
42
43
  
  #ifdef CONFIG_NET
07e98962f   Eric W. Biederman   kobject: Send hot...
44
  static LIST_HEAD(uevent_sock_list);
cd030c4cb   Cornelia Huck   kobject: fix link...
45
  #endif
7b60a18da   Andrew Vagin   uevent: send even...
46
47
  /* This lock protects uevent_seqnum and uevent_sock_list */
  static DEFINE_MUTEX(uevent_sock_mutex);
5c5daf657   Kay Sievers   Driver core: excl...
48
49
50
51
52
53
54
55
  /* the strings here must match the enum in include/linux/kobject.h */
  static const char *kobject_actions[] = {
  	[KOBJ_ADD] =		"add",
  	[KOBJ_REMOVE] =		"remove",
  	[KOBJ_CHANGE] =		"change",
  	[KOBJ_MOVE] =		"move",
  	[KOBJ_ONLINE] =		"online",
  	[KOBJ_OFFLINE] =	"offline",
1455cf8db   Dmitry Torokhov   driver core: emit...
56
57
  	[KOBJ_BIND] =		"bind",
  	[KOBJ_UNBIND] =		"unbind",
5c5daf657   Kay Sievers   Driver core: excl...
58
  };
f36776faf   Peter Rajnoha   kobject: support ...
59
60
61
  static int kobject_action_type(const char *buf, size_t count,
  			       enum kobject_action *type,
  			       const char **args)
5c5daf657   Kay Sievers   Driver core: excl...
62
63
  {
  	enum kobject_action action;
f36776faf   Peter Rajnoha   kobject: support ...
64
65
  	size_t count_first;
  	const char *args_start;
5c5daf657   Kay Sievers   Driver core: excl...
66
  	int ret = -EINVAL;
a9edadbf7   Mark Lord   fix uevent action...
67
68
  	if (count && (buf[count-1] == '
  ' || buf[count-1] == '\0'))
5c5daf657   Kay Sievers   Driver core: excl...
69
70
71
72
  		count--;
  
  	if (!count)
  		goto out;
f36776faf   Peter Rajnoha   kobject: support ...
73
74
75
76
77
78
  	args_start = strnchr(buf, count, ' ');
  	if (args_start) {
  		count_first = args_start - buf;
  		args_start = args_start + 1;
  	} else
  		count_first = count;
5c5daf657   Kay Sievers   Driver core: excl...
79
  	for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
f36776faf   Peter Rajnoha   kobject: support ...
80
  		if (strncmp(kobject_actions[action], buf, count_first) != 0)
5c5daf657   Kay Sievers   Driver core: excl...
81
  			continue;
f36776faf   Peter Rajnoha   kobject: support ...
82
  		if (kobject_actions[action][count_first] != '\0')
5c5daf657   Kay Sievers   Driver core: excl...
83
  			continue;
f36776faf   Peter Rajnoha   kobject: support ...
84
85
  		if (args)
  			*args = args_start;
5c5daf657   Kay Sievers   Driver core: excl...
86
87
88
89
90
91
92
  		*type = action;
  		ret = 0;
  		break;
  	}
  out:
  	return ret;
  }
f36776faf   Peter Rajnoha   kobject: support ...
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  static const char *action_arg_word_end(const char *buf, const char *buf_end,
  				       char delim)
  {
  	const char *next = buf;
  
  	while (next <= buf_end && *next != delim)
  		if (!isalnum(*next++))
  			return NULL;
  
  	if (next == buf)
  		return NULL;
  
  	return next;
  }
  
  static int kobject_action_args(const char *buf, size_t count,
  			       struct kobj_uevent_env **ret_env)
  {
  	struct kobj_uevent_env *env = NULL;
  	const char *next, *buf_end, *key;
  	int key_len;
  	int r = -EINVAL;
  
  	if (count && (buf[count - 1] == '
  ' || buf[count - 1] == '\0'))
  		count--;
  
  	if (!count)
  		return -EINVAL;
  
  	env = kzalloc(sizeof(*env), GFP_KERNEL);
  	if (!env)
  		return -ENOMEM;
  
  	/* first arg is UUID */
  	if (count < UUID_STRING_LEN || !uuid_is_valid(buf) ||
  	    add_uevent_var(env, "SYNTH_UUID=%.*s", UUID_STRING_LEN, buf))
  		goto out;
  
  	/*
  	 * the rest are custom environment variables in KEY=VALUE
  	 * format with ' ' delimiter between each KEY=VALUE pair
  	 */
  	next = buf + UUID_STRING_LEN;
  	buf_end = buf + count - 1;
  
  	while (next <= buf_end) {
  		if (*next != ' ')
  			goto out;
  
  		/* skip the ' ', key must follow */
  		key = ++next;
  		if (key > buf_end)
  			goto out;
  
  		buf = next;
  		next = action_arg_word_end(buf, buf_end, '=');
  		if (!next || next > buf_end || *next != '=')
  			goto out;
  		key_len = next - buf;
  
  		/* skip the '=', value must follow */
  		if (++next > buf_end)
  			goto out;
  
  		buf = next;
  		next = action_arg_word_end(buf, buf_end, ' ');
  		if (!next)
  			goto out;
  
  		if (add_uevent_var(env, "SYNTH_ARG_%.*s=%.*s",
  				   key_len, key, (int) (next - buf), buf))
  			goto out;
  	}
  
  	r = 0;
  out:
  	if (r)
  		kfree(env);
  	else
  		*ret_env = env;
  	return r;
  }
  
  /**
   * kobject_synth_uevent - send synthetic uevent with arguments
   *
   * @kobj: struct kobject for which synthetic uevent is to be generated
   * @buf: buffer containing action type and action args, newline is ignored
   * @count: length of buffer
   *
   * Returns 0 if kobject_synthetic_uevent() is completed with success or the
   * corresponding error when it fails.
   */
  int kobject_synth_uevent(struct kobject *kobj, const char *buf, size_t count)
  {
  	char *no_uuid_envp[] = { "SYNTH_UUID=0", NULL };
  	enum kobject_action action;
  	const char *action_args;
  	struct kobj_uevent_env *env;
  	const char *msg = NULL, *devpath;
  	int r;
  
  	r = kobject_action_type(buf, count, &action, &action_args);
  	if (r) {
549ad2437   Bo YU   kobject: drop new...
198
  		msg = "unknown uevent action string";
f36776faf   Peter Rajnoha   kobject: support ...
199
200
201
202
203
204
205
206
207
208
209
  		goto out;
  	}
  
  	if (!action_args) {
  		r = kobject_uevent_env(kobj, action, no_uuid_envp);
  		goto out;
  	}
  
  	r = kobject_action_args(action_args,
  				count - (action_args - buf), &env);
  	if (r == -EINVAL) {
549ad2437   Bo YU   kobject: drop new...
210
  		msg = "incorrect uevent action arguments";
f36776faf   Peter Rajnoha   kobject: support ...
211
212
213
214
215
216
217
218
219
220
221
  		goto out;
  	}
  
  	if (r)
  		goto out;
  
  	r = kobject_uevent_env(kobj, action, env->envp);
  	kfree(env);
  out:
  	if (r) {
  		devpath = kobject_get_path(kobj, GFP_KERNEL);
549ad2437   Bo YU   kobject: drop new...
222
223
  		pr_warn("synth uevent: %s: %s
  ",
f36776faf   Peter Rajnoha   kobject: support ...
224
225
226
227
228
229
  		       devpath ?: "unknown device",
  		       msg ?: "failed to send uevent");
  		kfree(devpath);
  	}
  	return r;
  }
86d56134f   Michael Marineau   kobject: Make sup...
230
  #ifdef CONFIG_UEVENT_HELPER
417daa1e8   Eric W. Biederman   hotplug: netns aw...
231
232
233
234
235
236
237
  static int kobj_usermode_filter(struct kobject *kobj)
  {
  	const struct kobj_ns_type_operations *ops;
  
  	ops = kobj_ns_ops(kobj);
  	if (ops) {
  		const void *init_ns, *ns;
6be244dcd   Bo YU   kobject: Fix warn...
238

417daa1e8   Eric W. Biederman   hotplug: netns aw...
239
240
241
242
243
244
245
  		ns = kobj->ktype->namespace(kobj);
  		init_ns = ops->initial_ns();
  		return ns != init_ns;
  	}
  
  	return 0;
  }
bcccff93a   Vladimir Davydov   kobject: don't bl...
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
  {
  	int len;
  
  	len = strlcpy(&env->buf[env->buflen], subsystem,
  		      sizeof(env->buf) - env->buflen);
  	if (len >= (sizeof(env->buf) - env->buflen)) {
  		WARN(1, KERN_ERR "init_uevent_argv: buffer size too small
  ");
  		return -ENOMEM;
  	}
  
  	env->argv[0] = uevent_helper;
  	env->argv[1] = &env->buf[env->buflen];
  	env->argv[2] = NULL;
  
  	env->buflen += len + 1;
  	return 0;
  }
  
  static void cleanup_uevent_env(struct subprocess_info *info)
  {
  	kfree(info->data);
  }
86d56134f   Michael Marineau   kobject: Make sup...
270
  #endif
bcccff93a   Vladimir Davydov   kobject: don't bl...
271

26045a7b1   Christian Brauner   uevent: add alloc...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  #ifdef CONFIG_NET
  static struct sk_buff *alloc_uevent_skb(struct kobj_uevent_env *env,
  					const char *action_string,
  					const char *devpath)
  {
  	struct netlink_skb_parms *parms;
  	struct sk_buff *skb = NULL;
  	char *scratch;
  	size_t len;
  
  	/* allocate message with maximum possible size */
  	len = strlen(action_string) + strlen(devpath) + 2;
  	skb = alloc_skb(len + env->buflen, GFP_KERNEL);
  	if (!skb)
  		return NULL;
  
  	/* add header */
  	scratch = skb_put(skb, len);
  	sprintf(scratch, "%s@%s", action_string, devpath);
  
  	skb_put_data(skb, env->buf, env->buflen);
  
  	parms = &NETLINK_CB(skb);
  	parms->creds.uid = GLOBAL_ROOT_UID;
  	parms->creds.gid = GLOBAL_ROOT_GID;
  	parms->dst_group = 1;
  	parms->portid = 0;
  
  	return skb;
  }
26045a7b1   Christian Brauner   uevent: add alloc...
302

a3498436b   Christian Brauner   netns: restrict u...
303
304
305
  static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
  					 const char *action_string,
  					 const char *devpath)
16dff336b   Eric Dumazet   kobject: add kobj...
306
  {
d464e84ee   Eric Dumazet   kobject: factoriz...
307
  	struct sk_buff *skb = NULL;
16dff336b   Eric Dumazet   kobject: add kobj...
308
  	struct uevent_sock *ue_sk;
a3498436b   Christian Brauner   netns: restrict u...
309
  	int retval = 0;
16dff336b   Eric Dumazet   kobject: add kobj...
310
311
312
313
  
  	/* send netlink message */
  	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
  		struct sock *uevent_sock = ue_sk->sk;
16dff336b   Eric Dumazet   kobject: add kobj...
314
315
316
  
  		if (!netlink_has_listeners(uevent_sock, 1))
  			continue;
d464e84ee   Eric Dumazet   kobject: factoriz...
317
  		if (!skb) {
d464e84ee   Eric Dumazet   kobject: factoriz...
318
  			retval = -ENOMEM;
26045a7b1   Christian Brauner   uevent: add alloc...
319
  			skb = alloc_uevent_skb(env, action_string, devpath);
d464e84ee   Eric Dumazet   kobject: factoriz...
320
321
  			if (!skb)
  				continue;
d464e84ee   Eric Dumazet   kobject: factoriz...
322
  		}
a3498436b   Christian Brauner   netns: restrict u...
323
324
  		retval = netlink_broadcast(uevent_sock, skb_get(skb), 0, 1,
  					   GFP_KERNEL);
d464e84ee   Eric Dumazet   kobject: factoriz...
325
326
327
  		/* ENOBUFS should be handled in userspace */
  		if (retval == -ENOBUFS || retval == -ESRCH)
  			retval = 0;
16dff336b   Eric Dumazet   kobject: add kobj...
328
  	}
d464e84ee   Eric Dumazet   kobject: factoriz...
329
  	consume_skb(skb);
a3498436b   Christian Brauner   netns: restrict u...
330

16dff336b   Eric Dumazet   kobject: add kobj...
331
332
  	return retval;
  }
a3498436b   Christian Brauner   netns: restrict u...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  static int uevent_net_broadcast_tagged(struct sock *usk,
  				       struct kobj_uevent_env *env,
  				       const char *action_string,
  				       const char *devpath)
  {
  	struct user_namespace *owning_user_ns = sock_net(usk)->user_ns;
  	struct sk_buff *skb = NULL;
  	int ret = 0;
  
  	skb = alloc_uevent_skb(env, action_string, devpath);
  	if (!skb)
  		return -ENOMEM;
  
  	/* fix credentials */
  	if (owning_user_ns != &init_user_ns) {
  		struct netlink_skb_parms *parms = &NETLINK_CB(skb);
  		kuid_t root_uid;
  		kgid_t root_gid;
  
  		/* fix uid */
  		root_uid = make_kuid(owning_user_ns, 0);
  		if (uid_valid(root_uid))
  			parms->creds.uid = root_uid;
  
  		/* fix gid */
  		root_gid = make_kgid(owning_user_ns, 0);
  		if (gid_valid(root_gid))
  			parms->creds.gid = root_gid;
  	}
  
  	ret = netlink_broadcast(usk, skb, 0, 1, GFP_KERNEL);
  	/* ENOBUFS should be handled in userspace */
  	if (ret == -ENOBUFS || ret == -ESRCH)
  		ret = 0;
  
  	return ret;
  }
  #endif
  
  static int kobject_uevent_net_broadcast(struct kobject *kobj,
  					struct kobj_uevent_env *env,
  					const char *action_string,
  					const char *devpath)
  {
  	int ret = 0;
  
  #ifdef CONFIG_NET
  	const struct kobj_ns_type_operations *ops;
  	const struct net *net = NULL;
  
  	ops = kobj_ns_ops(kobj);
  	if (!ops && kobj->kset) {
  		struct kobject *ksobj = &kobj->kset->kobj;
6be244dcd   Bo YU   kobject: Fix warn...
386

a3498436b   Christian Brauner   netns: restrict u...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
  		if (ksobj->parent != NULL)
  			ops = kobj_ns_ops(ksobj->parent);
  	}
  
  	/* kobjects currently only carry network namespace tags and they
  	 * are the only tag relevant here since we want to decide which
  	 * network namespaces to broadcast the uevent into.
  	 */
  	if (ops && ops->netlink_ns && kobj->ktype->namespace)
  		if (ops->type == KOBJ_NS_TYPE_NET)
  			net = kobj->ktype->namespace(kobj);
  
  	if (!net)
  		ret = uevent_net_broadcast_untagged(env, action_string,
  						    devpath);
  	else
  		ret = uevent_net_broadcast_tagged(net->uevent_sock->sk, env,
  						  action_string, devpath);
  #endif
  
  	return ret;
  }
6878e7de6   Dmitry Torokhov   driver core: supp...
409
410
411
  static void zap_modalias_env(struct kobj_uevent_env *env)
  {
  	static const char modalias_prefix[] = "MODALIAS=";
9b3fa47d4   Dmitry Torokhov   kobject: fix supp...
412
413
  	size_t len;
  	int i, j;
6878e7de6   Dmitry Torokhov   driver core: supp...
414
415
416
417
418
419
420
  
  	for (i = 0; i < env->envp_idx;) {
  		if (strncmp(env->envp[i], modalias_prefix,
  			    sizeof(modalias_prefix) - 1)) {
  			i++;
  			continue;
  		}
9b3fa47d4   Dmitry Torokhov   kobject: fix supp...
421
422
423
424
425
426
427
428
429
  		len = strlen(env->envp[i]) + 1;
  
  		if (i != env->envp_idx - 1) {
  			memmove(env->envp[i], env->envp[i + 1],
  				env->buflen - len);
  
  			for (j = i; j < env->envp_idx - 1; j++)
  				env->envp[j] = env->envp[j + 1] - len;
  		}
6878e7de6   Dmitry Torokhov   driver core: supp...
430
431
  
  		env->envp_idx--;
9b3fa47d4   Dmitry Torokhov   kobject: fix supp...
432
  		env->buflen -= len;
6878e7de6   Dmitry Torokhov   driver core: supp...
433
434
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  /**
8a82472f8   Cornelia Huck   driver core: Intr...
436
   * kobject_uevent_env - send an uevent with environmental data
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
   * @kobj: struct kobject that the action is happening to
edbbf994b   Julia Lawall   kobject: improve ...
439
   * @action: action that is happening
8a82472f8   Cornelia Huck   driver core: Intr...
440
   * @envp_ext: pointer to environmental data
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
441
   *
f6e6e7799   Xiaotian Feng   kobject_uevent: f...
442
   * Returns 0 if kobject_uevent_env() is completed with success or the
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
443
   * corresponding error when it fails.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
   */
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
445
  int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
7eff2e7a8   Kay Sievers   Driver core: chan...
446
  		       char *envp_ext[])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  {
7eff2e7a8   Kay Sievers   Driver core: chan...
448
449
  	struct kobj_uevent_env *env;
  	const char *action_string = kobject_actions[action];
5f123fbd8   Kay Sievers   [PATCH] merge kob...
450
451
452
453
  	const char *devpath = NULL;
  	const char *subsystem;
  	struct kobject *top_kobj;
  	struct kset *kset;
9cd43611c   Emese Revfy   kobject: Constify...
454
  	const struct kset_uevent_ops *uevent_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  	int i = 0;
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
456
  	int retval = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457

c03a0fd0b   Tetsuo Handa   kobject: Don't tr...
458
459
460
461
462
463
  	/*
  	 * Mark "remove" event done regardless of result, for some subsystems
  	 * do not want to re-trigger "remove" event via automatic cleanup.
  	 */
  	if (action == KOBJ_REMOVE)
  		kobj->state_remove_uevent_sent = 1;
9f66fa2a4   Greg Kroah-Hartman   kobject: clean up...
464
465
  	pr_debug("kobject: '%s' (%p): %s
  ",
810304db7   Harvey Harrison   lib: replace rema...
466
  		 kobject_name(kobj), kobj, __func__);
5f123fbd8   Kay Sievers   [PATCH] merge kob...
467

5f123fbd8   Kay Sievers   [PATCH] merge kob...
468
469
  	/* search the kset we belong to */
  	top_kobj = kobj;
ccd490a3c   Kay Sievers   Driver core: kern...
470
  	while (!top_kobj->kset && top_kobj->parent)
14193fb91   John Anthony Kazos Jr   Kobject: kobject_...
471
  		top_kobj = top_kobj->parent;
ccd490a3c   Kay Sievers   Driver core: kern...
472

542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
473
  	if (!top_kobj->kset) {
9f66fa2a4   Greg Kroah-Hartman   kobject: clean up...
474
475
476
  		pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
  			 "without kset!
  ", kobject_name(kobj), kobj,
810304db7   Harvey Harrison   lib: replace rema...
477
  			 __func__);
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
478
479
  		return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480

5f123fbd8   Kay Sievers   [PATCH] merge kob...
481
  	kset = top_kobj->kset;
312c004d3   Kay Sievers   [PATCH] driver co...
482
  	uevent_ops = kset->uevent_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483

f67f129e5   Ming Lei   Driver core: impl...
484
485
486
487
488
489
490
491
  	/* skip the event, if uevent_suppress is set*/
  	if (kobj->uevent_suppress) {
  		pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
  				 "caused the event to drop!
  ",
  				 kobject_name(kobj), kobj, __func__);
  		return 0;
  	}
7eff2e7a8   Kay Sievers   Driver core: chan...
492
  	/* skip the event, if the filter returns zero. */
312c004d3   Kay Sievers   [PATCH] driver co...
493
  	if (uevent_ops && uevent_ops->filter)
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
494
  		if (!uevent_ops->filter(kset, kobj)) {
9f66fa2a4   Greg Kroah-Hartman   kobject: clean up...
495
496
497
  			pr_debug("kobject: '%s' (%p): %s: filter function "
  				 "caused the event to drop!
  ",
810304db7   Harvey Harrison   lib: replace rema...
498
  				 kobject_name(kobj), kobj, __func__);
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
499
500
  			return 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501

864062457   Kay Sievers   driver core: fix ...
502
503
504
505
506
507
  	/* originating subsystem */
  	if (uevent_ops && uevent_ops->name)
  		subsystem = uevent_ops->name(kset, kobj);
  	else
  		subsystem = kobject_name(&kset->kobj);
  	if (!subsystem) {
9f66fa2a4   Greg Kroah-Hartman   kobject: clean up...
508
509
510
  		pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
  			 "event to drop!
  ", kobject_name(kobj), kobj,
810304db7   Harvey Harrison   lib: replace rema...
511
  			 __func__);
864062457   Kay Sievers   driver core: fix ...
512
513
  		return 0;
  	}
7eff2e7a8   Kay Sievers   Driver core: chan...
514
515
516
  	/* environment buffer */
  	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
  	if (!env)
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
517
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518

5f123fbd8   Kay Sievers   [PATCH] merge kob...
519
520
  	/* complete object path */
  	devpath = kobject_get_path(kobj, GFP_KERNEL);
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
521
522
  	if (!devpath) {
  		retval = -ENOENT;
5f123fbd8   Kay Sievers   [PATCH] merge kob...
523
  		goto exit;
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
524
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525

5f123fbd8   Kay Sievers   [PATCH] merge kob...
526
  	/* default keys */
7eff2e7a8   Kay Sievers   Driver core: chan...
527
528
529
530
531
532
533
534
535
536
537
538
539
  	retval = add_uevent_var(env, "ACTION=%s", action_string);
  	if (retval)
  		goto exit;
  	retval = add_uevent_var(env, "DEVPATH=%s", devpath);
  	if (retval)
  		goto exit;
  	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
  	if (retval)
  		goto exit;
  
  	/* keys passed in from the caller */
  	if (envp_ext) {
  		for (i = 0; envp_ext[i]; i++) {
c65b9145f   Tejun Heo   uevent: don't pas...
540
  			retval = add_uevent_var(env, "%s", envp_ext[i]);
7eff2e7a8   Kay Sievers   Driver core: chan...
541
542
543
544
  			if (retval)
  				goto exit;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545

5f123fbd8   Kay Sievers   [PATCH] merge kob...
546
  	/* let the kset specific function add its stuff */
312c004d3   Kay Sievers   [PATCH] driver co...
547
  	if (uevent_ops && uevent_ops->uevent) {
7eff2e7a8   Kay Sievers   Driver core: chan...
548
  		retval = uevent_ops->uevent(kset, kobj, env);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
  		if (retval) {
9f66fa2a4   Greg Kroah-Hartman   kobject: clean up...
550
551
552
  			pr_debug("kobject: '%s' (%p): %s: uevent() returned "
  				 "%d
  ", kobject_name(kobj), kobj,
810304db7   Harvey Harrison   lib: replace rema...
553
  				 __func__, retval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
555
556
  			goto exit;
  		}
  	}
6878e7de6   Dmitry Torokhov   driver core: supp...
557
558
559
560
561
562
563
564
565
  	switch (action) {
  	case KOBJ_ADD:
  		/*
  		 * Mark "add" event so we can make sure we deliver "remove"
  		 * event to userspace during automatic cleanup. If
  		 * the object did send an "add" event, "remove" will
  		 * automatically generated by the core, if not already done
  		 * by the caller.
  		 */
0f4dafc05   Kay Sievers   Kobject: auto-cle...
566
  		kobj->state_add_uevent_sent = 1;
6878e7de6   Dmitry Torokhov   driver core: supp...
567
  		break;
6878e7de6   Dmitry Torokhov   driver core: supp...
568
569
570
571
572
573
574
  	case KOBJ_UNBIND:
  		zap_modalias_env(env);
  		break;
  
  	default:
  		break;
  	}
0f4dafc05   Kay Sievers   Kobject: auto-cle...
575

7b60a18da   Andrew Vagin   uevent: send even...
576
  	mutex_lock(&uevent_sock_mutex);
7eff2e7a8   Kay Sievers   Driver core: chan...
577
  	/* we will send an event, so request a new sequence number */
e0d70bcb3   Bo YU   kobject: drop unn...
578
  	retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum);
7b60a18da   Andrew Vagin   uevent: send even...
579
580
  	if (retval) {
  		mutex_unlock(&uevent_sock_mutex);
7eff2e7a8   Kay Sievers   Driver core: chan...
581
  		goto exit;
7b60a18da   Andrew Vagin   uevent: send even...
582
  	}
16dff336b   Eric Dumazet   kobject: add kobj...
583
584
  	retval = kobject_uevent_net_broadcast(kobj, env, action_string,
  					      devpath);
7b60a18da   Andrew Vagin   uevent: send even...
585
  	mutex_unlock(&uevent_sock_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586

86d56134f   Michael Marineau   kobject: Make sup...
587
  #ifdef CONFIG_UEVENT_HELPER
5f123fbd8   Kay Sievers   [PATCH] merge kob...
588
  	/* call uevent_helper, usually only enabled during early boot */
417daa1e8   Eric W. Biederman   hotplug: netns aw...
589
  	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
bcccff93a   Vladimir Davydov   kobject: don't bl...
590
  		struct subprocess_info *info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591

7eff2e7a8   Kay Sievers   Driver core: chan...
592
593
594
  		retval = add_uevent_var(env, "HOME=/");
  		if (retval)
  			goto exit;
e374a2bfe   Greg Kroah-Hartman   Kobject: fix codi...
595
596
  		retval = add_uevent_var(env,
  					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
7eff2e7a8   Kay Sievers   Driver core: chan...
597
598
  		if (retval)
  			goto exit;
bcccff93a   Vladimir Davydov   kobject: don't bl...
599
600
601
  		retval = init_uevent_argv(env, subsystem);
  		if (retval)
  			goto exit;
7eff2e7a8   Kay Sievers   Driver core: chan...
602

bcccff93a   Vladimir Davydov   kobject: don't bl...
603
604
605
606
607
608
609
610
  		retval = -ENOMEM;
  		info = call_usermodehelper_setup(env->argv[0], env->argv,
  						 env->envp, GFP_KERNEL,
  						 NULL, cleanup_uevent_env, env);
  		if (info) {
  			retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
  			env = NULL;	/* freed by cleanup_uevent_env */
  		}
5f123fbd8   Kay Sievers   [PATCH] merge kob...
611
  	}
86d56134f   Michael Marineau   kobject: Make sup...
612
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
  
  exit:
5f123fbd8   Kay Sievers   [PATCH] merge kob...
615
  	kfree(devpath);
7eff2e7a8   Kay Sievers   Driver core: chan...
616
  	kfree(env);
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
617
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
  }
8a82472f8   Cornelia Huck   driver core: Intr...
619
620
621
  EXPORT_SYMBOL_GPL(kobject_uevent_env);
  
  /**
f6e6e7799   Xiaotian Feng   kobject_uevent: f...
622
   * kobject_uevent - notify userspace by sending an uevent
8a82472f8   Cornelia Huck   driver core: Intr...
623
   *
8a82472f8   Cornelia Huck   driver core: Intr...
624
   * @kobj: struct kobject that the action is happening to
edbbf994b   Julia Lawall   kobject: improve ...
625
   * @action: action that is happening
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
626
627
628
   *
   * Returns 0 if kobject_uevent() is completed with success or the
   * corresponding error when it fails.
8a82472f8   Cornelia Huck   driver core: Intr...
629
   */
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
630
  int kobject_uevent(struct kobject *kobj, enum kobject_action action)
8a82472f8   Cornelia Huck   driver core: Intr...
631
  {
542cfce6f   Aneesh Kumar K.V   kobject: kobject_...
632
  	return kobject_uevent_env(kobj, action, NULL);
8a82472f8   Cornelia Huck   driver core: Intr...
633
  }
312c004d3   Kay Sievers   [PATCH] driver co...
634
  EXPORT_SYMBOL_GPL(kobject_uevent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
636
  
  /**
7eff2e7a8   Kay Sievers   Driver core: chan...
637
638
639
   * add_uevent_var - add key value string to the environment buffer
   * @env: environment buffer structure
   * @format: printf format for the key=value pair
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
   *
   * Returns 0 if environment variable was added successfully or -ENOMEM
   * if no space was available.
   */
7eff2e7a8   Kay Sievers   Driver core: chan...
644
  int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
  {
  	va_list args;
7eff2e7a8   Kay Sievers   Driver core: chan...
647
  	int len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648

7eff2e7a8   Kay Sievers   Driver core: chan...
649
  	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
5cd2b459d   Arjan van de Ven   Use WARN() in lib/
650
651
  		WARN(1, KERN_ERR "add_uevent_var: too many keys
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
  		return -ENOMEM;
7eff2e7a8   Kay Sievers   Driver core: chan...
653
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
655
  
  	va_start(args, format);
7eff2e7a8   Kay Sievers   Driver core: chan...
656
657
658
  	len = vsnprintf(&env->buf[env->buflen],
  			sizeof(env->buf) - env->buflen,
  			format, args);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
  	va_end(args);
7eff2e7a8   Kay Sievers   Driver core: chan...
660
  	if (len >= (sizeof(env->buf) - env->buflen)) {
5cd2b459d   Arjan van de Ven   Use WARN() in lib/
661
662
  		WARN(1, KERN_ERR "add_uevent_var: buffer size too small
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
  		return -ENOMEM;
7eff2e7a8   Kay Sievers   Driver core: chan...
664
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665

7eff2e7a8   Kay Sievers   Driver core: chan...
666
667
  	env->envp[env->envp_idx++] = &env->buf[env->buflen];
  	env->buflen += len + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
  	return 0;
  }
312c004d3   Kay Sievers   [PATCH] driver co...
670
  EXPORT_SYMBOL_GPL(add_uevent_var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671

4d17ffda3   Kay Sievers   [PATCH] Kobject: ...
672
  #if defined(CONFIG_NET)
692ec06d7   Christian Brauner   netns: send ueven...
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
  static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,
  				struct netlink_ext_ack *extack)
  {
  	/* u64 to chars: 2^64 - 1 = 21 chars */
  	char buf[sizeof("SEQNUM=") + 21];
  	struct sk_buff *skbc;
  	int ret;
  
  	/* bump and prepare sequence number */
  	ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu", ++uevent_seqnum);
  	if (ret < 0 || (size_t)ret >= sizeof(buf))
  		return -ENOMEM;
  	ret++;
  
  	/* verify message does not overflow */
  	if ((skb->len + ret) > UEVENT_BUFFER_SIZE) {
  		NL_SET_ERR_MSG(extack, "uevent message too big");
  		return -EINVAL;
  	}
  
  	/* copy skb and extend to accommodate sequence number */
  	skbc = skb_copy_expand(skb, 0, ret, GFP_KERNEL);
  	if (!skbc)
  		return -ENOMEM;
  
  	/* append sequence number */
  	skb_put_data(skbc, buf, ret);
  
  	/* remove msg header */
  	skb_pull(skbc, NLMSG_HDRLEN);
  
  	/* set portid 0 to inform userspace message comes from kernel */
  	NETLINK_CB(skbc).portid = 0;
  	NETLINK_CB(skbc).dst_group = 1;
  
  	ret = netlink_broadcast(usk, skbc, 0, 1, GFP_KERNEL);
  	/* ENOBUFS should be handled in userspace */
  	if (ret == -ENOBUFS || ret == -ESRCH)
  		ret = 0;
  
  	return ret;
  }
  
  static int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh,
  			      struct netlink_ext_ack *extack)
  {
  	struct net *net;
  	int ret;
  
  	if (!nlmsg_data(nlh))
  		return -EINVAL;
  
  	/*
  	 * Verify that we are allowed to send messages to the target
  	 * network namespace. The caller must have CAP_SYS_ADMIN in the
  	 * owning user namespace of the target network namespace.
  	 */
  	net = sock_net(NETLINK_CB(skb).sk);
  	if (!netlink_ns_capable(skb, net->user_ns, CAP_SYS_ADMIN)) {
  		NL_SET_ERR_MSG(extack, "missing CAP_SYS_ADMIN capability");
  		return -EPERM;
  	}
  
  	mutex_lock(&uevent_sock_mutex);
  	ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack);
  	mutex_unlock(&uevent_sock_mutex);
  
  	return ret;
  }
  
  static void uevent_net_rcv(struct sk_buff *skb)
  {
  	netlink_rcv_skb(skb, &uevent_net_rcv_skb);
  }
07e98962f   Eric W. Biederman   kobject: Send hot...
747
  static int uevent_net_init(struct net *net)
5f123fbd8   Kay Sievers   [PATCH] merge kob...
748
  {
07e98962f   Eric W. Biederman   kobject: Send hot...
749
  	struct uevent_sock *ue_sk;
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
750
751
  	struct netlink_kernel_cfg cfg = {
  		.groups	= 1,
692ec06d7   Christian Brauner   netns: send ueven...
752
753
  		.input = uevent_net_rcv,
  		.flags	= NL_CFG_F_NONROOT_RECV
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
754
  	};
07e98962f   Eric W. Biederman   kobject: Send hot...
755
756
757
758
  
  	ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
  	if (!ue_sk)
  		return -ENOMEM;
9f00d9776   Pablo Neira Ayuso   netlink: hide str...
759
  	ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg);
07e98962f   Eric W. Biederman   kobject: Send hot...
760
  	if (!ue_sk->sk) {
b3fa29ad8   Bo YU   kobject: to repal...
761
762
  		pr_err("kobject_uevent: unable to create netlink socket!
  ");
743db2d90   Dan Carpenter   kobject: free mem...
763
  		kfree(ue_sk);
5f123fbd8   Kay Sievers   [PATCH] merge kob...
764
765
  		return -ENODEV;
  	}
94e5e3087   Christian Brauner   net: add uevent s...
766
767
  
  	net->uevent_sock = ue_sk;
a3498436b   Christian Brauner   netns: restrict u...
768
769
770
771
772
773
  	/* Restrict uevents to initial user namespace. */
  	if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) {
  		mutex_lock(&uevent_sock_mutex);
  		list_add_tail(&ue_sk->list, &uevent_sock_list);
  		mutex_unlock(&uevent_sock_mutex);
  	}
5f123fbd8   Kay Sievers   [PATCH] merge kob...
774
775
  	return 0;
  }
07e98962f   Eric W. Biederman   kobject: Send hot...
776
777
  static void uevent_net_exit(struct net *net)
  {
94e5e3087   Christian Brauner   net: add uevent s...
778
  	struct uevent_sock *ue_sk = net->uevent_sock;
07e98962f   Eric W. Biederman   kobject: Send hot...
779

a3498436b   Christian Brauner   netns: restrict u...
780
781
782
783
784
  	if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) {
  		mutex_lock(&uevent_sock_mutex);
  		list_del(&ue_sk->list);
  		mutex_unlock(&uevent_sock_mutex);
  	}
07e98962f   Eric W. Biederman   kobject: Send hot...
785
786
787
788
789
790
791
792
793
794
795
796
  
  	netlink_kernel_release(ue_sk->sk);
  	kfree(ue_sk);
  }
  
  static struct pernet_operations uevent_net_ops = {
  	.init	= uevent_net_init,
  	.exit	= uevent_net_exit,
  };
  
  static int __init kobject_uevent_init(void)
  {
07e98962f   Eric W. Biederman   kobject: Send hot...
797
798
  	return register_pernet_subsys(&uevent_net_ops);
  }
5f123fbd8   Kay Sievers   [PATCH] merge kob...
799
  postcore_initcall(kobject_uevent_init);
4d17ffda3   Kay Sievers   [PATCH] Kobject: ...
800
  #endif