1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/spinlock.h>
18#include <linux/socket.h>
19#include <linux/skbuff.h>
20#include <linux/netlink.h>
21#include <linux/string.h>
22#include <linux/kobject.h>
23#include <net/sock.h>
24
25
26u64 uevent_seqnum;
27char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
28static DEFINE_SPINLOCK(sequence_lock);
29#if defined(CONFIG_NET)
30static struct sock *uevent_sock;
31#endif
32
33
34static const char *kobject_actions[] = {
35 [KOBJ_ADD] = "add",
36 [KOBJ_REMOVE] = "remove",
37 [KOBJ_CHANGE] = "change",
38 [KOBJ_MOVE] = "move",
39 [KOBJ_ONLINE] = "online",
40 [KOBJ_OFFLINE] = "offline",
41};
42
43
44
45
46
47
48
49
50
51
52int kobject_action_type(const char *buf, size_t count,
53 enum kobject_action *type)
54{
55 enum kobject_action action;
56 int ret = -EINVAL;
57
58 if (count && buf[count-1] == '\n')
59 count--;
60
61 if (!count)
62 goto out;
63
64 for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
65 if (strncmp(kobject_actions[action], buf, count) != 0)
66 continue;
67 if (kobject_actions[action][count] != '\0')
68 continue;
69 *type = action;
70 ret = 0;
71 break;
72 }
73out:
74 return ret;
75}
76
77
78
79
80
81
82
83
84
85
86
87int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
88 char *envp_ext[])
89{
90 struct kobj_uevent_env *env;
91 const char *action_string = kobject_actions[action];
92 const char *devpath = NULL;
93 const char *subsystem;
94 struct kobject *top_kobj;
95 struct kset *kset;
96 struct kset_uevent_ops *uevent_ops;
97 u64 seq;
98 int i = 0;
99 int retval = 0;
100
101 pr_debug("%s\n", __FUNCTION__);
102
103
104 top_kobj = kobj;
105 while (!top_kobj->kset && top_kobj->parent)
106 top_kobj = top_kobj->parent;
107
108 if (!top_kobj->kset) {
109 pr_debug("kobject attempted to send uevent without kset!\n");
110 return -EINVAL;
111 }
112
113 kset = top_kobj->kset;
114 uevent_ops = kset->uevent_ops;
115
116
117 if (uevent_ops && uevent_ops->filter)
118 if (!uevent_ops->filter(kset, kobj)) {
119 pr_debug("kobject filter function caused the event to drop!\n");
120 return 0;
121 }
122
123
124 if (uevent_ops && uevent_ops->name)
125 subsystem = uevent_ops->name(kset, kobj);
126 else
127 subsystem = kobject_name(&kset->kobj);
128 if (!subsystem) {
129 pr_debug("unset subsystem caused the event to drop!\n");
130 return 0;
131 }
132
133
134 env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
135 if (!env)
136 return -ENOMEM;
137
138
139 devpath = kobject_get_path(kobj, GFP_KERNEL);
140 if (!devpath) {
141 retval = -ENOENT;
142 goto exit;
143 }
144
145
146 retval = add_uevent_var(env, "ACTION=%s", action_string);
147 if (retval)
148 goto exit;
149 retval = add_uevent_var(env, "DEVPATH=%s", devpath);
150 if (retval)
151 goto exit;
152 retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
153 if (retval)
154 goto exit;
155
156
157 if (envp_ext) {
158 for (i = 0; envp_ext[i]; i++) {
159 retval = add_uevent_var(env, envp_ext[i]);
160 if (retval)
161 goto exit;
162 }
163 }
164
165
166 if (uevent_ops && uevent_ops->uevent) {
167 retval = uevent_ops->uevent(kset, kobj, env);
168 if (retval) {
169 pr_debug ("%s - uevent() returned %d\n",
170 __FUNCTION__, retval);
171 goto exit;
172 }
173 }
174
175
176 spin_lock(&sequence_lock);
177 seq = ++uevent_seqnum;
178 spin_unlock(&sequence_lock);
179 retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
180 if (retval)
181 goto exit;
182
183#if defined(CONFIG_NET)
184
185 if (uevent_sock) {
186 struct sk_buff *skb;
187 size_t len;
188
189
190 len = strlen(action_string) + strlen(devpath) + 2;
191 skb = alloc_skb(len + env->buflen, GFP_KERNEL);
192 if (skb) {
193 char *scratch;
194
195
196 scratch = skb_put(skb, len);
197 sprintf(scratch, "%s@%s", action_string, devpath);
198
199
200 for (i = 0; i < env->envp_idx; i++) {
201 len = strlen(env->envp[i]) + 1;
202 scratch = skb_put(skb, len);
203 strcpy(scratch, env->envp[i]);
204 }
205
206 NETLINK_CB(skb).dst_group = 1;
207 netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
208 }
209 }
210#endif
211
212
213 if (uevent_helper[0]) {
214 char *argv [3];
215
216 argv [0] = uevent_helper;
217 argv [1] = (char *)subsystem;
218 argv [2] = NULL;
219 retval = add_uevent_var(env, "HOME=/");
220 if (retval)
221 goto exit;
222 retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
223 if (retval)
224 goto exit;
225
226 call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
227 }
228
229exit:
230 kfree(devpath);
231 kfree(env);
232 return retval;
233}
234
235EXPORT_SYMBOL_GPL(kobject_uevent_env);
236
237
238
239
240
241
242
243
244
245
246int kobject_uevent(struct kobject *kobj, enum kobject_action action)
247{
248 return kobject_uevent_env(kobj, action, NULL);
249}
250
251EXPORT_SYMBOL_GPL(kobject_uevent);
252
253
254
255
256
257
258
259
260
261int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
262{
263 va_list args;
264 int len;
265
266 if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
267 printk(KERN_ERR "add_uevent_var: too many keys\n");
268 WARN_ON(1);
269 return -ENOMEM;
270 }
271
272 va_start(args, format);
273 len = vsnprintf(&env->buf[env->buflen],
274 sizeof(env->buf) - env->buflen,
275 format, args);
276 va_end(args);
277
278 if (len >= (sizeof(env->buf) - env->buflen)) {
279 printk(KERN_ERR "add_uevent_var: buffer size too small\n");
280 WARN_ON(1);
281 return -ENOMEM;
282 }
283
284 env->envp[env->envp_idx++] = &env->buf[env->buflen];
285 env->buflen += len + 1;
286 return 0;
287}
288EXPORT_SYMBOL_GPL(add_uevent_var);
289
290#if defined(CONFIG_NET)
291static int __init kobject_uevent_init(void)
292{
293 uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
294 1, NULL, NULL, THIS_MODULE);
295 if (!uevent_sock) {
296 printk(KERN_ERR
297 "kobject_uevent: unable to create netlink socket!\n");
298 return -ENODEV;
299 }
300
301 return 0;
302}
303
304postcore_initcall(kobject_uevent_init);
305#endif
306