brintos

brintos / linux-shallow public Read only

0
0
Text · 19.4 KiB · b7f2fa0 Raw
829 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * kernel userspace event delivery4 *5 * Copyright (C) 2004 Red Hat, Inc.  All rights reserved.6 * Copyright (C) 2004 Novell, Inc.  All rights reserved.7 * Copyright (C) 2004 IBM, Inc. All rights reserved.8 *9 * Authors:10 *	Robert Love		<rml@novell.com>11 *	Kay Sievers		<kay.sievers@vrfy.org>12 *	Arjan van de Ven	<arjanv@redhat.com>13 *	Greg Kroah-Hartman	<greg@kroah.com>14 */15 16#include <linux/spinlock.h>17#include <linux/string.h>18#include <linux/kobject.h>19#include <linux/export.h>20#include <linux/kmod.h>21#include <linux/slab.h>22#include <linux/socket.h>23#include <linux/skbuff.h>24#include <linux/netlink.h>25#include <linux/uidgid.h>26#include <linux/uuid.h>27#include <linux/ctype.h>28#include <net/sock.h>29#include <net/netlink.h>30#include <net/net_namespace.h>31 32 33atomic64_t uevent_seqnum;34#ifdef CONFIG_UEVENT_HELPER35char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;36#endif37 38struct uevent_sock {39	struct list_head list;40	struct sock *sk;41};42 43#ifdef CONFIG_NET44static LIST_HEAD(uevent_sock_list);45/* This lock protects uevent_sock_list */46static DEFINE_MUTEX(uevent_sock_mutex);47#endif48 49/* the strings here must match the enum in include/linux/kobject.h */50static const char *kobject_actions[] = {51	[KOBJ_ADD] =		"add",52	[KOBJ_REMOVE] =		"remove",53	[KOBJ_CHANGE] =		"change",54	[KOBJ_MOVE] =		"move",55	[KOBJ_ONLINE] =		"online",56	[KOBJ_OFFLINE] =	"offline",57	[KOBJ_BIND] =		"bind",58	[KOBJ_UNBIND] =		"unbind",59};60 61static int kobject_action_type(const char *buf, size_t count,62			       enum kobject_action *type,63			       const char **args)64{65	enum kobject_action action;66	size_t count_first;67	const char *args_start;68	int ret = -EINVAL;69 70	if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))71		count--;72 73	if (!count)74		goto out;75 76	args_start = strnchr(buf, count, ' ');77	if (args_start) {78		count_first = args_start - buf;79		args_start = args_start + 1;80	} else81		count_first = count;82 83	for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {84		if (strncmp(kobject_actions[action], buf, count_first) != 0)85			continue;86		if (kobject_actions[action][count_first] != '\0')87			continue;88		if (args)89			*args = args_start;90		*type = action;91		ret = 0;92		break;93	}94out:95	return ret;96}97 98static const char *action_arg_word_end(const char *buf, const char *buf_end,99				       char delim)100{101	const char *next = buf;102 103	while (next <= buf_end && *next != delim)104		if (!isalnum(*next++))105			return NULL;106 107	if (next == buf)108		return NULL;109 110	return next;111}112 113static int kobject_action_args(const char *buf, size_t count,114			       struct kobj_uevent_env **ret_env)115{116	struct kobj_uevent_env *env = NULL;117	const char *next, *buf_end, *key;118	int key_len;119	int r = -EINVAL;120 121	if (count && (buf[count - 1] == '\n' || buf[count - 1] == '\0'))122		count--;123 124	if (!count)125		return -EINVAL;126 127	env = kzalloc(sizeof(*env), GFP_KERNEL);128	if (!env)129		return -ENOMEM;130 131	/* first arg is UUID */132	if (count < UUID_STRING_LEN || !uuid_is_valid(buf) ||133	    add_uevent_var(env, "SYNTH_UUID=%.*s", UUID_STRING_LEN, buf))134		goto out;135 136	/*137	 * the rest are custom environment variables in KEY=VALUE138	 * format with ' ' delimiter between each KEY=VALUE pair139	 */140	next = buf + UUID_STRING_LEN;141	buf_end = buf + count - 1;142 143	while (next <= buf_end) {144		if (*next != ' ')145			goto out;146 147		/* skip the ' ', key must follow */148		key = ++next;149		if (key > buf_end)150			goto out;151 152		buf = next;153		next = action_arg_word_end(buf, buf_end, '=');154		if (!next || next > buf_end || *next != '=')155			goto out;156		key_len = next - buf;157 158		/* skip the '=', value must follow */159		if (++next > buf_end)160			goto out;161 162		buf = next;163		next = action_arg_word_end(buf, buf_end, ' ');164		if (!next)165			goto out;166 167		if (add_uevent_var(env, "SYNTH_ARG_%.*s=%.*s",168				   key_len, key, (int) (next - buf), buf))169			goto out;170	}171 172	r = 0;173out:174	if (r)175		kfree(env);176	else177		*ret_env = env;178	return r;179}180 181/**182 * kobject_synth_uevent - send synthetic uevent with arguments183 *184 * @kobj: struct kobject for which synthetic uevent is to be generated185 * @buf: buffer containing action type and action args, newline is ignored186 * @count: length of buffer187 *188 * Returns 0 if kobject_synthetic_uevent() is completed with success or the189 * corresponding error when it fails.190 */191int kobject_synth_uevent(struct kobject *kobj, const char *buf, size_t count)192{193	char *no_uuid_envp[] = { "SYNTH_UUID=0", NULL };194	enum kobject_action action;195	const char *action_args;196	struct kobj_uevent_env *env;197	const char *msg = NULL, *devpath;198	int r;199 200	r = kobject_action_type(buf, count, &action, &action_args);201	if (r) {202		msg = "unknown uevent action string";203		goto out;204	}205 206	if (!action_args) {207		r = kobject_uevent_env(kobj, action, no_uuid_envp);208		goto out;209	}210 211	r = kobject_action_args(action_args,212				count - (action_args - buf), &env);213	if (r == -EINVAL) {214		msg = "incorrect uevent action arguments";215		goto out;216	}217 218	if (r)219		goto out;220 221	r = kobject_uevent_env(kobj, action, env->envp);222	kfree(env);223out:224	if (r) {225		devpath = kobject_get_path(kobj, GFP_KERNEL);226		pr_warn("synth uevent: %s: %s\n",227		       devpath ?: "unknown device",228		       msg ?: "failed to send uevent");229		kfree(devpath);230	}231	return r;232}233 234#ifdef CONFIG_UEVENT_HELPER235static int kobj_usermode_filter(struct kobject *kobj)236{237	const struct kobj_ns_type_operations *ops;238 239	ops = kobj_ns_ops(kobj);240	if (ops) {241		const void *init_ns, *ns;242 243		ns = kobj->ktype->namespace(kobj);244		init_ns = ops->initial_ns();245		return ns != init_ns;246	}247 248	return 0;249}250 251static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)252{253	int buffer_size = sizeof(env->buf) - env->buflen;254	int len;255 256	len = strscpy(&env->buf[env->buflen], subsystem, buffer_size);257	if (len < 0) {258		pr_warn("%s: insufficient buffer space (%u left) for %s\n",259			__func__, buffer_size, subsystem);260		return -ENOMEM;261	}262 263	env->argv[0] = uevent_helper;264	env->argv[1] = &env->buf[env->buflen];265	env->argv[2] = NULL;266 267	env->buflen += len + 1;268	return 0;269}270 271static void cleanup_uevent_env(struct subprocess_info *info)272{273	kfree(info->data);274}275#endif276 277#ifdef CONFIG_NET278static struct sk_buff *alloc_uevent_skb(struct kobj_uevent_env *env,279					const char *action_string,280					const char *devpath)281{282	struct netlink_skb_parms *parms;283	struct sk_buff *skb = NULL;284	char *scratch;285	size_t len;286 287	/* allocate message with maximum possible size */288	len = strlen(action_string) + strlen(devpath) + 2;289	skb = alloc_skb(len + env->buflen, GFP_KERNEL);290	if (!skb)291		return NULL;292 293	/* add header */294	scratch = skb_put(skb, len);295	sprintf(scratch, "%s@%s", action_string, devpath);296 297	skb_put_data(skb, env->buf, env->buflen);298 299	parms = &NETLINK_CB(skb);300	parms->creds.uid = GLOBAL_ROOT_UID;301	parms->creds.gid = GLOBAL_ROOT_GID;302	parms->dst_group = 1;303	parms->portid = 0;304 305	return skb;306}307 308static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,309					 const char *action_string,310					 const char *devpath)311{312	struct sk_buff *skb = NULL;313	struct uevent_sock *ue_sk;314	int retval = 0;315 316	/* send netlink message */317	mutex_lock(&uevent_sock_mutex);318	list_for_each_entry(ue_sk, &uevent_sock_list, list) {319		struct sock *uevent_sock = ue_sk->sk;320 321		if (!netlink_has_listeners(uevent_sock, 1))322			continue;323 324		if (!skb) {325			retval = -ENOMEM;326			skb = alloc_uevent_skb(env, action_string, devpath);327			if (!skb)328				continue;329		}330 331		retval = netlink_broadcast(uevent_sock, skb_get(skb), 0, 1,332					   GFP_KERNEL);333		/* ENOBUFS should be handled in userspace */334		if (retval == -ENOBUFS || retval == -ESRCH)335			retval = 0;336	}337	mutex_unlock(&uevent_sock_mutex);338	consume_skb(skb);339 340	return retval;341}342 343static int uevent_net_broadcast_tagged(struct sock *usk,344				       struct kobj_uevent_env *env,345				       const char *action_string,346				       const char *devpath)347{348	struct user_namespace *owning_user_ns = sock_net(usk)->user_ns;349	struct sk_buff *skb = NULL;350	int ret = 0;351 352	skb = alloc_uevent_skb(env, action_string, devpath);353	if (!skb)354		return -ENOMEM;355 356	/* fix credentials */357	if (owning_user_ns != &init_user_ns) {358		struct netlink_skb_parms *parms = &NETLINK_CB(skb);359		kuid_t root_uid;360		kgid_t root_gid;361 362		/* fix uid */363		root_uid = make_kuid(owning_user_ns, 0);364		if (uid_valid(root_uid))365			parms->creds.uid = root_uid;366 367		/* fix gid */368		root_gid = make_kgid(owning_user_ns, 0);369		if (gid_valid(root_gid))370			parms->creds.gid = root_gid;371	}372 373	ret = netlink_broadcast(usk, skb, 0, 1, GFP_KERNEL);374	/* ENOBUFS should be handled in userspace */375	if (ret == -ENOBUFS || ret == -ESRCH)376		ret = 0;377 378	return ret;379}380#endif381 382static int kobject_uevent_net_broadcast(struct kobject *kobj,383					struct kobj_uevent_env *env,384					const char *action_string,385					const char *devpath)386{387	int ret = 0;388 389#ifdef CONFIG_NET390	const struct kobj_ns_type_operations *ops;391	const struct net *net = NULL;392 393	ops = kobj_ns_ops(kobj);394	if (!ops && kobj->kset) {395		struct kobject *ksobj = &kobj->kset->kobj;396 397		if (ksobj->parent != NULL)398			ops = kobj_ns_ops(ksobj->parent);399	}400 401	/* kobjects currently only carry network namespace tags and they402	 * are the only tag relevant here since we want to decide which403	 * network namespaces to broadcast the uevent into.404	 */405	if (ops && ops->netlink_ns && kobj->ktype->namespace)406		if (ops->type == KOBJ_NS_TYPE_NET)407			net = kobj->ktype->namespace(kobj);408 409	if (!net)410		ret = uevent_net_broadcast_untagged(env, action_string,411						    devpath);412	else413		ret = uevent_net_broadcast_tagged(net->uevent_sock->sk, env,414						  action_string, devpath);415#endif416 417	return ret;418}419 420static void zap_modalias_env(struct kobj_uevent_env *env)421{422	static const char modalias_prefix[] = "MODALIAS=";423	size_t len;424	int i, j;425 426	for (i = 0; i < env->envp_idx;) {427		if (strncmp(env->envp[i], modalias_prefix,428			    sizeof(modalias_prefix) - 1)) {429			i++;430			continue;431		}432 433		len = strlen(env->envp[i]) + 1;434 435		if (i != env->envp_idx - 1) {436			/* @env->envp[] contains pointers to @env->buf[]437			 * with @env->buflen chars, and we are removing438			 * variable MODALIAS here pointed by @env->envp[i]439			 * with length @len as shown below:440			 *441			 * 0               @env->buf[]      @env->buflen442			 * ---------------------------------------------443			 * ^             ^              ^              ^444			 * |             |->   @len   <-| target block |445			 * @env->envp[0] @env->envp[i]  @env->envp[i + 1]446			 *447			 * so the "target block" indicated above is moved448			 * backward by @len, and its right size is449			 * @env->buflen - (@env->envp[i + 1] - @env->envp[0]).450			 */451			memmove(env->envp[i], env->envp[i + 1],452				env->buflen - (env->envp[i + 1] - env->envp[0]));453 454			for (j = i; j < env->envp_idx - 1; j++)455				env->envp[j] = env->envp[j + 1] - len;456		}457 458		env->envp_idx--;459		env->buflen -= len;460	}461}462 463/**464 * kobject_uevent_env - send an uevent with environmental data465 *466 * @kobj: struct kobject that the action is happening to467 * @action: action that is happening468 * @envp_ext: pointer to environmental data469 *470 * Returns 0 if kobject_uevent_env() is completed with success or the471 * corresponding error when it fails.472 */473int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,474		       char *envp_ext[])475{476	struct kobj_uevent_env *env;477	const char *action_string = kobject_actions[action];478	const char *devpath = NULL;479	const char *subsystem;480	struct kobject *top_kobj;481	struct kset *kset;482	const struct kset_uevent_ops *uevent_ops;483	int i = 0;484	int retval = 0;485 486	/*487	 * Mark "remove" event done regardless of result, for some subsystems488	 * do not want to re-trigger "remove" event via automatic cleanup.489	 */490	if (action == KOBJ_REMOVE)491		kobj->state_remove_uevent_sent = 1;492 493	pr_debug("kobject: '%s' (%p): %s\n",494		 kobject_name(kobj), kobj, __func__);495 496	/* search the kset we belong to */497	top_kobj = kobj;498	while (!top_kobj->kset && top_kobj->parent)499		top_kobj = top_kobj->parent;500 501	if (!top_kobj->kset) {502		pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "503			 "without kset!\n", kobject_name(kobj), kobj,504			 __func__);505		return -EINVAL;506	}507 508	kset = top_kobj->kset;509	uevent_ops = kset->uevent_ops;510 511	/* skip the event, if uevent_suppress is set*/512	if (kobj->uevent_suppress) {513		pr_debug("kobject: '%s' (%p): %s: uevent_suppress "514				 "caused the event to drop!\n",515				 kobject_name(kobj), kobj, __func__);516		return 0;517	}518	/* skip the event, if the filter returns zero. */519	if (uevent_ops && uevent_ops->filter)520		if (!uevent_ops->filter(kobj)) {521			pr_debug("kobject: '%s' (%p): %s: filter function "522				 "caused the event to drop!\n",523				 kobject_name(kobj), kobj, __func__);524			return 0;525		}526 527	/* originating subsystem */528	if (uevent_ops && uevent_ops->name)529		subsystem = uevent_ops->name(kobj);530	else531		subsystem = kobject_name(&kset->kobj);532	if (!subsystem) {533		pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "534			 "event to drop!\n", kobject_name(kobj), kobj,535			 __func__);536		return 0;537	}538 539	/* environment buffer */540	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);541	if (!env)542		return -ENOMEM;543 544	/* complete object path */545	devpath = kobject_get_path(kobj, GFP_KERNEL);546	if (!devpath) {547		retval = -ENOENT;548		goto exit;549	}550 551	/* default keys */552	retval = add_uevent_var(env, "ACTION=%s", action_string);553	if (retval)554		goto exit;555	retval = add_uevent_var(env, "DEVPATH=%s", devpath);556	if (retval)557		goto exit;558	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);559	if (retval)560		goto exit;561 562	/* keys passed in from the caller */563	if (envp_ext) {564		for (i = 0; envp_ext[i]; i++) {565			retval = add_uevent_var(env, "%s", envp_ext[i]);566			if (retval)567				goto exit;568		}569	}570 571	/* let the kset specific function add its stuff */572	if (uevent_ops && uevent_ops->uevent) {573		retval = uevent_ops->uevent(kobj, env);574		if (retval) {575			pr_debug("kobject: '%s' (%p): %s: uevent() returned "576				 "%d\n", kobject_name(kobj), kobj,577				 __func__, retval);578			goto exit;579		}580	}581 582	switch (action) {583	case KOBJ_ADD:584		/*585		 * Mark "add" event so we can make sure we deliver "remove"586		 * event to userspace during automatic cleanup. If587		 * the object did send an "add" event, "remove" will588		 * automatically generated by the core, if not already done589		 * by the caller.590		 */591		kobj->state_add_uevent_sent = 1;592		break;593 594	case KOBJ_UNBIND:595		zap_modalias_env(env);596		break;597 598	default:599		break;600	}601 602	/* we will send an event, so request a new sequence number */603	retval = add_uevent_var(env, "SEQNUM=%llu",604				atomic64_inc_return(&uevent_seqnum));605	if (retval)606		goto exit;607 608	retval = kobject_uevent_net_broadcast(kobj, env, action_string,609					      devpath);610 611#ifdef CONFIG_UEVENT_HELPER612	/* call uevent_helper, usually only enabled during early boot */613	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {614		struct subprocess_info *info;615 616		retval = add_uevent_var(env, "HOME=/");617		if (retval)618			goto exit;619		retval = add_uevent_var(env,620					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");621		if (retval)622			goto exit;623		retval = init_uevent_argv(env, subsystem);624		if (retval)625			goto exit;626 627		retval = -ENOMEM;628		info = call_usermodehelper_setup(env->argv[0], env->argv,629						 env->envp, GFP_KERNEL,630						 NULL, cleanup_uevent_env, env);631		if (info) {632			retval = call_usermodehelper_exec(info, UMH_NO_WAIT);633			env = NULL;	/* freed by cleanup_uevent_env */634		}635	}636#endif637 638exit:639	kfree(devpath);640	kfree(env);641	return retval;642}643EXPORT_SYMBOL_GPL(kobject_uevent_env);644 645/**646 * kobject_uevent - notify userspace by sending an uevent647 *648 * @kobj: struct kobject that the action is happening to649 * @action: action that is happening650 *651 * Returns 0 if kobject_uevent() is completed with success or the652 * corresponding error when it fails.653 */654int kobject_uevent(struct kobject *kobj, enum kobject_action action)655{656	return kobject_uevent_env(kobj, action, NULL);657}658EXPORT_SYMBOL_GPL(kobject_uevent);659 660/**661 * add_uevent_var - add key value string to the environment buffer662 * @env: environment buffer structure663 * @format: printf format for the key=value pair664 *665 * Returns 0 if environment variable was added successfully or -ENOMEM666 * if no space was available.667 */668int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)669{670	va_list args;671	int len;672 673	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {674		WARN(1, KERN_ERR "add_uevent_var: too many keys\n");675		return -ENOMEM;676	}677 678	va_start(args, format);679	len = vsnprintf(&env->buf[env->buflen],680			sizeof(env->buf) - env->buflen,681			format, args);682	va_end(args);683 684	if (len >= (sizeof(env->buf) - env->buflen)) {685		WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");686		return -ENOMEM;687	}688 689	env->envp[env->envp_idx++] = &env->buf[env->buflen];690	env->buflen += len + 1;691	return 0;692}693EXPORT_SYMBOL_GPL(add_uevent_var);694 695#if defined(CONFIG_NET)696static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,697				struct netlink_ext_ack *extack)698{699	/* u64 to chars: 2^64 - 1 = 21 chars */700	char buf[sizeof("SEQNUM=") + 21];701	struct sk_buff *skbc;702	int ret;703 704	/* bump and prepare sequence number */705	ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu",706		       atomic64_inc_return(&uevent_seqnum));707	if (ret < 0 || (size_t)ret >= sizeof(buf))708		return -ENOMEM;709	ret++;710 711	/* verify message does not overflow */712	if ((skb->len + ret) > UEVENT_BUFFER_SIZE) {713		NL_SET_ERR_MSG(extack, "uevent message too big");714		return -EINVAL;715	}716 717	/* copy skb and extend to accommodate sequence number */718	skbc = skb_copy_expand(skb, 0, ret, GFP_KERNEL);719	if (!skbc)720		return -ENOMEM;721 722	/* append sequence number */723	skb_put_data(skbc, buf, ret);724 725	/* remove msg header */726	skb_pull(skbc, NLMSG_HDRLEN);727 728	/* set portid 0 to inform userspace message comes from kernel */729	NETLINK_CB(skbc).portid = 0;730	NETLINK_CB(skbc).dst_group = 1;731 732	ret = netlink_broadcast(usk, skbc, 0, 1, GFP_KERNEL);733	/* ENOBUFS should be handled in userspace */734	if (ret == -ENOBUFS || ret == -ESRCH)735		ret = 0;736 737	return ret;738}739 740static int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh,741			      struct netlink_ext_ack *extack)742{743	struct net *net;744	int ret;745 746	if (!nlmsg_data(nlh))747		return -EINVAL;748 749	/*750	 * Verify that we are allowed to send messages to the target751	 * network namespace. The caller must have CAP_SYS_ADMIN in the752	 * owning user namespace of the target network namespace.753	 */754	net = sock_net(NETLINK_CB(skb).sk);755	if (!netlink_ns_capable(skb, net->user_ns, CAP_SYS_ADMIN)) {756		NL_SET_ERR_MSG(extack, "missing CAP_SYS_ADMIN capability");757		return -EPERM;758	}759 760	ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack);761 762	return ret;763}764 765static void uevent_net_rcv(struct sk_buff *skb)766{767	netlink_rcv_skb(skb, &uevent_net_rcv_skb);768}769 770static int uevent_net_init(struct net *net)771{772	struct uevent_sock *ue_sk;773	struct netlink_kernel_cfg cfg = {774		.groups	= 1,775		.input = uevent_net_rcv,776		.flags	= NL_CFG_F_NONROOT_RECV777	};778 779	ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);780	if (!ue_sk)781		return -ENOMEM;782 783	ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg);784	if (!ue_sk->sk) {785		pr_err("kobject_uevent: unable to create netlink socket!\n");786		kfree(ue_sk);787		return -ENODEV;788	}789 790	net->uevent_sock = ue_sk;791 792	/* Restrict uevents to initial user namespace. */793	if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) {794		mutex_lock(&uevent_sock_mutex);795		list_add_tail(&ue_sk->list, &uevent_sock_list);796		mutex_unlock(&uevent_sock_mutex);797	}798 799	return 0;800}801 802static void uevent_net_exit(struct net *net)803{804	struct uevent_sock *ue_sk = net->uevent_sock;805 806	if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) {807		mutex_lock(&uevent_sock_mutex);808		list_del(&ue_sk->list);809		mutex_unlock(&uevent_sock_mutex);810	}811 812	netlink_kernel_release(ue_sk->sk);813	kfree(ue_sk);814}815 816static struct pernet_operations uevent_net_ops = {817	.init	= uevent_net_init,818	.exit	= uevent_net_exit,819};820 821static int __init kobject_uevent_init(void)822{823	return register_pernet_subsys(&uevent_net_ops);824}825 826 827postcore_initcall(kobject_uevent_init);828#endif829