]>
Commit | Line | Data |
---|---|---|
d46f37fd | 1 | /* |
0ec5b5e1 | 2 | * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> |
d46f37fd | 3 | * |
55e9959b KS |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation, either version 2 of the License, or | |
7 | * (at your option) any later version. | |
d46f37fd | 8 | * |
55e9959b KS |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
d46f37fd KS |
16 | */ |
17 | ||
d46f37fd KS |
18 | #include <stdlib.h> |
19 | #include <stdio.h> | |
20 | #include <stddef.h> | |
21 | #include <unistd.h> | |
22 | #include <fcntl.h> | |
23 | #include <errno.h> | |
24 | #include <ctype.h> | |
25 | #include <string.h> | |
959e8b5d DM |
26 | #include <time.h> |
27 | #include <net/if.h> | |
d46f37fd KS |
28 | #include <sys/ioctl.h> |
29 | #include <sys/socket.h> | |
d46f37fd KS |
30 | #include <linux/sockios.h> |
31 | ||
32 | #include "udev.h" | |
d46f37fd | 33 | |
aa8734ff KS |
34 | struct udev_event *udev_event_new(struct udev_device *dev) |
35 | { | |
36 | struct udev_event *event; | |
37 | ||
b29a5e4a | 38 | event = calloc(1, sizeof(struct udev_event)); |
aa8734ff KS |
39 | if (event == NULL) |
40 | return NULL; | |
f2291cd0 | 41 | event->mode = 0600; |
aa8734ff KS |
42 | event->dev = dev; |
43 | event->udev = udev_device_get_udev(dev); | |
44 | udev_list_init(&event->run_list); | |
aa8734ff KS |
45 | dbg(event->udev, "allocated event %p\n", event); |
46 | return event; | |
47 | } | |
48 | ||
49 | void udev_event_unref(struct udev_event *event) | |
50 | { | |
40fd3bc8 KS |
51 | if (event == NULL) |
52 | return; | |
eb8837e1 | 53 | udev_list_cleanup_entries(event->udev, &event->run_list); |
40fd3bc8 KS |
54 | free(event->tmp_node); |
55 | free(event->program_result); | |
b99028c9 | 56 | free(event->name); |
aa8734ff KS |
57 | dbg(event->udev, "free event %p\n", event); |
58 | free(event); | |
59 | } | |
60 | ||
065db052 | 61 | size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) |
f1128767 KS |
62 | { |
63 | struct udev_device *dev = event->dev; | |
f1128767 KS |
64 | enum subst_type { |
65 | SUBST_UNKNOWN, | |
065db052 KS |
66 | SUBST_TEMP_NODE, |
67 | SUBST_ATTR, | |
68 | SUBST_ENV, | |
f1128767 KS |
69 | SUBST_KERNEL, |
70 | SUBST_KERNEL_NUMBER, | |
f1128767 | 71 | SUBST_DRIVER, |
065db052 KS |
72 | SUBST_DEVPATH, |
73 | SUBST_ID, | |
f1128767 KS |
74 | SUBST_MAJOR, |
75 | SUBST_MINOR, | |
76 | SUBST_RESULT, | |
f1128767 | 77 | SUBST_PARENT, |
f1128767 KS |
78 | SUBST_NAME, |
79 | SUBST_LINKS, | |
80 | SUBST_ROOT, | |
81 | SUBST_SYS, | |
f1128767 KS |
82 | }; |
83 | static const struct subst_map { | |
84 | char *name; | |
85 | char fmt; | |
86 | enum subst_type type; | |
87 | } map[] = { | |
065db052 KS |
88 | { .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE }, |
89 | { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, | |
90 | { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, | |
91 | { .name = "env", .fmt = 'E', .type = SUBST_ENV }, | |
f1128767 | 92 | { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, |
065db052 | 93 | { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, |
f1128767 | 94 | { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, |
065db052 KS |
95 | { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, |
96 | { .name = "id", .fmt = 'b', .type = SUBST_ID }, | |
f1128767 KS |
97 | { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, |
98 | { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, | |
99 | { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, | |
f1128767 | 100 | { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, |
f1128767 KS |
101 | { .name = "name", .fmt = 'D', .type = SUBST_NAME }, |
102 | { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, | |
103 | { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, | |
104 | { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, | |
f1128767 | 105 | }; |
065db052 KS |
106 | const char *from; |
107 | char *s; | |
108 | size_t l; | |
109 | ||
110 | from = src; | |
111 | s = dest; | |
112 | l = size; | |
f1128767 | 113 | |
88cbfb09 | 114 | for (;;) { |
065db052 KS |
115 | enum subst_type type = SUBST_UNKNOWN; |
116 | char attrbuf[UTIL_PATH_SIZE]; | |
117 | char *attr = NULL; | |
118 | ||
119 | while (from[0] != '\0') { | |
120 | if (from[0] == '$') { | |
f1128767 | 121 | /* substitute named variable */ |
065db052 KS |
122 | unsigned int i; |
123 | ||
124 | if (from[1] == '$') { | |
125 | from++; | |
126 | goto copy; | |
f1128767 | 127 | } |
065db052 KS |
128 | |
129 | for (i = 0; i < ARRAY_SIZE(map); i++) { | |
d5b5a611 | 130 | if (strncmp(&from[1], map[i].name, strlen(map[i].name)) == 0) { |
065db052 KS |
131 | type = map[i].type; |
132 | from += strlen(map[i].name)+1; | |
133 | dbg(event->udev, "will substitute format name '%s'\n", map[i].name); | |
134 | goto subst; | |
f1128767 KS |
135 | } |
136 | } | |
065db052 | 137 | } else if (from[0] == '%') { |
f1128767 | 138 | /* substitute format char */ |
065db052 KS |
139 | unsigned int i; |
140 | ||
141 | if (from[1] == '%') { | |
142 | from++; | |
143 | goto copy; | |
f1128767 | 144 | } |
065db052 KS |
145 | |
146 | for (i = 0; i < ARRAY_SIZE(map); i++) { | |
147 | if (from[1] == map[i].fmt) { | |
148 | type = map[i].type; | |
149 | from += 2; | |
150 | dbg(event->udev, "will substitute format char '%c'\n", map[i].fmt); | |
151 | goto subst; | |
f1128767 KS |
152 | } |
153 | } | |
f1128767 | 154 | } |
065db052 KS |
155 | copy: |
156 | /* copy char */ | |
157 | if (l == 0) | |
158 | goto out; | |
159 | s[0] = from[0]; | |
160 | from++; | |
161 | s++; | |
162 | l--; | |
163 | } | |
164 | ||
165 | goto out; | |
166 | subst: | |
167 | /* extract possible $format{attr} */ | |
168 | if (from[0] == '{') { | |
169 | unsigned int i; | |
170 | ||
171 | from++; | |
172 | for (i = 0; from[i] != '}'; i++) { | |
173 | if (from[i] == '\0') { | |
174 | err(event->udev, "missing closing brace for format '%s'\n", src); | |
175 | goto out; | |
176 | } | |
177 | } | |
178 | if (i >= sizeof(attrbuf)) | |
179 | goto out; | |
180 | memcpy(attrbuf, from, i); | |
181 | attrbuf[i] = '\0'; | |
182 | from += i+1; | |
183 | attr = attrbuf; | |
4950b6e6 KS |
184 | } else { |
185 | attr = NULL; | |
f1128767 | 186 | } |
f1128767 KS |
187 | |
188 | switch (type) { | |
189 | case SUBST_DEVPATH: | |
065db052 | 190 | l = util_strpcpy(&s, l, udev_device_get_devpath(dev)); |
f1128767 KS |
191 | dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); |
192 | break; | |
193 | case SUBST_KERNEL: | |
065db052 | 194 | l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); |
f1128767 KS |
195 | dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); |
196 | break; | |
197 | case SUBST_KERNEL_NUMBER: | |
198 | if (udev_device_get_sysnum(dev) == NULL) | |
199 | break; | |
065db052 | 200 | l = util_strpcpy(&s, l, udev_device_get_sysnum(dev)); |
f1128767 KS |
201 | dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); |
202 | break; | |
203 | case SUBST_ID: | |
065db052 KS |
204 | if (event->dev_parent == NULL) |
205 | break; | |
206 | l = util_strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); | |
207 | dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); | |
f1128767 | 208 | break; |
065db052 KS |
209 | case SUBST_DRIVER: { |
210 | const char *driver; | |
f1128767 | 211 | |
065db052 KS |
212 | if (event->dev_parent == NULL) |
213 | break; | |
214 | ||
215 | driver = udev_device_get_driver(event->dev_parent); | |
216 | if (driver == NULL) | |
217 | break; | |
218 | l = util_strpcpy(&s, l, driver); | |
219 | dbg(event->udev, "substitute driver '%s'\n", driver); | |
f1128767 | 220 | break; |
065db052 KS |
221 | } |
222 | case SUBST_MAJOR: { | |
223 | char num[UTIL_PATH_SIZE]; | |
224 | ||
225 | sprintf(num, "%d", major(udev_device_get_devnum(dev))); | |
226 | l = util_strpcpy(&s, l, num); | |
227 | dbg(event->udev, "substitute major number '%s'\n", num); | |
f1128767 | 228 | break; |
065db052 KS |
229 | } |
230 | case SUBST_MINOR: { | |
231 | char num[UTIL_PATH_SIZE]; | |
232 | ||
233 | sprintf(num, "%d", minor(udev_device_get_devnum(dev))); | |
234 | l = util_strpcpy(&s, l, num); | |
235 | dbg(event->udev, "substitute minor number '%s'\n", num); | |
f1128767 | 236 | break; |
065db052 KS |
237 | } |
238 | case SUBST_RESULT: { | |
239 | char *rest; | |
240 | int i; | |
241 | ||
40fd3bc8 | 242 | if (event->program_result == NULL) |
f1128767 KS |
243 | break; |
244 | /* get part part of the result string */ | |
245 | i = 0; | |
246 | if (attr != NULL) | |
247 | i = strtoul(attr, &rest, 10); | |
248 | if (i > 0) { | |
40fd3bc8 | 249 | char result[UTIL_PATH_SIZE]; |
065db052 KS |
250 | char tmp[UTIL_PATH_SIZE]; |
251 | char *cpos; | |
40fd3bc8 | 252 | |
f1128767 | 253 | dbg(event->udev, "request part #%d of result string\n", i); |
065db052 | 254 | util_strscpy(result, sizeof(result), event->program_result); |
40fd3bc8 | 255 | cpos = result; |
f1128767 KS |
256 | while (--i) { |
257 | while (cpos[0] != '\0' && !isspace(cpos[0])) | |
258 | cpos++; | |
259 | while (isspace(cpos[0])) | |
260 | cpos++; | |
261 | } | |
262 | if (i > 0) { | |
263 | err(event->udev, "requested part of result string not found\n"); | |
264 | break; | |
265 | } | |
065db052 | 266 | util_strscpy(tmp, sizeof(tmp), cpos); |
f1128767 KS |
267 | /* %{2+}c copies the whole string from the second part on */ |
268 | if (rest[0] != '+') { | |
065db052 | 269 | cpos = strchr(tmp, ' '); |
f1128767 KS |
270 | if (cpos) |
271 | cpos[0] = '\0'; | |
272 | } | |
065db052 KS |
273 | l = util_strpcpy(&s, l, tmp); |
274 | dbg(event->udev, "substitute part of result string '%s'\n", tmp); | |
f1128767 | 275 | } else { |
065db052 | 276 | l = util_strpcpy(&s, l, event->program_result); |
f1128767 KS |
277 | dbg(event->udev, "substitute result string '%s'\n", event->program_result); |
278 | } | |
279 | break; | |
065db052 KS |
280 | } |
281 | case SUBST_ATTR: { | |
4950b6e6 KS |
282 | const char *value = NULL; |
283 | char vbuf[UTIL_NAME_SIZE]; | |
065db052 KS |
284 | size_t len; |
285 | int count; | |
286 | ||
287 | if (attr == NULL) { | |
f1128767 | 288 | err(event->udev, "missing file parameter for attr\n"); |
065db052 KS |
289 | break; |
290 | } | |
f1128767 | 291 | |
4950b6e6 KS |
292 | /* try to read the value specified by "[dmi/id]product_name" */ |
293 | if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) | |
294 | value = vbuf; | |
f1128767 | 295 | |
4950b6e6 KS |
296 | /* try to read the attribute the device */ |
297 | if (value == NULL) | |
298 | value = udev_device_get_sysattr_value(event->dev, attr); | |
f1128767 | 299 | |
065db052 | 300 | /* try to read the attribute of the parent device, other matches have selected */ |
4950b6e6 KS |
301 | if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) |
302 | value = udev_device_get_sysattr_value(event->dev_parent, attr); | |
065db052 | 303 | |
4950b6e6 | 304 | if (value == NULL) |
065db052 KS |
305 | break; |
306 | ||
307 | /* strip trailing whitespace, and replace unwanted characters */ | |
4950b6e6 KS |
308 | if (value != vbuf) |
309 | util_strscpy(vbuf, sizeof(vbuf), value); | |
310 | len = strlen(vbuf); | |
311 | while (len > 0 && isspace(vbuf[--len])) | |
312 | vbuf[len] = '\0'; | |
313 | count = udev_util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); | |
065db052 KS |
314 | if (count > 0) |
315 | info(event->udev, "%i character(s) replaced\n" , count); | |
4950b6e6 KS |
316 | l = util_strpcpy(&s, l, vbuf); |
317 | dbg(event->udev, "substitute sysfs value '%s'\n", vbuf); | |
f1128767 | 318 | break; |
065db052 KS |
319 | } |
320 | case SUBST_PARENT: { | |
321 | struct udev_device *dev_parent; | |
322 | const char *devnode; | |
f1128767 | 323 | |
065db052 KS |
324 | dev_parent = udev_device_get_parent(event->dev); |
325 | if (dev_parent == NULL) | |
326 | break; | |
f1128767 | 327 | devnode = udev_device_get_devnode(dev_parent); |
065db052 KS |
328 | if (devnode != NULL) { |
329 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | |
f1128767 | 330 | |
065db052 KS |
331 | l = util_strpcpy(&s, l, &devnode[devlen]); |
332 | dbg(event->udev, "found parent '%s', got node name '%s'\n", | |
333 | udev_device_get_syspath(dev_parent), &devnode[devlen]); | |
f1128767 KS |
334 | } |
335 | break; | |
065db052 KS |
336 | } |
337 | case SUBST_TEMP_NODE: { | |
338 | dev_t devnum; | |
339 | struct stat statbuf; | |
340 | char filename[UTIL_PATH_SIZE]; | |
341 | const char *devtype; | |
342 | ||
343 | if (event->tmp_node != NULL) { | |
344 | l = util_strpcpy(&s, l, event->tmp_node); | |
345 | dbg(event->udev, "tempnode: return earlier created one\n"); | |
346 | break; | |
347 | } | |
348 | devnum = udev_device_get_devnum(dev); | |
349 | if (major(devnum) == 0) | |
350 | break; | |
351 | /* lookup kernel provided node */ | |
352 | if (udev_device_get_knodename(dev) != NULL) { | |
353 | util_strscpyl(filename, sizeof(filename), | |
354 | udev_get_dev_path(event->udev), "/", udev_device_get_knodename(dev), NULL); | |
5a05e120 | 355 | if (stat(filename, &statbuf) == 0 && statbuf.st_rdev == devnum) { |
065db052 KS |
356 | l = util_strpcpy(&s, l, filename); |
357 | dbg(event->udev, "tempnode: return kernel node\n"); | |
5a05e120 KS |
358 | break; |
359 | } | |
f1128767 | 360 | } |
065db052 KS |
361 | /* lookup /dev/{char,block}/<maj>:<min> */ |
362 | if (strcmp(udev_device_get_subsystem(dev), "block") == 0) | |
363 | devtype = "block"; | |
364 | else | |
365 | devtype = "char"; | |
366 | snprintf(filename, sizeof(filename), "%s/%s/%u:%u", | |
367 | udev_get_dev_path(event->udev), devtype, | |
368 | major(udev_device_get_devnum(dev)), | |
369 | minor(udev_device_get_devnum(dev))); | |
370 | if (stat(filename, &statbuf) == 0 && statbuf.st_rdev == devnum) { | |
371 | l = util_strpcpy(&s, l, filename); | |
372 | dbg(event->udev, "tempnode: return maj:min node\n"); | |
373 | break; | |
374 | } | |
375 | /* create temporary node */ | |
376 | dbg(event->udev, "tempnode: create temp node\n"); | |
377 | asprintf(&event->tmp_node, "%s/.tmp-%s-%u:%u", | |
378 | udev_get_dev_path(event->udev), devtype, | |
379 | major(udev_device_get_devnum(dev)), | |
380 | minor(udev_device_get_devnum(dev))); | |
381 | if (event->tmp_node == NULL) | |
382 | break; | |
c7cdd8b2 | 383 | udev_node_mknod(dev, event->tmp_node, 0600, 0, 0); |
065db052 | 384 | l = util_strpcpy(&s, l, event->tmp_node); |
f1128767 | 385 | break; |
065db052 | 386 | } |
f1128767 KS |
387 | case SUBST_NAME: |
388 | if (event->name != NULL) { | |
065db052 | 389 | l = util_strpcpy(&s, l, event->name); |
f1128767 KS |
390 | dbg(event->udev, "substitute name '%s'\n", event->name); |
391 | } else { | |
065db052 | 392 | l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); |
f1128767 KS |
393 | dbg(event->udev, "substitute sysname '%s'\n", udev_device_get_sysname(dev)); |
394 | } | |
395 | break; | |
065db052 KS |
396 | case SUBST_LINKS: { |
397 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | |
398 | struct udev_list_entry *list_entry; | |
f1128767 | 399 | |
065db052 KS |
400 | list_entry = udev_device_get_devlinks_list_entry(dev); |
401 | if (list_entry == NULL) | |
402 | break; | |
403 | l = util_strpcpy(&s, l, &udev_list_entry_get_name(list_entry)[devlen]); | |
404 | udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) | |
405 | l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL); | |
f1128767 | 406 | break; |
065db052 | 407 | } |
f1128767 | 408 | case SUBST_ROOT: |
065db052 | 409 | l = util_strpcpy(&s, l, udev_get_dev_path(event->udev)); |
f1128767 KS |
410 | dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); |
411 | break; | |
412 | case SUBST_SYS: | |
065db052 | 413 | l = util_strpcpy(&s, l, udev_get_sys_path(event->udev)); |
f1128767 KS |
414 | dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); |
415 | break; | |
416 | case SUBST_ENV: | |
417 | if (attr == NULL) { | |
418 | dbg(event->udev, "missing attribute\n"); | |
419 | break; | |
420 | } else { | |
f1128767 KS |
421 | const char *value; |
422 | ||
3d7b2831 | 423 | value = udev_device_get_property_value(event->dev, attr); |
5375c80e KS |
424 | if (value == NULL) |
425 | break; | |
f1128767 | 426 | dbg(event->udev, "substitute env '%s=%s'\n", attr, value); |
065db052 | 427 | l = util_strpcpy(&s, l, value); |
f1128767 KS |
428 | break; |
429 | } | |
430 | default: | |
431 | err(event->udev, "unknown substitution type=%i\n", type); | |
432 | break; | |
433 | } | |
f1128767 | 434 | } |
065db052 KS |
435 | |
436 | out: | |
437 | s[0] = '\0'; | |
438 | dbg(event->udev, "'%s' -> '%s' (%zu)\n", src, dest, l); | |
439 | return l; | |
f1128767 KS |
440 | } |
441 | ||
442 | static void rename_netif_kernel_log(struct ifreq ifr) | |
d46f37fd KS |
443 | { |
444 | int klog; | |
445 | FILE *f; | |
446 | ||
447 | klog = open("/dev/kmsg", O_WRONLY); | |
448 | if (klog < 0) | |
449 | return; | |
450 | ||
451 | f = fdopen(klog, "w"); | |
452 | if (f == NULL) { | |
453 | close(klog); | |
454 | return; | |
455 | } | |
456 | ||
cf3b3fbc | 457 | fprintf(f, "<30>udev[%u]: renamed network interface %s to %s\n", |
c25bfbfb | 458 | getpid(), ifr.ifr_name, ifr.ifr_newname); |
d46f37fd KS |
459 | fclose(f); |
460 | } | |
461 | ||
aa8734ff | 462 | static int rename_netif(struct udev_event *event) |
d46f37fd | 463 | { |
aa8734ff | 464 | struct udev_device *dev = event->dev; |
d46f37fd KS |
465 | int sk; |
466 | struct ifreq ifr; | |
ce9a42be | 467 | int loop; |
aa8734ff | 468 | int err; |
d46f37fd | 469 | |
aa8734ff KS |
470 | info(event->udev, "changing net interface name from '%s' to '%s'\n", |
471 | udev_device_get_sysname(dev), event->name); | |
d46f37fd KS |
472 | |
473 | sk = socket(PF_INET, SOCK_DGRAM, 0); | |
474 | if (sk < 0) { | |
1c2311c5 | 475 | err = -errno; |
aa8734ff | 476 | err(event->udev, "error opening socket: %m\n"); |
1c2311c5 | 477 | return err; |
d46f37fd KS |
478 | } |
479 | ||
480 | memset(&ifr, 0x00, sizeof(struct ifreq)); | |
065db052 KS |
481 | util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev)); |
482 | util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); | |
aa8734ff | 483 | err = ioctl(sk, SIOCSIFNAME, &ifr); |
ce9a42be | 484 | if (err == 0) { |
f1128767 | 485 | rename_netif_kernel_log(ifr); |
ce9a42be KS |
486 | goto out; |
487 | } | |
d46f37fd | 488 | |
ce9a42be KS |
489 | /* keep trying if the destination interface name already exists */ |
490 | err = -errno; | |
491 | if (err != -EEXIST) | |
492 | goto out; | |
d46f37fd | 493 | |
ce9a42be KS |
494 | /* free our own name, another process may wait for us */ |
495 | util_strscpyl(ifr.ifr_newname, IFNAMSIZ, udev_device_get_sysname(dev), "-", event->name, NULL); | |
496 | err = ioctl(sk, SIOCSIFNAME, &ifr); | |
497 | if (err < 0) { | |
498 | err = -errno; | |
499 | goto out; | |
500 | } | |
959e8b5d | 501 | |
ce9a42be KS |
502 | /* log temporary name */ |
503 | rename_netif_kernel_log(ifr); | |
d46f37fd | 504 | |
ce9a42be KS |
505 | /* wait a maximum of 90 seconds for our target to become available */ |
506 | util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname); | |
507 | util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); | |
508 | loop = 90 * 20; | |
509 | while (loop--) { | |
510 | const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 }; | |
511 | ||
512 | dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", | |
513 | event->name, (90 * 20) - loop); | |
514 | nanosleep(&duration, NULL); | |
515 | ||
516 | err = ioctl(sk, SIOCSIFNAME, &ifr); | |
517 | if (err == 0) { | |
518 | rename_netif_kernel_log(ifr); | |
519 | break; | |
d46f37fd | 520 | } |
ce9a42be KS |
521 | err = -errno; |
522 | if (err != -EEXIST) | |
523 | break; | |
d46f37fd | 524 | } |
ce9a42be KS |
525 | |
526 | out: | |
527 | if (err < 0) | |
528 | err(event->udev, "error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname); | |
d46f37fd | 529 | close(sk); |
aa8734ff | 530 | return err; |
d46f37fd KS |
531 | } |
532 | ||
dcdcb8cc | 533 | int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules) |
d46f37fd | 534 | { |
aa8734ff KS |
535 | struct udev_device *dev = event->dev; |
536 | int err = 0; | |
d46f37fd | 537 | |
4281da1f KS |
538 | if (udev_device_get_subsystem(dev) == NULL) |
539 | return -1; | |
540 | ||
0ec5b5e1 | 541 | if (strcmp(udev_device_get_action(dev), "remove") == 0) { |
5f59fa09 | 542 | udev_device_read_db(dev, NULL); |
0ec5b5e1 | 543 | udev_device_delete_db(dev); |
c1dbe11d | 544 | udev_device_tag_index(dev, NULL, false); |
3d3a0a70 | 545 | |
0ec5b5e1 KS |
546 | if (major(udev_device_get_devnum(dev)) != 0) |
547 | udev_watch_end(event->udev, dev); | |
3d3a0a70 | 548 | |
6880b25d | 549 | udev_rules_apply_to_event(rules, event); |
cb14f454 | 550 | |
0ec5b5e1 KS |
551 | if (major(udev_device_get_devnum(dev)) != 0) |
552 | err = udev_node_remove(dev); | |
553 | } else { | |
554 | event->dev_db = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); | |
555 | if (event->dev_db != NULL) { | |
5f59fa09 | 556 | udev_device_read_db(event->dev_db, NULL); |
0ec5b5e1 | 557 | udev_device_set_info_loaded(event->dev_db); |
d46f37fd | 558 | |
0ec5b5e1 KS |
559 | /* disable watch during event processing */ |
560 | if (major(udev_device_get_devnum(dev)) != 0) | |
561 | udev_watch_end(event->udev, event->dev_db); | |
cb14f454 | 562 | } |
aa8734ff | 563 | |
6880b25d | 564 | udev_rules_apply_to_event(rules, event); |
d46f37fd | 565 | |
0ec5b5e1 | 566 | /* rename a new network interface, if needed */ |
ff0e1f4e | 567 | if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 && |
0ec5b5e1 | 568 | event->name != NULL && strcmp(event->name, udev_device_get_sysname(dev)) != 0) { |
aa8734ff | 569 | char syspath[UTIL_PATH_SIZE]; |
d46f37fd KS |
570 | char *pos; |
571 | ||
aa8734ff | 572 | err = rename_netif(event); |
0ec5b5e1 KS |
573 | if (err == 0) { |
574 | info(event->udev, "renamed netif to '%s'\n", event->name); | |
575 | ||
576 | /* delete stale db file */ | |
577 | udev_device_delete_db(dev); | |
c1dbe11d | 578 | udev_device_tag_index(dev, NULL, false); |
0ec5b5e1 KS |
579 | |
580 | /* remember old name */ | |
581 | udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); | |
582 | ||
583 | /* now change the devpath, because the kernel device name has changed */ | |
584 | util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); | |
585 | pos = strrchr(syspath, '/'); | |
586 | if (pos != NULL) { | |
587 | pos++; | |
588 | util_strscpy(pos, sizeof(syspath) - (pos - syspath), event->name); | |
589 | udev_device_set_syspath(event->dev, syspath); | |
590 | udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); | |
591 | info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); | |
592 | } | |
d46f37fd KS |
593 | } |
594 | } | |
d46f37fd | 595 | |
0ec5b5e1 KS |
596 | if (major(udev_device_get_devnum(dev)) != 0) { |
597 | char filename[UTIL_PATH_SIZE]; | |
aa8734ff | 598 | |
0ec5b5e1 KS |
599 | if (event->tmp_node != NULL) { |
600 | info(event->udev, "cleanup temporary device node\n"); | |
601 | util_unlink_secure(event->udev, event->tmp_node); | |
602 | free(event->tmp_node); | |
603 | event->tmp_node = NULL; | |
604 | } | |
3d3a0a70 | 605 | |
0ec5b5e1 KS |
606 | /* no rule, use kernel provided name */ |
607 | if (event->name == NULL) { | |
608 | if (udev_device_get_knodename(dev) != NULL) { | |
609 | event->name = strdup(udev_device_get_knodename(dev)); | |
610 | info(event->udev, "no node name set, will use kernel supplied name '%s'\n", event->name); | |
611 | } else { | |
612 | event->name = strdup(udev_device_get_sysname(event->dev)); | |
613 | info(event->udev, "no node name set, will use device name '%s'\n", event->name); | |
614 | } | |
615 | } | |
616 | ||
75cb1ac5 | 617 | if (event->name == NULL || event->name[0] == '\0') { |
0ec5b5e1 | 618 | udev_device_delete_db(dev); |
c1dbe11d | 619 | udev_device_tag_index(dev, NULL, false); |
0ec5b5e1 KS |
620 | udev_device_unref(event->dev_db); |
621 | err = -ENOMEM; | |
75cb1ac5 | 622 | err(event->udev, "no node name, something went wrong, ignoring\n"); |
0ec5b5e1 KS |
623 | goto out; |
624 | } | |
aa8734ff | 625 | |
75cb1ac5 KS |
626 | if (udev_device_get_knodename(dev) != NULL && strcmp(udev_device_get_knodename(dev), event->name) != 0) |
627 | err(event->udev, "kernel-provided name '%s' and NAME= '%s' disagree, " | |
628 | "please use SYMLINK+= or change the kernel to provide the proper name\n", | |
629 | udev_device_get_knodename(dev), event->name); | |
630 | ||
0ec5b5e1 KS |
631 | /* set device node name */ |
632 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", event->name, NULL); | |
633 | udev_device_set_devnode(dev, filename); | |
d46f37fd | 634 | |
0ec5b5e1 KS |
635 | /* remove/update possible left-over symlinks from old database entry */ |
636 | if (event->dev_db != NULL) | |
637 | udev_node_update_old_links(dev, event->dev_db); | |
638 | ||
f2291cd0 KS |
639 | /* change default 0600 to 0660 if a group is assigned */ |
640 | if (event->mode == 0600 && event->gid > 0) | |
641 | event->mode = 0660; | |
642 | ||
75cb1ac5 | 643 | err = udev_node_add(dev, event->mode, event->uid, event->gid); |
d46f37fd | 644 | } |
d46f37fd | 645 | |
9c6a11b1 KS |
646 | /* preserve old, or get new initialization timestamp */ |
647 | if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0) | |
648 | udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db)); | |
649 | else | |
650 | udev_device_set_usec_initialized(event->dev, usec_monotonic()); | |
651 | ||
652 | /* (re)write database file */ | |
48a0170b KS |
653 | udev_device_update_db(dev); |
654 | udev_device_tag_index(dev, event->dev_db, true); | |
655 | udev_device_set_is_initialized(dev); | |
656 | ||
0ec5b5e1 KS |
657 | udev_device_unref(event->dev_db); |
658 | event->dev_db = NULL; | |
d46f37fd | 659 | } |
0ec5b5e1 | 660 | out: |
aa8734ff | 661 | return err; |
d46f37fd | 662 | } |
2d73813e | 663 | |
d412a685 | 664 | int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) |
2d73813e KS |
665 | { |
666 | struct udev_list_entry *list_entry; | |
667 | int err = 0; | |
668 | ||
669 | dbg(event->udev, "executing run list\n"); | |
670 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { | |
671 | const char *cmd = udev_list_entry_get_name(list_entry); | |
672 | ||
673 | if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { | |
674 | struct udev_monitor *monitor; | |
675 | ||
676 | monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); | |
677 | if (monitor == NULL) | |
678 | continue; | |
1e03b754 | 679 | udev_monitor_send_device(monitor, NULL, event->dev); |
2d73813e KS |
680 | udev_monitor_unref(monitor); |
681 | } else { | |
682 | char program[UTIL_PATH_SIZE]; | |
683 | char **envp; | |
684 | ||
065db052 | 685 | udev_event_apply_format(event, cmd, program, sizeof(program)); |
2d73813e | 686 | envp = udev_device_get_properties_envp(event->dev); |
c830e98d KS |
687 | if (event->exec_delay > 0) { |
688 | info(event->udev, "delay execution of '%s'\n", program); | |
689 | sleep(event->exec_delay); | |
690 | } | |
e85f5ec1 | 691 | if (util_run_program(event->udev, program, envp, NULL, 0, NULL, sigmask, true) != 0) { |
fbb31cd6 | 692 | if (udev_list_entry_get_flags(list_entry)) |
2d73813e KS |
693 | err = -1; |
694 | } | |
695 | } | |
696 | } | |
697 | return err; | |
698 | } |