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