Blame view
lib/kobject_uevent.c
18.8 KB
d9d16e16a kobject: add SPDX... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
1da177e4c 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 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 [NETNS]: Do no in... |
17 18 |
#include <linux/string.h> #include <linux/kobject.h> |
8bc3bcc93 lib: reduce the u... |
19 20 |
#include <linux/export.h> #include <linux/kmod.h> |
5a0e3ad6a include cleanup: ... |
21 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
22 23 24 |
#include <linux/socket.h> #include <linux/skbuff.h> #include <linux/netlink.h> |
26045a7b1 uevent: add alloc... |
25 |
#include <linux/uidgid.h> |
f36776faf kobject: support ... |
26 27 |
#include <linux/uuid.h> #include <linux/ctype.h> |
1da177e4c Linux-2.6.12-rc2 |
28 |
#include <net/sock.h> |
692ec06d7 netns: send ueven... |
29 |
#include <net/netlink.h> |
07e98962f kobject: Send hot... |
30 |
#include <net/net_namespace.h> |
1da177e4c Linux-2.6.12-rc2 |
31 |
|
1da177e4c Linux-2.6.12-rc2 |
32 |
|
cd030c4cb kobject: fix link... |
33 |
u64 uevent_seqnum; |
86d56134f kobject: Make sup... |
34 |
#ifdef CONFIG_UEVENT_HELPER |
6a8d8abb6 Driver core: add ... |
35 |
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; |
86d56134f kobject: Make sup... |
36 |
#endif |
94e5e3087 net: add uevent s... |
37 |
|
07e98962f kobject: Send hot... |
38 39 40 41 |
struct uevent_sock { struct list_head list; struct sock *sk; }; |
94e5e3087 net: add uevent s... |
42 43 |
#ifdef CONFIG_NET |
07e98962f kobject: Send hot... |
44 |
static LIST_HEAD(uevent_sock_list); |
cd030c4cb kobject: fix link... |
45 |
#endif |
7b60a18da uevent: send even... |
46 47 |
/* This lock protects uevent_seqnum and uevent_sock_list */ static DEFINE_MUTEX(uevent_sock_mutex); |
5c5daf657 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 driver core: emit... |
56 57 |
[KOBJ_BIND] = "bind", [KOBJ_UNBIND] = "unbind", |
5c5daf657 Driver core: excl... |
58 |
}; |
f36776faf kobject: support ... |
59 60 61 |
static int kobject_action_type(const char *buf, size_t count, enum kobject_action *type, const char **args) |
5c5daf657 Driver core: excl... |
62 63 |
{ enum kobject_action action; |
f36776faf kobject: support ... |
64 65 |
size_t count_first; const char *args_start; |
5c5daf657 Driver core: excl... |
66 |
int ret = -EINVAL; |
a9edadbf7 fix uevent action... |
67 68 |
if (count && (buf[count-1] == ' ' || buf[count-1] == '\0')) |
5c5daf657 Driver core: excl... |
69 70 71 72 |
count--; if (!count) goto out; |
f36776faf 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 Driver core: excl... |
79 |
for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) { |
f36776faf kobject: support ... |
80 |
if (strncmp(kobject_actions[action], buf, count_first) != 0) |
5c5daf657 Driver core: excl... |
81 |
continue; |
f36776faf kobject: support ... |
82 |
if (kobject_actions[action][count_first] != '\0') |
5c5daf657 Driver core: excl... |
83 |
continue; |
f36776faf kobject: support ... |
84 85 |
if (args) *args = args_start; |
5c5daf657 Driver core: excl... |
86 87 88 89 90 91 92 |
*type = action; ret = 0; break; } out: return ret; } |
f36776faf 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 kobject: drop new... |
198 |
msg = "unknown uevent action string"; |
f36776faf 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 kobject: drop new... |
210 |
msg = "incorrect uevent action arguments"; |
f36776faf 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 kobject: drop new... |
222 223 |
pr_warn("synth uevent: %s: %s ", |
f36776faf kobject: support ... |
224 225 226 227 228 229 |
devpath ?: "unknown device", msg ?: "failed to send uevent"); kfree(devpath); } return r; } |
86d56134f kobject: Make sup... |
230 |
#ifdef CONFIG_UEVENT_HELPER |
417daa1e8 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 kobject: Fix warn... |
238 |
|
417daa1e8 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 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 kobject: Make sup... |
270 |
#endif |
bcccff93a kobject: don't bl... |
271 |
|
26045a7b1 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 uevent: add alloc... |
302 |
|
a3498436b 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 kobject: add kobj... |
306 |
{ |
d464e84ee kobject: factoriz... |
307 |
struct sk_buff *skb = NULL; |
16dff336b kobject: add kobj... |
308 |
struct uevent_sock *ue_sk; |
a3498436b netns: restrict u... |
309 |
int retval = 0; |
16dff336b 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 kobject: add kobj... |
314 315 316 |
if (!netlink_has_listeners(uevent_sock, 1)) continue; |
d464e84ee kobject: factoriz... |
317 |
if (!skb) { |
d464e84ee kobject: factoriz... |
318 |
retval = -ENOMEM; |
26045a7b1 uevent: add alloc... |
319 |
skb = alloc_uevent_skb(env, action_string, devpath); |
d464e84ee kobject: factoriz... |
320 321 |
if (!skb) continue; |
d464e84ee kobject: factoriz... |
322 |
} |
a3498436b netns: restrict u... |
323 324 |
retval = netlink_broadcast(uevent_sock, skb_get(skb), 0, 1, GFP_KERNEL); |
d464e84ee kobject: factoriz... |
325 326 327 |
/* ENOBUFS should be handled in userspace */ if (retval == -ENOBUFS || retval == -ESRCH) retval = 0; |
16dff336b kobject: add kobj... |
328 |
} |
d464e84ee kobject: factoriz... |
329 |
consume_skb(skb); |
a3498436b netns: restrict u... |
330 |
|
16dff336b kobject: add kobj... |
331 332 |
return retval; } |
a3498436b 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 kobject: Fix warn... |
386 |
|
a3498436b 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 driver core: supp... |
409 410 411 |
static void zap_modalias_env(struct kobj_uevent_env *env) { static const char modalias_prefix[] = "MODALIAS="; |
9b3fa47d4 kobject: fix supp... |
412 413 |
size_t len; int i, j; |
6878e7de6 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 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 driver core: supp... |
430 431 |
env->envp_idx--; |
9b3fa47d4 kobject: fix supp... |
432 |
env->buflen -= len; |
6878e7de6 driver core: supp... |
433 434 |
} } |
1da177e4c Linux-2.6.12-rc2 |
435 |
/** |
8a82472f8 driver core: Intr... |
436 |
* kobject_uevent_env - send an uevent with environmental data |
1da177e4c Linux-2.6.12-rc2 |
437 |
* |
1da177e4c Linux-2.6.12-rc2 |
438 |
* @kobj: struct kobject that the action is happening to |
edbbf994b kobject: improve ... |
439 |
* @action: action that is happening |
8a82472f8 driver core: Intr... |
440 |
* @envp_ext: pointer to environmental data |
542cfce6f kobject: kobject_... |
441 |
* |
f6e6e7799 kobject_uevent: f... |
442 |
* Returns 0 if kobject_uevent_env() is completed with success or the |
542cfce6f kobject: kobject_... |
443 |
* corresponding error when it fails. |
1da177e4c Linux-2.6.12-rc2 |
444 |
*/ |
542cfce6f kobject: kobject_... |
445 |
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, |
7eff2e7a8 Driver core: chan... |
446 |
char *envp_ext[]) |
1da177e4c Linux-2.6.12-rc2 |
447 |
{ |
7eff2e7a8 Driver core: chan... |
448 449 |
struct kobj_uevent_env *env; const char *action_string = kobject_actions[action]; |
5f123fbd8 [PATCH] merge kob... |
450 451 452 453 |
const char *devpath = NULL; const char *subsystem; struct kobject *top_kobj; struct kset *kset; |
9cd43611c kobject: Constify... |
454 |
const struct kset_uevent_ops *uevent_ops; |
1da177e4c Linux-2.6.12-rc2 |
455 |
int i = 0; |
542cfce6f kobject: kobject_... |
456 |
int retval = 0; |
1da177e4c Linux-2.6.12-rc2 |
457 |
|
c03a0fd0b 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 kobject: clean up... |
464 465 |
pr_debug("kobject: '%s' (%p): %s ", |
810304db7 lib: replace rema... |
466 |
kobject_name(kobj), kobj, __func__); |
5f123fbd8 [PATCH] merge kob... |
467 |
|
5f123fbd8 [PATCH] merge kob... |
468 469 |
/* search the kset we belong to */ top_kobj = kobj; |
ccd490a3c Driver core: kern... |
470 |
while (!top_kobj->kset && top_kobj->parent) |
14193fb91 Kobject: kobject_... |
471 |
top_kobj = top_kobj->parent; |
ccd490a3c Driver core: kern... |
472 |
|
542cfce6f kobject: kobject_... |
473 |
if (!top_kobj->kset) { |
9f66fa2a4 kobject: clean up... |
474 475 476 |
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent " "without kset! ", kobject_name(kobj), kobj, |
810304db7 lib: replace rema... |
477 |
__func__); |
542cfce6f kobject: kobject_... |
478 479 |
return -EINVAL; } |
1da177e4c Linux-2.6.12-rc2 |
480 |
|
5f123fbd8 [PATCH] merge kob... |
481 |
kset = top_kobj->kset; |
312c004d3 [PATCH] driver co... |
482 |
uevent_ops = kset->uevent_ops; |
1da177e4c Linux-2.6.12-rc2 |
483 |
|
f67f129e5 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 Driver core: chan... |
492 |
/* skip the event, if the filter returns zero. */ |
312c004d3 [PATCH] driver co... |
493 |
if (uevent_ops && uevent_ops->filter) |
542cfce6f kobject: kobject_... |
494 |
if (!uevent_ops->filter(kset, kobj)) { |
9f66fa2a4 kobject: clean up... |
495 496 497 |
pr_debug("kobject: '%s' (%p): %s: filter function " "caused the event to drop! ", |
810304db7 lib: replace rema... |
498 |
kobject_name(kobj), kobj, __func__); |
542cfce6f kobject: kobject_... |
499 500 |
return 0; } |
1da177e4c Linux-2.6.12-rc2 |
501 |
|
864062457 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 kobject: clean up... |
508 509 510 |
pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the " "event to drop! ", kobject_name(kobj), kobj, |
810304db7 lib: replace rema... |
511 |
__func__); |
864062457 driver core: fix ... |
512 513 |
return 0; } |
7eff2e7a8 Driver core: chan... |
514 515 516 |
/* environment buffer */ env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); if (!env) |
542cfce6f kobject: kobject_... |
517 |
return -ENOMEM; |
1da177e4c Linux-2.6.12-rc2 |
518 |
|
5f123fbd8 [PATCH] merge kob... |
519 520 |
/* complete object path */ devpath = kobject_get_path(kobj, GFP_KERNEL); |
542cfce6f kobject: kobject_... |
521 522 |
if (!devpath) { retval = -ENOENT; |
5f123fbd8 [PATCH] merge kob... |
523 |
goto exit; |
542cfce6f kobject: kobject_... |
524 |
} |
1da177e4c Linux-2.6.12-rc2 |
525 |
|
5f123fbd8 [PATCH] merge kob... |
526 |
/* default keys */ |
7eff2e7a8 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 uevent: don't pas... |
540 |
retval = add_uevent_var(env, "%s", envp_ext[i]); |
7eff2e7a8 Driver core: chan... |
541 542 543 544 |
if (retval) goto exit; } } |
1da177e4c Linux-2.6.12-rc2 |
545 |
|
5f123fbd8 [PATCH] merge kob... |
546 |
/* let the kset specific function add its stuff */ |
312c004d3 [PATCH] driver co... |
547 |
if (uevent_ops && uevent_ops->uevent) { |
7eff2e7a8 Driver core: chan... |
548 |
retval = uevent_ops->uevent(kset, kobj, env); |
1da177e4c Linux-2.6.12-rc2 |
549 |
if (retval) { |
9f66fa2a4 kobject: clean up... |
550 551 552 |
pr_debug("kobject: '%s' (%p): %s: uevent() returned " "%d ", kobject_name(kobj), kobj, |
810304db7 lib: replace rema... |
553 |
__func__, retval); |
1da177e4c Linux-2.6.12-rc2 |
554 555 556 |
goto exit; } } |
6878e7de6 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 Kobject: auto-cle... |
566 |
kobj->state_add_uevent_sent = 1; |
6878e7de6 driver core: supp... |
567 |
break; |
6878e7de6 driver core: supp... |
568 569 570 571 572 573 574 |
case KOBJ_UNBIND: zap_modalias_env(env); break; default: break; } |
0f4dafc05 Kobject: auto-cle... |
575 |
|
7b60a18da uevent: send even... |
576 |
mutex_lock(&uevent_sock_mutex); |
7eff2e7a8 Driver core: chan... |
577 |
/* we will send an event, so request a new sequence number */ |
e0d70bcb3 kobject: drop unn... |
578 |
retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum); |
7b60a18da uevent: send even... |
579 580 |
if (retval) { mutex_unlock(&uevent_sock_mutex); |
7eff2e7a8 Driver core: chan... |
581 |
goto exit; |
7b60a18da uevent: send even... |
582 |
} |
16dff336b kobject: add kobj... |
583 584 |
retval = kobject_uevent_net_broadcast(kobj, env, action_string, devpath); |
7b60a18da uevent: send even... |
585 |
mutex_unlock(&uevent_sock_mutex); |
1da177e4c Linux-2.6.12-rc2 |
586 |
|
86d56134f kobject: Make sup... |
587 |
#ifdef CONFIG_UEVENT_HELPER |
5f123fbd8 [PATCH] merge kob... |
588 |
/* call uevent_helper, usually only enabled during early boot */ |
417daa1e8 hotplug: netns aw... |
589 |
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { |
bcccff93a kobject: don't bl... |
590 |
struct subprocess_info *info; |
1da177e4c Linux-2.6.12-rc2 |
591 |
|
7eff2e7a8 Driver core: chan... |
592 593 594 |
retval = add_uevent_var(env, "HOME=/"); if (retval) goto exit; |
e374a2bfe Kobject: fix codi... |
595 596 |
retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); |
7eff2e7a8 Driver core: chan... |
597 598 |
if (retval) goto exit; |
bcccff93a kobject: don't bl... |
599 600 601 |
retval = init_uevent_argv(env, subsystem); if (retval) goto exit; |
7eff2e7a8 Driver core: chan... |
602 |
|
bcccff93a 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 [PATCH] merge kob... |
611 |
} |
86d56134f kobject: Make sup... |
612 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
613 614 |
exit: |
5f123fbd8 [PATCH] merge kob... |
615 |
kfree(devpath); |
7eff2e7a8 Driver core: chan... |
616 |
kfree(env); |
542cfce6f kobject: kobject_... |
617 |
return retval; |
1da177e4c Linux-2.6.12-rc2 |
618 |
} |
8a82472f8 driver core: Intr... |
619 620 621 |
EXPORT_SYMBOL_GPL(kobject_uevent_env); /** |
f6e6e7799 kobject_uevent: f... |
622 |
* kobject_uevent - notify userspace by sending an uevent |
8a82472f8 driver core: Intr... |
623 |
* |
8a82472f8 driver core: Intr... |
624 |
* @kobj: struct kobject that the action is happening to |
edbbf994b kobject: improve ... |
625 |
* @action: action that is happening |
542cfce6f kobject: kobject_... |
626 627 628 |
* * Returns 0 if kobject_uevent() is completed with success or the * corresponding error when it fails. |
8a82472f8 driver core: Intr... |
629 |
*/ |
542cfce6f kobject: kobject_... |
630 |
int kobject_uevent(struct kobject *kobj, enum kobject_action action) |
8a82472f8 driver core: Intr... |
631 |
{ |
542cfce6f kobject: kobject_... |
632 |
return kobject_uevent_env(kobj, action, NULL); |
8a82472f8 driver core: Intr... |
633 |
} |
312c004d3 [PATCH] driver co... |
634 |
EXPORT_SYMBOL_GPL(kobject_uevent); |
1da177e4c Linux-2.6.12-rc2 |
635 636 |
/** |
7eff2e7a8 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 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 Driver core: chan... |
644 |
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) |
1da177e4c Linux-2.6.12-rc2 |
645 646 |
{ va_list args; |
7eff2e7a8 Driver core: chan... |
647 |
int len; |
1da177e4c Linux-2.6.12-rc2 |
648 |
|
7eff2e7a8 Driver core: chan... |
649 |
if (env->envp_idx >= ARRAY_SIZE(env->envp)) { |
5cd2b459d Use WARN() in lib/ |
650 651 |
WARN(1, KERN_ERR "add_uevent_var: too many keys "); |
1da177e4c Linux-2.6.12-rc2 |
652 |
return -ENOMEM; |
7eff2e7a8 Driver core: chan... |
653 |
} |
1da177e4c Linux-2.6.12-rc2 |
654 655 |
va_start(args, format); |
7eff2e7a8 Driver core: chan... |
656 657 658 |
len = vsnprintf(&env->buf[env->buflen], sizeof(env->buf) - env->buflen, format, args); |
1da177e4c Linux-2.6.12-rc2 |
659 |
va_end(args); |
7eff2e7a8 Driver core: chan... |
660 |
if (len >= (sizeof(env->buf) - env->buflen)) { |
5cd2b459d Use WARN() in lib/ |
661 662 |
WARN(1, KERN_ERR "add_uevent_var: buffer size too small "); |
1da177e4c Linux-2.6.12-rc2 |
663 |
return -ENOMEM; |
7eff2e7a8 Driver core: chan... |
664 |
} |
1da177e4c Linux-2.6.12-rc2 |
665 |
|
7eff2e7a8 Driver core: chan... |
666 667 |
env->envp[env->envp_idx++] = &env->buf[env->buflen]; env->buflen += len + 1; |
1da177e4c Linux-2.6.12-rc2 |
668 669 |
return 0; } |
312c004d3 [PATCH] driver co... |
670 |
EXPORT_SYMBOL_GPL(add_uevent_var); |
1da177e4c Linux-2.6.12-rc2 |
671 |
|
4d17ffda3 [PATCH] Kobject: ... |
672 |
#if defined(CONFIG_NET) |
692ec06d7 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 kobject: Send hot... |
747 |
static int uevent_net_init(struct net *net) |
5f123fbd8 [PATCH] merge kob... |
748 |
{ |
07e98962f kobject: Send hot... |
749 |
struct uevent_sock *ue_sk; |
a31f2d17b netlink: add netl... |
750 751 |
struct netlink_kernel_cfg cfg = { .groups = 1, |
692ec06d7 netns: send ueven... |
752 753 |
.input = uevent_net_rcv, .flags = NL_CFG_F_NONROOT_RECV |
a31f2d17b netlink: add netl... |
754 |
}; |
07e98962f kobject: Send hot... |
755 756 757 758 |
ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); if (!ue_sk) return -ENOMEM; |
9f00d9776 netlink: hide str... |
759 |
ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg); |
07e98962f kobject: Send hot... |
760 |
if (!ue_sk->sk) { |
b3fa29ad8 kobject: to repal... |
761 762 |
pr_err("kobject_uevent: unable to create netlink socket! "); |
743db2d90 kobject: free mem... |
763 |
kfree(ue_sk); |
5f123fbd8 [PATCH] merge kob... |
764 765 |
return -ENODEV; } |
94e5e3087 net: add uevent s... |
766 767 |
net->uevent_sock = ue_sk; |
a3498436b 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 [PATCH] merge kob... |
774 775 |
return 0; } |
07e98962f kobject: Send hot... |
776 777 |
static void uevent_net_exit(struct net *net) { |
94e5e3087 net: add uevent s... |
778 |
struct uevent_sock *ue_sk = net->uevent_sock; |
07e98962f kobject: Send hot... |
779 |
|
a3498436b 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 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 kobject: Send hot... |
797 798 |
return register_pernet_subsys(&uevent_net_ops); } |
5f123fbd8 [PATCH] merge kob... |
799 |
postcore_initcall(kobject_uevent_init); |
4d17ffda3 [PATCH] Kobject: ... |
800 |
#endif |