18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * kernel userspace event delivery
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Novell, Inc.  All rights reserved.
78c2ecf20Sopenharmony_ci * Copyright (C) 2004 IBM, Inc. All rights reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Authors:
108c2ecf20Sopenharmony_ci *	Robert Love		<rml@novell.com>
118c2ecf20Sopenharmony_ci *	Kay Sievers		<kay.sievers@vrfy.org>
128c2ecf20Sopenharmony_ci *	Arjan van de Ven	<arjanv@redhat.com>
138c2ecf20Sopenharmony_ci *	Greg Kroah-Hartman	<greg@kroah.com>
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/kobject.h>
198c2ecf20Sopenharmony_ci#include <linux/export.h>
208c2ecf20Sopenharmony_ci#include <linux/kmod.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/socket.h>
238c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
248c2ecf20Sopenharmony_ci#include <linux/netlink.h>
258c2ecf20Sopenharmony_ci#include <linux/uidgid.h>
268c2ecf20Sopenharmony_ci#include <linux/uuid.h>
278c2ecf20Sopenharmony_ci#include <linux/ctype.h>
288c2ecf20Sopenharmony_ci#include <net/sock.h>
298c2ecf20Sopenharmony_ci#include <net/netlink.h>
308c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciu64 uevent_seqnum;
348c2ecf20Sopenharmony_ci#ifdef CONFIG_UEVENT_HELPER
358c2ecf20Sopenharmony_cichar uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
368c2ecf20Sopenharmony_ci#endif
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct uevent_sock {
398c2ecf20Sopenharmony_ci	struct list_head list;
408c2ecf20Sopenharmony_ci	struct sock *sk;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#ifdef CONFIG_NET
448c2ecf20Sopenharmony_cistatic LIST_HEAD(uevent_sock_list);
458c2ecf20Sopenharmony_ci#endif
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* This lock protects uevent_seqnum and uevent_sock_list */
488c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(uevent_sock_mutex);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* the strings here must match the enum in include/linux/kobject.h */
518c2ecf20Sopenharmony_cistatic const char *kobject_actions[] = {
528c2ecf20Sopenharmony_ci	[KOBJ_ADD] =		"add",
538c2ecf20Sopenharmony_ci	[KOBJ_REMOVE] =		"remove",
548c2ecf20Sopenharmony_ci	[KOBJ_CHANGE] =		"change",
558c2ecf20Sopenharmony_ci	[KOBJ_MOVE] =		"move",
568c2ecf20Sopenharmony_ci	[KOBJ_ONLINE] =		"online",
578c2ecf20Sopenharmony_ci	[KOBJ_OFFLINE] =	"offline",
588c2ecf20Sopenharmony_ci	[KOBJ_BIND] =		"bind",
598c2ecf20Sopenharmony_ci	[KOBJ_UNBIND] =		"unbind",
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int kobject_action_type(const char *buf, size_t count,
638c2ecf20Sopenharmony_ci			       enum kobject_action *type,
648c2ecf20Sopenharmony_ci			       const char **args)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	enum kobject_action action;
678c2ecf20Sopenharmony_ci	size_t count_first;
688c2ecf20Sopenharmony_ci	const char *args_start;
698c2ecf20Sopenharmony_ci	int ret = -EINVAL;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
728c2ecf20Sopenharmony_ci		count--;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (!count)
758c2ecf20Sopenharmony_ci		goto out;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	args_start = strnchr(buf, count, ' ');
788c2ecf20Sopenharmony_ci	if (args_start) {
798c2ecf20Sopenharmony_ci		count_first = args_start - buf;
808c2ecf20Sopenharmony_ci		args_start = args_start + 1;
818c2ecf20Sopenharmony_ci	} else
828c2ecf20Sopenharmony_ci		count_first = count;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
858c2ecf20Sopenharmony_ci		if (strncmp(kobject_actions[action], buf, count_first) != 0)
868c2ecf20Sopenharmony_ci			continue;
878c2ecf20Sopenharmony_ci		if (kobject_actions[action][count_first] != '\0')
888c2ecf20Sopenharmony_ci			continue;
898c2ecf20Sopenharmony_ci		if (args)
908c2ecf20Sopenharmony_ci			*args = args_start;
918c2ecf20Sopenharmony_ci		*type = action;
928c2ecf20Sopenharmony_ci		ret = 0;
938c2ecf20Sopenharmony_ci		break;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ciout:
968c2ecf20Sopenharmony_ci	return ret;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const char *action_arg_word_end(const char *buf, const char *buf_end,
1008c2ecf20Sopenharmony_ci				       char delim)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	const char *next = buf;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	while (next <= buf_end && *next != delim)
1058c2ecf20Sopenharmony_ci		if (!isalnum(*next++))
1068c2ecf20Sopenharmony_ci			return NULL;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (next == buf)
1098c2ecf20Sopenharmony_ci		return NULL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return next;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int kobject_action_args(const char *buf, size_t count,
1158c2ecf20Sopenharmony_ci			       struct kobj_uevent_env **ret_env)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct kobj_uevent_env *env = NULL;
1188c2ecf20Sopenharmony_ci	const char *next, *buf_end, *key;
1198c2ecf20Sopenharmony_ci	int key_len;
1208c2ecf20Sopenharmony_ci	int r = -EINVAL;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (count && (buf[count - 1] == '\n' || buf[count - 1] == '\0'))
1238c2ecf20Sopenharmony_ci		count--;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (!count)
1268c2ecf20Sopenharmony_ci		return -EINVAL;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	env = kzalloc(sizeof(*env), GFP_KERNEL);
1298c2ecf20Sopenharmony_ci	if (!env)
1308c2ecf20Sopenharmony_ci		return -ENOMEM;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* first arg is UUID */
1338c2ecf20Sopenharmony_ci	if (count < UUID_STRING_LEN || !uuid_is_valid(buf) ||
1348c2ecf20Sopenharmony_ci	    add_uevent_var(env, "SYNTH_UUID=%.*s", UUID_STRING_LEN, buf))
1358c2ecf20Sopenharmony_ci		goto out;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/*
1388c2ecf20Sopenharmony_ci	 * the rest are custom environment variables in KEY=VALUE
1398c2ecf20Sopenharmony_ci	 * format with ' ' delimiter between each KEY=VALUE pair
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	next = buf + UUID_STRING_LEN;
1428c2ecf20Sopenharmony_ci	buf_end = buf + count - 1;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	while (next <= buf_end) {
1458c2ecf20Sopenharmony_ci		if (*next != ' ')
1468c2ecf20Sopenharmony_ci			goto out;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		/* skip the ' ', key must follow */
1498c2ecf20Sopenharmony_ci		key = ++next;
1508c2ecf20Sopenharmony_ci		if (key > buf_end)
1518c2ecf20Sopenharmony_ci			goto out;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		buf = next;
1548c2ecf20Sopenharmony_ci		next = action_arg_word_end(buf, buf_end, '=');
1558c2ecf20Sopenharmony_ci		if (!next || next > buf_end || *next != '=')
1568c2ecf20Sopenharmony_ci			goto out;
1578c2ecf20Sopenharmony_ci		key_len = next - buf;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		/* skip the '=', value must follow */
1608c2ecf20Sopenharmony_ci		if (++next > buf_end)
1618c2ecf20Sopenharmony_ci			goto out;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		buf = next;
1648c2ecf20Sopenharmony_ci		next = action_arg_word_end(buf, buf_end, ' ');
1658c2ecf20Sopenharmony_ci		if (!next)
1668c2ecf20Sopenharmony_ci			goto out;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		if (add_uevent_var(env, "SYNTH_ARG_%.*s=%.*s",
1698c2ecf20Sopenharmony_ci				   key_len, key, (int) (next - buf), buf))
1708c2ecf20Sopenharmony_ci			goto out;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	r = 0;
1748c2ecf20Sopenharmony_ciout:
1758c2ecf20Sopenharmony_ci	if (r)
1768c2ecf20Sopenharmony_ci		kfree(env);
1778c2ecf20Sopenharmony_ci	else
1788c2ecf20Sopenharmony_ci		*ret_env = env;
1798c2ecf20Sopenharmony_ci	return r;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * kobject_synth_uevent - send synthetic uevent with arguments
1848c2ecf20Sopenharmony_ci *
1858c2ecf20Sopenharmony_ci * @kobj: struct kobject for which synthetic uevent is to be generated
1868c2ecf20Sopenharmony_ci * @buf: buffer containing action type and action args, newline is ignored
1878c2ecf20Sopenharmony_ci * @count: length of buffer
1888c2ecf20Sopenharmony_ci *
1898c2ecf20Sopenharmony_ci * Returns 0 if kobject_synthetic_uevent() is completed with success or the
1908c2ecf20Sopenharmony_ci * corresponding error when it fails.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_ciint kobject_synth_uevent(struct kobject *kobj, const char *buf, size_t count)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	char *no_uuid_envp[] = { "SYNTH_UUID=0", NULL };
1958c2ecf20Sopenharmony_ci	enum kobject_action action;
1968c2ecf20Sopenharmony_ci	const char *action_args;
1978c2ecf20Sopenharmony_ci	struct kobj_uevent_env *env;
1988c2ecf20Sopenharmony_ci	const char *msg = NULL, *devpath;
1998c2ecf20Sopenharmony_ci	int r;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	r = kobject_action_type(buf, count, &action, &action_args);
2028c2ecf20Sopenharmony_ci	if (r) {
2038c2ecf20Sopenharmony_ci		msg = "unknown uevent action string";
2048c2ecf20Sopenharmony_ci		goto out;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (!action_args) {
2088c2ecf20Sopenharmony_ci		r = kobject_uevent_env(kobj, action, no_uuid_envp);
2098c2ecf20Sopenharmony_ci		goto out;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	r = kobject_action_args(action_args,
2138c2ecf20Sopenharmony_ci				count - (action_args - buf), &env);
2148c2ecf20Sopenharmony_ci	if (r == -EINVAL) {
2158c2ecf20Sopenharmony_ci		msg = "incorrect uevent action arguments";
2168c2ecf20Sopenharmony_ci		goto out;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (r)
2208c2ecf20Sopenharmony_ci		goto out;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	r = kobject_uevent_env(kobj, action, env->envp);
2238c2ecf20Sopenharmony_ci	kfree(env);
2248c2ecf20Sopenharmony_ciout:
2258c2ecf20Sopenharmony_ci	if (r) {
2268c2ecf20Sopenharmony_ci		devpath = kobject_get_path(kobj, GFP_KERNEL);
2278c2ecf20Sopenharmony_ci		pr_warn("synth uevent: %s: %s\n",
2288c2ecf20Sopenharmony_ci		       devpath ?: "unknown device",
2298c2ecf20Sopenharmony_ci		       msg ?: "failed to send uevent");
2308c2ecf20Sopenharmony_ci		kfree(devpath);
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	return r;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci#ifdef CONFIG_UEVENT_HELPER
2368c2ecf20Sopenharmony_cistatic int kobj_usermode_filter(struct kobject *kobj)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	const struct kobj_ns_type_operations *ops;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	ops = kobj_ns_ops(kobj);
2418c2ecf20Sopenharmony_ci	if (ops) {
2428c2ecf20Sopenharmony_ci		const void *init_ns, *ns;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		ns = kobj->ktype->namespace(kobj);
2458c2ecf20Sopenharmony_ci		init_ns = ops->initial_ns();
2468c2ecf20Sopenharmony_ci		return ns != init_ns;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	int buffer_size = sizeof(env->buf) - env->buflen;
2558c2ecf20Sopenharmony_ci	int len;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	len = strlcpy(&env->buf[env->buflen], subsystem, buffer_size);
2588c2ecf20Sopenharmony_ci	if (len >= buffer_size) {
2598c2ecf20Sopenharmony_ci		pr_warn("init_uevent_argv: buffer size of %d too small, needed %d\n",
2608c2ecf20Sopenharmony_ci			buffer_size, len);
2618c2ecf20Sopenharmony_ci		return -ENOMEM;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	env->argv[0] = uevent_helper;
2658c2ecf20Sopenharmony_ci	env->argv[1] = &env->buf[env->buflen];
2668c2ecf20Sopenharmony_ci	env->argv[2] = NULL;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	env->buflen += len + 1;
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic void cleanup_uevent_env(struct subprocess_info *info)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	kfree(info->data);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci#endif
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci#ifdef CONFIG_NET
2798c2ecf20Sopenharmony_cistatic struct sk_buff *alloc_uevent_skb(struct kobj_uevent_env *env,
2808c2ecf20Sopenharmony_ci					const char *action_string,
2818c2ecf20Sopenharmony_ci					const char *devpath)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct netlink_skb_parms *parms;
2848c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
2858c2ecf20Sopenharmony_ci	char *scratch;
2868c2ecf20Sopenharmony_ci	size_t len;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* allocate message with maximum possible size */
2898c2ecf20Sopenharmony_ci	len = strlen(action_string) + strlen(devpath) + 2;
2908c2ecf20Sopenharmony_ci	skb = alloc_skb(len + env->buflen, GFP_KERNEL);
2918c2ecf20Sopenharmony_ci	if (!skb)
2928c2ecf20Sopenharmony_ci		return NULL;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* add header */
2958c2ecf20Sopenharmony_ci	scratch = skb_put(skb, len);
2968c2ecf20Sopenharmony_ci	sprintf(scratch, "%s@%s", action_string, devpath);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	skb_put_data(skb, env->buf, env->buflen);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	parms = &NETLINK_CB(skb);
3018c2ecf20Sopenharmony_ci	parms->creds.uid = GLOBAL_ROOT_UID;
3028c2ecf20Sopenharmony_ci	parms->creds.gid = GLOBAL_ROOT_GID;
3038c2ecf20Sopenharmony_ci	parms->dst_group = 1;
3048c2ecf20Sopenharmony_ci	parms->portid = 0;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return skb;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
3108c2ecf20Sopenharmony_ci					 const char *action_string,
3118c2ecf20Sopenharmony_ci					 const char *devpath)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
3148c2ecf20Sopenharmony_ci	struct uevent_sock *ue_sk;
3158c2ecf20Sopenharmony_ci	int retval = 0;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* send netlink message */
3188c2ecf20Sopenharmony_ci	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
3198c2ecf20Sopenharmony_ci		struct sock *uevent_sock = ue_sk->sk;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		if (!netlink_has_listeners(uevent_sock, 1))
3228c2ecf20Sopenharmony_ci			continue;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		if (!skb) {
3258c2ecf20Sopenharmony_ci			retval = -ENOMEM;
3268c2ecf20Sopenharmony_ci			skb = alloc_uevent_skb(env, action_string, devpath);
3278c2ecf20Sopenharmony_ci			if (!skb)
3288c2ecf20Sopenharmony_ci				continue;
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		retval = netlink_broadcast(uevent_sock, skb_get(skb), 0, 1,
3328c2ecf20Sopenharmony_ci					   GFP_KERNEL);
3338c2ecf20Sopenharmony_ci		/* ENOBUFS should be handled in userspace */
3348c2ecf20Sopenharmony_ci		if (retval == -ENOBUFS || retval == -ESRCH)
3358c2ecf20Sopenharmony_ci			retval = 0;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	consume_skb(skb);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return retval;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic int uevent_net_broadcast_tagged(struct sock *usk,
3438c2ecf20Sopenharmony_ci				       struct kobj_uevent_env *env,
3448c2ecf20Sopenharmony_ci				       const char *action_string,
3458c2ecf20Sopenharmony_ci				       const char *devpath)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct user_namespace *owning_user_ns = sock_net(usk)->user_ns;
3488c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
3498c2ecf20Sopenharmony_ci	int ret = 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	skb = alloc_uevent_skb(env, action_string, devpath);
3528c2ecf20Sopenharmony_ci	if (!skb)
3538c2ecf20Sopenharmony_ci		return -ENOMEM;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* fix credentials */
3568c2ecf20Sopenharmony_ci	if (owning_user_ns != &init_user_ns) {
3578c2ecf20Sopenharmony_ci		struct netlink_skb_parms *parms = &NETLINK_CB(skb);
3588c2ecf20Sopenharmony_ci		kuid_t root_uid;
3598c2ecf20Sopenharmony_ci		kgid_t root_gid;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		/* fix uid */
3628c2ecf20Sopenharmony_ci		root_uid = make_kuid(owning_user_ns, 0);
3638c2ecf20Sopenharmony_ci		if (uid_valid(root_uid))
3648c2ecf20Sopenharmony_ci			parms->creds.uid = root_uid;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		/* fix gid */
3678c2ecf20Sopenharmony_ci		root_gid = make_kgid(owning_user_ns, 0);
3688c2ecf20Sopenharmony_ci		if (gid_valid(root_gid))
3698c2ecf20Sopenharmony_ci			parms->creds.gid = root_gid;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	ret = netlink_broadcast(usk, skb, 0, 1, GFP_KERNEL);
3738c2ecf20Sopenharmony_ci	/* ENOBUFS should be handled in userspace */
3748c2ecf20Sopenharmony_ci	if (ret == -ENOBUFS || ret == -ESRCH)
3758c2ecf20Sopenharmony_ci		ret = 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return ret;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci#endif
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int kobject_uevent_net_broadcast(struct kobject *kobj,
3828c2ecf20Sopenharmony_ci					struct kobj_uevent_env *env,
3838c2ecf20Sopenharmony_ci					const char *action_string,
3848c2ecf20Sopenharmony_ci					const char *devpath)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	int ret = 0;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci#ifdef CONFIG_NET
3898c2ecf20Sopenharmony_ci	const struct kobj_ns_type_operations *ops;
3908c2ecf20Sopenharmony_ci	const struct net *net = NULL;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	ops = kobj_ns_ops(kobj);
3938c2ecf20Sopenharmony_ci	if (!ops && kobj->kset) {
3948c2ecf20Sopenharmony_ci		struct kobject *ksobj = &kobj->kset->kobj;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		if (ksobj->parent != NULL)
3978c2ecf20Sopenharmony_ci			ops = kobj_ns_ops(ksobj->parent);
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* kobjects currently only carry network namespace tags and they
4018c2ecf20Sopenharmony_ci	 * are the only tag relevant here since we want to decide which
4028c2ecf20Sopenharmony_ci	 * network namespaces to broadcast the uevent into.
4038c2ecf20Sopenharmony_ci	 */
4048c2ecf20Sopenharmony_ci	if (ops && ops->netlink_ns && kobj->ktype->namespace)
4058c2ecf20Sopenharmony_ci		if (ops->type == KOBJ_NS_TYPE_NET)
4068c2ecf20Sopenharmony_ci			net = kobj->ktype->namespace(kobj);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (!net)
4098c2ecf20Sopenharmony_ci		ret = uevent_net_broadcast_untagged(env, action_string,
4108c2ecf20Sopenharmony_ci						    devpath);
4118c2ecf20Sopenharmony_ci	else
4128c2ecf20Sopenharmony_ci		ret = uevent_net_broadcast_tagged(net->uevent_sock->sk, env,
4138c2ecf20Sopenharmony_ci						  action_string, devpath);
4148c2ecf20Sopenharmony_ci#endif
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return ret;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic void zap_modalias_env(struct kobj_uevent_env *env)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	static const char modalias_prefix[] = "MODALIAS=";
4228c2ecf20Sopenharmony_ci	size_t len;
4238c2ecf20Sopenharmony_ci	int i, j;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	for (i = 0; i < env->envp_idx;) {
4268c2ecf20Sopenharmony_ci		if (strncmp(env->envp[i], modalias_prefix,
4278c2ecf20Sopenharmony_ci			    sizeof(modalias_prefix) - 1)) {
4288c2ecf20Sopenharmony_ci			i++;
4298c2ecf20Sopenharmony_ci			continue;
4308c2ecf20Sopenharmony_ci		}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		len = strlen(env->envp[i]) + 1;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		if (i != env->envp_idx - 1) {
4358c2ecf20Sopenharmony_ci			/* @env->envp[] contains pointers to @env->buf[]
4368c2ecf20Sopenharmony_ci			 * with @env->buflen chars, and we are removing
4378c2ecf20Sopenharmony_ci			 * variable MODALIAS here pointed by @env->envp[i]
4388c2ecf20Sopenharmony_ci			 * with length @len as shown below:
4398c2ecf20Sopenharmony_ci			 *
4408c2ecf20Sopenharmony_ci			 * 0               @env->buf[]      @env->buflen
4418c2ecf20Sopenharmony_ci			 * ---------------------------------------------
4428c2ecf20Sopenharmony_ci			 * ^             ^              ^              ^
4438c2ecf20Sopenharmony_ci			 * |             |->   @len   <-| target block |
4448c2ecf20Sopenharmony_ci			 * @env->envp[0] @env->envp[i]  @env->envp[i + 1]
4458c2ecf20Sopenharmony_ci			 *
4468c2ecf20Sopenharmony_ci			 * so the "target block" indicated above is moved
4478c2ecf20Sopenharmony_ci			 * backward by @len, and its right size is
4488c2ecf20Sopenharmony_ci			 * @env->buflen - (@env->envp[i + 1] - @env->envp[0]).
4498c2ecf20Sopenharmony_ci			 */
4508c2ecf20Sopenharmony_ci			memmove(env->envp[i], env->envp[i + 1],
4518c2ecf20Sopenharmony_ci				env->buflen - (env->envp[i + 1] - env->envp[0]));
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci			for (j = i; j < env->envp_idx - 1; j++)
4548c2ecf20Sopenharmony_ci				env->envp[j] = env->envp[j + 1] - len;
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		env->envp_idx--;
4588c2ecf20Sopenharmony_ci		env->buflen -= len;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/**
4638c2ecf20Sopenharmony_ci * kobject_uevent_env - send an uevent with environmental data
4648c2ecf20Sopenharmony_ci *
4658c2ecf20Sopenharmony_ci * @kobj: struct kobject that the action is happening to
4668c2ecf20Sopenharmony_ci * @action: action that is happening
4678c2ecf20Sopenharmony_ci * @envp_ext: pointer to environmental data
4688c2ecf20Sopenharmony_ci *
4698c2ecf20Sopenharmony_ci * Returns 0 if kobject_uevent_env() is completed with success or the
4708c2ecf20Sopenharmony_ci * corresponding error when it fails.
4718c2ecf20Sopenharmony_ci */
4728c2ecf20Sopenharmony_ciint kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
4738c2ecf20Sopenharmony_ci		       char *envp_ext[])
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct kobj_uevent_env *env;
4768c2ecf20Sopenharmony_ci	const char *action_string = kobject_actions[action];
4778c2ecf20Sopenharmony_ci	const char *devpath = NULL;
4788c2ecf20Sopenharmony_ci	const char *subsystem;
4798c2ecf20Sopenharmony_ci	struct kobject *top_kobj;
4808c2ecf20Sopenharmony_ci	struct kset *kset;
4818c2ecf20Sopenharmony_ci	const struct kset_uevent_ops *uevent_ops;
4828c2ecf20Sopenharmony_ci	int i = 0;
4838c2ecf20Sopenharmony_ci	int retval = 0;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/*
4868c2ecf20Sopenharmony_ci	 * Mark "remove" event done regardless of result, for some subsystems
4878c2ecf20Sopenharmony_ci	 * do not want to re-trigger "remove" event via automatic cleanup.
4888c2ecf20Sopenharmony_ci	 */
4898c2ecf20Sopenharmony_ci	if (action == KOBJ_REMOVE)
4908c2ecf20Sopenharmony_ci		kobj->state_remove_uevent_sent = 1;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	pr_debug("kobject: '%s' (%p): %s\n",
4938c2ecf20Sopenharmony_ci		 kobject_name(kobj), kobj, __func__);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* search the kset we belong to */
4968c2ecf20Sopenharmony_ci	top_kobj = kobj;
4978c2ecf20Sopenharmony_ci	while (!top_kobj->kset && top_kobj->parent)
4988c2ecf20Sopenharmony_ci		top_kobj = top_kobj->parent;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (!top_kobj->kset) {
5018c2ecf20Sopenharmony_ci		pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
5028c2ecf20Sopenharmony_ci			 "without kset!\n", kobject_name(kobj), kobj,
5038c2ecf20Sopenharmony_ci			 __func__);
5048c2ecf20Sopenharmony_ci		return -EINVAL;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	kset = top_kobj->kset;
5088c2ecf20Sopenharmony_ci	uevent_ops = kset->uevent_ops;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* skip the event, if uevent_suppress is set*/
5118c2ecf20Sopenharmony_ci	if (kobj->uevent_suppress) {
5128c2ecf20Sopenharmony_ci		pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
5138c2ecf20Sopenharmony_ci				 "caused the event to drop!\n",
5148c2ecf20Sopenharmony_ci				 kobject_name(kobj), kobj, __func__);
5158c2ecf20Sopenharmony_ci		return 0;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci	/* skip the event, if the filter returns zero. */
5188c2ecf20Sopenharmony_ci	if (uevent_ops && uevent_ops->filter)
5198c2ecf20Sopenharmony_ci		if (!uevent_ops->filter(kset, kobj)) {
5208c2ecf20Sopenharmony_ci			pr_debug("kobject: '%s' (%p): %s: filter function "
5218c2ecf20Sopenharmony_ci				 "caused the event to drop!\n",
5228c2ecf20Sopenharmony_ci				 kobject_name(kobj), kobj, __func__);
5238c2ecf20Sopenharmony_ci			return 0;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* originating subsystem */
5278c2ecf20Sopenharmony_ci	if (uevent_ops && uevent_ops->name)
5288c2ecf20Sopenharmony_ci		subsystem = uevent_ops->name(kset, kobj);
5298c2ecf20Sopenharmony_ci	else
5308c2ecf20Sopenharmony_ci		subsystem = kobject_name(&kset->kobj);
5318c2ecf20Sopenharmony_ci	if (!subsystem) {
5328c2ecf20Sopenharmony_ci		pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
5338c2ecf20Sopenharmony_ci			 "event to drop!\n", kobject_name(kobj), kobj,
5348c2ecf20Sopenharmony_ci			 __func__);
5358c2ecf20Sopenharmony_ci		return 0;
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/* environment buffer */
5398c2ecf20Sopenharmony_ci	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
5408c2ecf20Sopenharmony_ci	if (!env)
5418c2ecf20Sopenharmony_ci		return -ENOMEM;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* complete object path */
5448c2ecf20Sopenharmony_ci	devpath = kobject_get_path(kobj, GFP_KERNEL);
5458c2ecf20Sopenharmony_ci	if (!devpath) {
5468c2ecf20Sopenharmony_ci		retval = -ENOENT;
5478c2ecf20Sopenharmony_ci		goto exit;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	/* default keys */
5518c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "ACTION=%s", action_string);
5528c2ecf20Sopenharmony_ci	if (retval)
5538c2ecf20Sopenharmony_ci		goto exit;
5548c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "DEVPATH=%s", devpath);
5558c2ecf20Sopenharmony_ci	if (retval)
5568c2ecf20Sopenharmony_ci		goto exit;
5578c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
5588c2ecf20Sopenharmony_ci	if (retval)
5598c2ecf20Sopenharmony_ci		goto exit;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* keys passed in from the caller */
5628c2ecf20Sopenharmony_ci	if (envp_ext) {
5638c2ecf20Sopenharmony_ci		for (i = 0; envp_ext[i]; i++) {
5648c2ecf20Sopenharmony_ci			retval = add_uevent_var(env, "%s", envp_ext[i]);
5658c2ecf20Sopenharmony_ci			if (retval)
5668c2ecf20Sopenharmony_ci				goto exit;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* let the kset specific function add its stuff */
5718c2ecf20Sopenharmony_ci	if (uevent_ops && uevent_ops->uevent) {
5728c2ecf20Sopenharmony_ci		retval = uevent_ops->uevent(kset, kobj, env);
5738c2ecf20Sopenharmony_ci		if (retval) {
5748c2ecf20Sopenharmony_ci			pr_debug("kobject: '%s' (%p): %s: uevent() returned "
5758c2ecf20Sopenharmony_ci				 "%d\n", kobject_name(kobj), kobj,
5768c2ecf20Sopenharmony_ci				 __func__, retval);
5778c2ecf20Sopenharmony_ci			goto exit;
5788c2ecf20Sopenharmony_ci		}
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	switch (action) {
5828c2ecf20Sopenharmony_ci	case KOBJ_ADD:
5838c2ecf20Sopenharmony_ci		/*
5848c2ecf20Sopenharmony_ci		 * Mark "add" event so we can make sure we deliver "remove"
5858c2ecf20Sopenharmony_ci		 * event to userspace during automatic cleanup. If
5868c2ecf20Sopenharmony_ci		 * the object did send an "add" event, "remove" will
5878c2ecf20Sopenharmony_ci		 * automatically generated by the core, if not already done
5888c2ecf20Sopenharmony_ci		 * by the caller.
5898c2ecf20Sopenharmony_ci		 */
5908c2ecf20Sopenharmony_ci		kobj->state_add_uevent_sent = 1;
5918c2ecf20Sopenharmony_ci		break;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	case KOBJ_UNBIND:
5948c2ecf20Sopenharmony_ci		zap_modalias_env(env);
5958c2ecf20Sopenharmony_ci		break;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	default:
5988c2ecf20Sopenharmony_ci		break;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	mutex_lock(&uevent_sock_mutex);
6028c2ecf20Sopenharmony_ci	/* we will send an event, so request a new sequence number */
6038c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum);
6048c2ecf20Sopenharmony_ci	if (retval) {
6058c2ecf20Sopenharmony_ci		mutex_unlock(&uevent_sock_mutex);
6068c2ecf20Sopenharmony_ci		goto exit;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci	retval = kobject_uevent_net_broadcast(kobj, env, action_string,
6098c2ecf20Sopenharmony_ci					      devpath);
6108c2ecf20Sopenharmony_ci	mutex_unlock(&uevent_sock_mutex);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci#ifdef CONFIG_UEVENT_HELPER
6138c2ecf20Sopenharmony_ci	/* call uevent_helper, usually only enabled during early boot */
6148c2ecf20Sopenharmony_ci	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
6158c2ecf20Sopenharmony_ci		struct subprocess_info *info;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		retval = add_uevent_var(env, "HOME=/");
6188c2ecf20Sopenharmony_ci		if (retval)
6198c2ecf20Sopenharmony_ci			goto exit;
6208c2ecf20Sopenharmony_ci		retval = add_uevent_var(env,
6218c2ecf20Sopenharmony_ci					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
6228c2ecf20Sopenharmony_ci		if (retval)
6238c2ecf20Sopenharmony_ci			goto exit;
6248c2ecf20Sopenharmony_ci		retval = init_uevent_argv(env, subsystem);
6258c2ecf20Sopenharmony_ci		if (retval)
6268c2ecf20Sopenharmony_ci			goto exit;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci		retval = -ENOMEM;
6298c2ecf20Sopenharmony_ci		info = call_usermodehelper_setup(env->argv[0], env->argv,
6308c2ecf20Sopenharmony_ci						 env->envp, GFP_KERNEL,
6318c2ecf20Sopenharmony_ci						 NULL, cleanup_uevent_env, env);
6328c2ecf20Sopenharmony_ci		if (info) {
6338c2ecf20Sopenharmony_ci			retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
6348c2ecf20Sopenharmony_ci			env = NULL;	/* freed by cleanup_uevent_env */
6358c2ecf20Sopenharmony_ci		}
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci#endif
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ciexit:
6408c2ecf20Sopenharmony_ci	kfree(devpath);
6418c2ecf20Sopenharmony_ci	kfree(env);
6428c2ecf20Sopenharmony_ci	return retval;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kobject_uevent_env);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci/**
6478c2ecf20Sopenharmony_ci * kobject_uevent - notify userspace by sending an uevent
6488c2ecf20Sopenharmony_ci *
6498c2ecf20Sopenharmony_ci * @kobj: struct kobject that the action is happening to
6508c2ecf20Sopenharmony_ci * @action: action that is happening
6518c2ecf20Sopenharmony_ci *
6528c2ecf20Sopenharmony_ci * Returns 0 if kobject_uevent() is completed with success or the
6538c2ecf20Sopenharmony_ci * corresponding error when it fails.
6548c2ecf20Sopenharmony_ci */
6558c2ecf20Sopenharmony_ciint kobject_uevent(struct kobject *kobj, enum kobject_action action)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	return kobject_uevent_env(kobj, action, NULL);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kobject_uevent);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci/**
6628c2ecf20Sopenharmony_ci * add_uevent_var - add key value string to the environment buffer
6638c2ecf20Sopenharmony_ci * @env: environment buffer structure
6648c2ecf20Sopenharmony_ci * @format: printf format for the key=value pair
6658c2ecf20Sopenharmony_ci *
6668c2ecf20Sopenharmony_ci * Returns 0 if environment variable was added successfully or -ENOMEM
6678c2ecf20Sopenharmony_ci * if no space was available.
6688c2ecf20Sopenharmony_ci */
6698c2ecf20Sopenharmony_ciint add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	va_list args;
6728c2ecf20Sopenharmony_ci	int len;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
6758c2ecf20Sopenharmony_ci		WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
6768c2ecf20Sopenharmony_ci		return -ENOMEM;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	va_start(args, format);
6808c2ecf20Sopenharmony_ci	len = vsnprintf(&env->buf[env->buflen],
6818c2ecf20Sopenharmony_ci			sizeof(env->buf) - env->buflen,
6828c2ecf20Sopenharmony_ci			format, args);
6838c2ecf20Sopenharmony_ci	va_end(args);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	if (len >= (sizeof(env->buf) - env->buflen)) {
6868c2ecf20Sopenharmony_ci		WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
6878c2ecf20Sopenharmony_ci		return -ENOMEM;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	env->envp[env->envp_idx++] = &env->buf[env->buflen];
6918c2ecf20Sopenharmony_ci	env->buflen += len + 1;
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(add_uevent_var);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci#if defined(CONFIG_NET)
6978c2ecf20Sopenharmony_cistatic int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,
6988c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	/* u64 to chars: 2^64 - 1 = 21 chars */
7018c2ecf20Sopenharmony_ci	char buf[sizeof("SEQNUM=") + 21];
7028c2ecf20Sopenharmony_ci	struct sk_buff *skbc;
7038c2ecf20Sopenharmony_ci	int ret;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* bump and prepare sequence number */
7068c2ecf20Sopenharmony_ci	ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu", ++uevent_seqnum);
7078c2ecf20Sopenharmony_ci	if (ret < 0 || (size_t)ret >= sizeof(buf))
7088c2ecf20Sopenharmony_ci		return -ENOMEM;
7098c2ecf20Sopenharmony_ci	ret++;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* verify message does not overflow */
7128c2ecf20Sopenharmony_ci	if ((skb->len + ret) > UEVENT_BUFFER_SIZE) {
7138c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "uevent message too big");
7148c2ecf20Sopenharmony_ci		return -EINVAL;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* copy skb and extend to accommodate sequence number */
7188c2ecf20Sopenharmony_ci	skbc = skb_copy_expand(skb, 0, ret, GFP_KERNEL);
7198c2ecf20Sopenharmony_ci	if (!skbc)
7208c2ecf20Sopenharmony_ci		return -ENOMEM;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* append sequence number */
7238c2ecf20Sopenharmony_ci	skb_put_data(skbc, buf, ret);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	/* remove msg header */
7268c2ecf20Sopenharmony_ci	skb_pull(skbc, NLMSG_HDRLEN);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* set portid 0 to inform userspace message comes from kernel */
7298c2ecf20Sopenharmony_ci	NETLINK_CB(skbc).portid = 0;
7308c2ecf20Sopenharmony_ci	NETLINK_CB(skbc).dst_group = 1;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	ret = netlink_broadcast(usk, skbc, 0, 1, GFP_KERNEL);
7338c2ecf20Sopenharmony_ci	/* ENOBUFS should be handled in userspace */
7348c2ecf20Sopenharmony_ci	if (ret == -ENOBUFS || ret == -ESRCH)
7358c2ecf20Sopenharmony_ci		ret = 0;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	return ret;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh,
7418c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	struct net *net;
7448c2ecf20Sopenharmony_ci	int ret;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (!nlmsg_data(nlh))
7478c2ecf20Sopenharmony_ci		return -EINVAL;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	/*
7508c2ecf20Sopenharmony_ci	 * Verify that we are allowed to send messages to the target
7518c2ecf20Sopenharmony_ci	 * network namespace. The caller must have CAP_SYS_ADMIN in the
7528c2ecf20Sopenharmony_ci	 * owning user namespace of the target network namespace.
7538c2ecf20Sopenharmony_ci	 */
7548c2ecf20Sopenharmony_ci	net = sock_net(NETLINK_CB(skb).sk);
7558c2ecf20Sopenharmony_ci	if (!netlink_ns_capable(skb, net->user_ns, CAP_SYS_ADMIN)) {
7568c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "missing CAP_SYS_ADMIN capability");
7578c2ecf20Sopenharmony_ci		return -EPERM;
7588c2ecf20Sopenharmony_ci	}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	mutex_lock(&uevent_sock_mutex);
7618c2ecf20Sopenharmony_ci	ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack);
7628c2ecf20Sopenharmony_ci	mutex_unlock(&uevent_sock_mutex);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	return ret;
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic void uevent_net_rcv(struct sk_buff *skb)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	netlink_rcv_skb(skb, &uevent_net_rcv_skb);
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic int uevent_net_init(struct net *net)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct uevent_sock *ue_sk;
7758c2ecf20Sopenharmony_ci	struct netlink_kernel_cfg cfg = {
7768c2ecf20Sopenharmony_ci		.groups	= 1,
7778c2ecf20Sopenharmony_ci		.input = uevent_net_rcv,
7788c2ecf20Sopenharmony_ci		.flags	= NL_CFG_F_NONROOT_RECV
7798c2ecf20Sopenharmony_ci	};
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
7828c2ecf20Sopenharmony_ci	if (!ue_sk)
7838c2ecf20Sopenharmony_ci		return -ENOMEM;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg);
7868c2ecf20Sopenharmony_ci	if (!ue_sk->sk) {
7878c2ecf20Sopenharmony_ci		pr_err("kobject_uevent: unable to create netlink socket!\n");
7888c2ecf20Sopenharmony_ci		kfree(ue_sk);
7898c2ecf20Sopenharmony_ci		return -ENODEV;
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	net->uevent_sock = ue_sk;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/* Restrict uevents to initial user namespace. */
7958c2ecf20Sopenharmony_ci	if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) {
7968c2ecf20Sopenharmony_ci		mutex_lock(&uevent_sock_mutex);
7978c2ecf20Sopenharmony_ci		list_add_tail(&ue_sk->list, &uevent_sock_list);
7988c2ecf20Sopenharmony_ci		mutex_unlock(&uevent_sock_mutex);
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return 0;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic void uevent_net_exit(struct net *net)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct uevent_sock *ue_sk = net->uevent_sock;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) {
8098c2ecf20Sopenharmony_ci		mutex_lock(&uevent_sock_mutex);
8108c2ecf20Sopenharmony_ci		list_del(&ue_sk->list);
8118c2ecf20Sopenharmony_ci		mutex_unlock(&uevent_sock_mutex);
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	netlink_kernel_release(ue_sk->sk);
8158c2ecf20Sopenharmony_ci	kfree(ue_sk);
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic struct pernet_operations uevent_net_ops = {
8198c2ecf20Sopenharmony_ci	.init	= uevent_net_init,
8208c2ecf20Sopenharmony_ci	.exit	= uevent_net_exit,
8218c2ecf20Sopenharmony_ci};
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic int __init kobject_uevent_init(void)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	return register_pernet_subsys(&uevent_net_ops);
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cipostcore_initcall(kobject_uevent_init);
8308c2ecf20Sopenharmony_ci#endif
831