]>
Commit | Line | Data |
---|---|---|
d46f37fd | 1 | /* |
55e9959b | 2 | * Copyright (C) 2004-2008 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> | |
26 | #include <sys/ioctl.h> | |
27 | #include <sys/socket.h> | |
28 | #include <net/if.h> | |
29 | #include <linux/sockios.h> | |
30 | ||
31 | #include "udev.h" | |
d46f37fd | 32 | |
aa8734ff KS |
33 | struct udev_event *udev_event_new(struct udev_device *dev) |
34 | { | |
35 | struct udev_event *event; | |
36 | ||
37 | event = malloc(sizeof(struct udev_event)); | |
38 | if (event == NULL) | |
39 | return NULL; | |
40 | memset(event, 0x00, sizeof(struct udev_event)); | |
41 | ||
42 | event->dev = dev; | |
43 | event->udev = udev_device_get_udev(dev); | |
44 | udev_list_init(&event->run_list); | |
45 | event->mode = 0660; | |
46 | util_strlcpy(event->owner, "0", sizeof(event->owner)); | |
47 | util_strlcpy(event->group, "0", sizeof(event->group)); | |
48 | ||
49 | dbg(event->udev, "allocated event %p\n", event); | |
50 | return event; | |
51 | } | |
52 | ||
53 | void udev_event_unref(struct udev_event *event) | |
54 | { | |
eb8837e1 | 55 | udev_list_cleanup_entries(event->udev, &event->run_list); |
aa8734ff KS |
56 | dbg(event->udev, "free event %p\n", event); |
57 | free(event); | |
58 | } | |
59 | ||
f1128767 KS |
60 | /* extract possible {attr} and move str behind it */ |
61 | static char *get_format_attribute(struct udev *udev, char **str) | |
62 | { | |
63 | char *pos; | |
64 | char *attr = NULL; | |
65 | ||
66 | if (*str[0] == '{') { | |
67 | pos = strchr(*str, '}'); | |
68 | if (pos == NULL) { | |
69 | err(udev, "missing closing brace for format\n"); | |
70 | return NULL; | |
71 | } | |
72 | pos[0] = '\0'; | |
73 | attr = *str+1; | |
74 | *str = pos+1; | |
75 | dbg(udev, "attribute='%s', str='%s'\n", attr, *str); | |
76 | } | |
77 | return attr; | |
78 | } | |
79 | ||
80 | /* extract possible format length and move str behind it*/ | |
81 | static int get_format_len(struct udev *udev, char **str) | |
82 | { | |
83 | int num; | |
84 | char *tail; | |
85 | ||
86 | if (isdigit(*str[0])) { | |
87 | num = (int) strtoul(*str, &tail, 10); | |
88 | if (num > 0) { | |
89 | *str = tail; | |
90 | dbg(udev, "format length=%i\n", num); | |
91 | return num; | |
92 | } else { | |
93 | err(udev, "format parsing error '%s'\n", *str); | |
94 | } | |
95 | } | |
96 | return -1; | |
97 | } | |
98 | ||
99 | /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */ | |
100 | int udev_event_apply_subsys_kernel(struct udev_event *udev_event, const char *string, | |
101 | char *result, size_t maxsize, int read_value) | |
102 | { | |
103 | char temp[UTIL_PATH_SIZE]; | |
104 | char *subsys; | |
105 | char *sysname; | |
106 | char *attr; | |
107 | struct udev_device *dev; | |
108 | ||
109 | if (string == NULL) | |
110 | string = result; | |
111 | if (string[0] != '[') | |
112 | return -1; | |
113 | ||
114 | util_strlcpy(temp, string, sizeof(temp)); | |
115 | ||
116 | subsys = &temp[1]; | |
117 | ||
118 | sysname = strchr(subsys, '/'); | |
119 | if (sysname == NULL) | |
120 | return -1; | |
121 | sysname[0] = '\0'; | |
122 | sysname = &sysname[1]; | |
123 | ||
124 | attr = strchr(sysname, ']'); | |
125 | if (attr == NULL) | |
126 | return -1; | |
127 | attr[0] = '\0'; | |
128 | attr = &attr[1]; | |
129 | if (attr[0] == '/') | |
130 | attr = &attr[1]; | |
131 | if (attr[0] == '\0') | |
132 | attr = NULL; | |
133 | ||
134 | if (read_value && attr == NULL) | |
135 | return -1; | |
136 | ||
137 | dev = udev_device_new_from_subsystem_sysname(udev_event->udev, subsys, sysname); | |
138 | if (dev == NULL) | |
139 | return -1; | |
140 | ||
141 | if (read_value) { | |
142 | const char *val; | |
143 | ||
144 | val = udev_device_get_attr_value(dev, attr); | |
145 | if (val != NULL) | |
146 | util_strlcpy(result, val, maxsize); | |
147 | else | |
148 | result[0] = '\0'; | |
149 | info(udev_event->udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); | |
150 | } else { | |
151 | util_strlcpy(result, udev_device_get_syspath(dev), maxsize); | |
152 | if (attr != NULL) { | |
153 | util_strlcat(result, "/", maxsize); | |
154 | util_strlcat(result, attr, maxsize); | |
155 | } | |
156 | info(udev_event->udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); | |
157 | } | |
158 | udev_device_unref(dev); | |
159 | return 0; | |
160 | } | |
161 | ||
162 | void udev_event_apply_format(struct udev_event *event, char *string, size_t maxsize) | |
163 | { | |
164 | struct udev_device *dev = event->dev; | |
165 | char temp[UTIL_PATH_SIZE]; | |
166 | char temp2[UTIL_PATH_SIZE]; | |
167 | char *head, *tail, *cpos, *attr, *rest; | |
168 | int len; | |
169 | int i; | |
170 | int count; | |
171 | enum subst_type { | |
172 | SUBST_UNKNOWN, | |
173 | SUBST_DEVPATH, | |
174 | SUBST_KERNEL, | |
175 | SUBST_KERNEL_NUMBER, | |
176 | SUBST_ID, | |
177 | SUBST_DRIVER, | |
178 | SUBST_MAJOR, | |
179 | SUBST_MINOR, | |
180 | SUBST_RESULT, | |
181 | SUBST_ATTR, | |
182 | SUBST_PARENT, | |
183 | SUBST_TEMP_NODE, | |
184 | SUBST_NAME, | |
185 | SUBST_LINKS, | |
186 | SUBST_ROOT, | |
187 | SUBST_SYS, | |
188 | SUBST_ENV, | |
189 | }; | |
190 | static const struct subst_map { | |
191 | char *name; | |
192 | char fmt; | |
193 | enum subst_type type; | |
194 | } map[] = { | |
195 | { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, | |
196 | { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, | |
197 | { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, | |
198 | { .name = "id", .fmt = 'b', .type = SUBST_ID }, | |
199 | { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, | |
200 | { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, | |
201 | { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, | |
202 | { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, | |
203 | { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, | |
204 | { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, | |
205 | { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, | |
206 | { .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE }, | |
207 | { .name = "name", .fmt = 'D', .type = SUBST_NAME }, | |
208 | { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, | |
209 | { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, | |
210 | { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, | |
211 | { .name = "env", .fmt = 'E', .type = SUBST_ENV }, | |
212 | { NULL, '\0', 0 } | |
213 | }; | |
214 | enum subst_type type; | |
215 | const struct subst_map *subst; | |
216 | ||
217 | head = string; | |
218 | while (1) { | |
219 | len = -1; | |
220 | while (head[0] != '\0') { | |
221 | if (head[0] == '$') { | |
222 | /* substitute named variable */ | |
223 | if (head[1] == '\0') | |
224 | break; | |
225 | if (head[1] == '$') { | |
226 | util_strlcpy(temp, head+2, sizeof(temp)); | |
227 | util_strlcpy(head+1, temp, maxsize); | |
228 | head++; | |
229 | continue; | |
230 | } | |
231 | head[0] = '\0'; | |
232 | for (subst = map; subst->name; subst++) { | |
233 | if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { | |
234 | type = subst->type; | |
235 | tail = head + strlen(subst->name)+1; | |
236 | dbg(event->udev, "will substitute format name '%s'\n", subst->name); | |
237 | goto found; | |
238 | } | |
239 | } | |
240 | head[0] = '$'; | |
241 | err(event->udev, "unknown format variable '%s'\n", head); | |
242 | } else if (head[0] == '%') { | |
243 | /* substitute format char */ | |
244 | if (head[1] == '\0') | |
245 | break; | |
246 | if (head[1] == '%') { | |
247 | util_strlcpy(temp, head+2, sizeof(temp)); | |
248 | util_strlcpy(head+1, temp, maxsize); | |
249 | head++; | |
250 | continue; | |
251 | } | |
252 | head[0] = '\0'; | |
253 | tail = head+1; | |
254 | len = get_format_len(event->udev, &tail); | |
255 | for (subst = map; subst->name; subst++) { | |
256 | if (tail[0] == subst->fmt) { | |
257 | type = subst->type; | |
258 | tail++; | |
259 | dbg(event->udev, "will substitute format char '%c'\n", subst->fmt); | |
260 | goto found; | |
261 | } | |
262 | } | |
263 | head[0] = '%'; | |
264 | err(event->udev, "unknown format char '%c'\n", tail[0]); | |
265 | } | |
266 | head++; | |
267 | } | |
268 | break; | |
269 | found: | |
270 | attr = get_format_attribute(event->udev, &tail); | |
271 | util_strlcpy(temp, tail, sizeof(temp)); | |
272 | dbg(event->udev, "format=%i, string='%s', tail='%s'\n", type ,string, tail); | |
273 | ||
274 | switch (type) { | |
275 | case SUBST_DEVPATH: | |
276 | util_strlcat(string, udev_device_get_devpath(dev), maxsize); | |
277 | dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); | |
278 | break; | |
279 | case SUBST_KERNEL: | |
280 | util_strlcat(string, udev_device_get_sysname(dev), maxsize); | |
281 | dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); | |
282 | break; | |
283 | case SUBST_KERNEL_NUMBER: | |
284 | if (udev_device_get_sysnum(dev) == NULL) | |
285 | break; | |
286 | util_strlcat(string, udev_device_get_sysnum(dev), maxsize); | |
287 | dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); | |
288 | break; | |
289 | case SUBST_ID: | |
290 | if (event->dev_parent != NULL) { | |
291 | util_strlcat(string, udev_device_get_sysname(event->dev_parent), maxsize); | |
292 | dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); | |
293 | } | |
294 | break; | |
295 | case SUBST_DRIVER: | |
296 | if (event->dev_parent != NULL) { | |
297 | const char *driver = udev_device_get_driver(event->dev_parent); | |
298 | ||
299 | if (driver == NULL) | |
300 | break; | |
301 | util_strlcat(string, driver, maxsize); | |
302 | dbg(event->udev, "substitute driver '%s'\n", driver); | |
303 | } | |
304 | break; | |
305 | case SUBST_MAJOR: | |
306 | sprintf(temp2, "%d", major(udev_device_get_devnum(dev))); | |
307 | util_strlcat(string, temp2, maxsize); | |
308 | dbg(event->udev, "substitute major number '%s'\n", temp2); | |
309 | break; | |
310 | case SUBST_MINOR: | |
311 | sprintf(temp2, "%d", minor(udev_device_get_devnum(dev))); | |
312 | util_strlcat(string, temp2, maxsize); | |
313 | dbg(event->udev, "substitute minor number '%s'\n", temp2); | |
314 | break; | |
315 | case SUBST_RESULT: | |
316 | if (event->program_result[0] == '\0') | |
317 | break; | |
318 | /* get part part of the result string */ | |
319 | i = 0; | |
320 | if (attr != NULL) | |
321 | i = strtoul(attr, &rest, 10); | |
322 | if (i > 0) { | |
323 | dbg(event->udev, "request part #%d of result string\n", i); | |
324 | cpos = event->program_result; | |
325 | while (--i) { | |
326 | while (cpos[0] != '\0' && !isspace(cpos[0])) | |
327 | cpos++; | |
328 | while (isspace(cpos[0])) | |
329 | cpos++; | |
330 | } | |
331 | if (i > 0) { | |
332 | err(event->udev, "requested part of result string not found\n"); | |
333 | break; | |
334 | } | |
335 | util_strlcpy(temp2, cpos, sizeof(temp2)); | |
336 | /* %{2+}c copies the whole string from the second part on */ | |
337 | if (rest[0] != '+') { | |
338 | cpos = strchr(temp2, ' '); | |
339 | if (cpos) | |
340 | cpos[0] = '\0'; | |
341 | } | |
342 | util_strlcat(string, temp2, maxsize); | |
343 | dbg(event->udev, "substitute part of result string '%s'\n", temp2); | |
344 | } else { | |
345 | util_strlcat(string, event->program_result, maxsize); | |
346 | dbg(event->udev, "substitute result string '%s'\n", event->program_result); | |
347 | } | |
348 | break; | |
349 | case SUBST_ATTR: | |
350 | if (attr == NULL) | |
351 | err(event->udev, "missing file parameter for attr\n"); | |
352 | else { | |
353 | char value[UTIL_NAME_SIZE] = ""; | |
354 | size_t size; | |
355 | ||
356 | udev_event_apply_subsys_kernel(event, attr, value, sizeof(value), 1); | |
357 | ||
358 | /* try the current device, other matches may have selected */ | |
359 | if (value[0] == '\0' && event->dev_parent != NULL && event->dev_parent != event->dev) { | |
360 | const char *val; | |
361 | ||
362 | val = udev_device_get_attr_value(event->dev_parent, attr); | |
363 | if (val != NULL) | |
364 | util_strlcpy(value, val, sizeof(value)); | |
365 | } | |
366 | ||
367 | /* look at all devices along the chain of parents */ | |
368 | if (value[0] == '\0') { | |
369 | struct udev_device *dev_parent = dev; | |
370 | const char *val; | |
371 | ||
372 | do { | |
373 | dbg(event->udev, "looking at '%s'\n", udev_device_get_syspath(dev_parent)); | |
374 | val = udev_device_get_attr_value(dev_parent, attr); | |
375 | if (val != NULL) { | |
376 | util_strlcpy(value, val, sizeof(value)); | |
377 | break; | |
378 | } | |
379 | dev_parent = udev_device_get_parent(dev_parent); | |
380 | } while (dev_parent != NULL); | |
381 | } | |
382 | ||
383 | if (value[0]=='\0') | |
384 | break; | |
385 | ||
386 | /* strip trailing whitespace, and replace unwanted characters */ | |
387 | size = strlen(value); | |
388 | while (size > 0 && isspace(value[--size])) | |
389 | value[size] = '\0'; | |
390 | count = util_replace_chars(value, ALLOWED_CHARS_INPUT); | |
391 | if (count > 0) | |
392 | info(event->udev, "%i character(s) replaced\n" , count); | |
393 | util_strlcat(string, value, maxsize); | |
394 | dbg(event->udev, "substitute sysfs value '%s'\n", value); | |
395 | } | |
396 | break; | |
397 | case SUBST_PARENT: | |
398 | { | |
399 | struct udev_device *dev_parent; | |
400 | const char *devnode; | |
401 | ||
402 | dev_parent = udev_device_get_parent(event->dev); | |
403 | if (dev_parent == NULL) | |
404 | break; | |
405 | devnode = udev_device_get_devnode(dev_parent); | |
406 | if (devnode != NULL) { | |
407 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | |
408 | ||
409 | util_strlcat(string, &devnode[devlen], maxsize); | |
410 | dbg(event->udev, "found parent '%s', got node name '%s'\n", | |
411 | udev_device_get_syspath(dev_parent), &devnode[devlen]); | |
412 | } | |
413 | } | |
414 | break; | |
415 | case SUBST_TEMP_NODE: | |
416 | if (event->tmp_node[0] == '\0' && major(udev_device_get_devnum(dev)) > 0) { | |
417 | dbg(event->udev, "create temporary device node for callout\n"); | |
418 | snprintf(event->tmp_node, sizeof(event->tmp_node), "%s/.tmp-%u-%u", | |
419 | udev_get_dev_path(event->udev), | |
420 | major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); | |
421 | udev_node_mknod(dev, event->tmp_node, makedev(0,0), 0600, 0, 0); | |
422 | } | |
423 | util_strlcat(string, event->tmp_node, maxsize); | |
424 | dbg(event->udev, "substitute temporary device node name '%s'\n", event->tmp_node); | |
425 | break; | |
426 | case SUBST_NAME: | |
427 | if (event->name != NULL) { | |
428 | util_strlcat(string, event->name, maxsize); | |
429 | dbg(event->udev, "substitute name '%s'\n", event->name); | |
430 | } else { | |
431 | util_strlcat(string, udev_device_get_sysname(dev), maxsize); | |
432 | dbg(event->udev, "substitute sysname '%s'\n", udev_device_get_sysname(dev)); | |
433 | } | |
434 | break; | |
435 | case SUBST_LINKS: | |
436 | { | |
437 | struct udev_list_entry *list_entry; | |
438 | ||
439 | list_entry = udev_device_get_properties_list_entry(dev); | |
440 | util_strlcpy(string, udev_list_entry_get_name(list_entry), maxsize); | |
441 | udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) { | |
442 | util_strlcat(string, " ", maxsize); | |
443 | util_strlcat(string, udev_list_entry_get_name(list_entry), maxsize); | |
444 | } | |
445 | } | |
446 | break; | |
447 | case SUBST_ROOT: | |
448 | util_strlcat(string, udev_get_dev_path(event->udev), maxsize); | |
449 | dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); | |
450 | break; | |
451 | case SUBST_SYS: | |
452 | util_strlcat(string, udev_get_sys_path(event->udev), maxsize); | |
453 | dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); | |
454 | break; | |
455 | case SUBST_ENV: | |
456 | if (attr == NULL) { | |
457 | dbg(event->udev, "missing attribute\n"); | |
458 | break; | |
459 | } else { | |
460 | struct udev_list_entry *list_entry; | |
461 | const char *value; | |
462 | ||
463 | list_entry = udev_device_get_properties_list_entry(event->dev); | |
464 | list_entry = udev_list_entry_get_by_name(list_entry, attr); | |
465 | if (list_entry == NULL) | |
466 | break; | |
467 | value = udev_list_entry_get_value(list_entry); | |
468 | dbg(event->udev, "substitute env '%s=%s'\n", attr, value); | |
469 | util_strlcat(string, value, maxsize); | |
470 | break; | |
471 | } | |
472 | default: | |
473 | err(event->udev, "unknown substitution type=%i\n", type); | |
474 | break; | |
475 | } | |
476 | /* possibly truncate to format-char specified length */ | |
477 | if (len >= 0 && len < (int)strlen(head)) { | |
478 | head[len] = '\0'; | |
479 | dbg(event->udev, "truncate to %i chars, subtitution string becomes '%s'\n", len, head); | |
480 | } | |
481 | util_strlcat(string, temp, maxsize); | |
482 | } | |
483 | } | |
484 | ||
485 | static void rename_netif_kernel_log(struct ifreq ifr) | |
d46f37fd KS |
486 | { |
487 | int klog; | |
488 | FILE *f; | |
489 | ||
490 | klog = open("/dev/kmsg", O_WRONLY); | |
491 | if (klog < 0) | |
492 | return; | |
493 | ||
494 | f = fdopen(klog, "w"); | |
495 | if (f == NULL) { | |
496 | close(klog); | |
497 | return; | |
498 | } | |
499 | ||
500 | fprintf(f, "<6>udev: renamed network interface %s to %s\n", | |
501 | ifr.ifr_name, ifr.ifr_newname); | |
502 | fclose(f); | |
503 | } | |
504 | ||
aa8734ff | 505 | static int rename_netif(struct udev_event *event) |
d46f37fd | 506 | { |
aa8734ff | 507 | struct udev_device *dev = event->dev; |
d46f37fd KS |
508 | int sk; |
509 | struct ifreq ifr; | |
aa8734ff | 510 | int err; |
d46f37fd | 511 | |
aa8734ff KS |
512 | info(event->udev, "changing net interface name from '%s' to '%s'\n", |
513 | udev_device_get_sysname(dev), event->name); | |
514 | if (event->test) | |
d46f37fd KS |
515 | return 0; |
516 | ||
517 | sk = socket(PF_INET, SOCK_DGRAM, 0); | |
518 | if (sk < 0) { | |
aa8734ff | 519 | err(event->udev, "error opening socket: %m\n"); |
d46f37fd KS |
520 | return -1; |
521 | } | |
522 | ||
523 | memset(&ifr, 0x00, sizeof(struct ifreq)); | |
aa8734ff KS |
524 | util_strlcpy(ifr.ifr_name, udev_device_get_sysname(dev), IFNAMSIZ); |
525 | util_strlcpy(ifr.ifr_newname, event->name, IFNAMSIZ); | |
526 | err = ioctl(sk, SIOCSIFNAME, &ifr); | |
527 | if (err == 0) | |
f1128767 | 528 | rename_netif_kernel_log(ifr); |
d46f37fd KS |
529 | else { |
530 | int loop; | |
531 | ||
532 | /* see if the destination interface name already exists */ | |
533 | if (errno != EEXIST) { | |
aa8734ff | 534 | err(event->udev, "error changing netif name %s to %s: %m\n", |
659353f5 | 535 | ifr.ifr_name, ifr.ifr_newname); |
d46f37fd KS |
536 | goto exit; |
537 | } | |
538 | ||
539 | /* free our own name, another process may wait for us */ | |
aa8734ff | 540 | util_strlcpy(ifr.ifr_newname, udev_device_get_sysname(dev), IFNAMSIZ); |
31c1f537 | 541 | util_strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ); |
aa8734ff KS |
542 | err = ioctl(sk, SIOCSIFNAME, &ifr); |
543 | if (err != 0) { | |
544 | err(event->udev, "error changing netif name %s to %s: %m\n", | |
659353f5 | 545 | ifr.ifr_name, ifr.ifr_newname); |
d46f37fd KS |
546 | goto exit; |
547 | } | |
548 | ||
549 | /* wait 30 seconds for our target to become available */ | |
31c1f537 | 550 | util_strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ); |
aa8734ff | 551 | util_strlcpy(ifr.ifr_newname, udev_device_get_devnode(dev), IFNAMSIZ); |
d46f37fd KS |
552 | loop = 30 * 20; |
553 | while (loop--) { | |
aa8734ff KS |
554 | err = ioctl(sk, SIOCSIFNAME, &ifr); |
555 | if (err == 0) { | |
f1128767 | 556 | rename_netif_kernel_log(ifr); |
d46f37fd KS |
557 | break; |
558 | } | |
559 | ||
560 | if (errno != EEXIST) { | |
aa8734ff | 561 | err(event->udev, "error changing net interface name %s to %s: %m\n", |
659353f5 | 562 | ifr.ifr_name, ifr.ifr_newname); |
d46f37fd KS |
563 | break; |
564 | } | |
aa8734ff KS |
565 | dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", |
566 | udev_device_get_devnode(dev), (30 * 20) - loop); | |
d46f37fd KS |
567 | usleep(1000 * 1000 / 20); |
568 | } | |
569 | } | |
d46f37fd KS |
570 | exit: |
571 | close(sk); | |
aa8734ff | 572 | return err; |
d46f37fd KS |
573 | } |
574 | ||
dcdcb8cc | 575 | int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules) |
d46f37fd | 576 | { |
aa8734ff KS |
577 | struct udev_device *dev = event->dev; |
578 | int err = 0; | |
d46f37fd | 579 | |
aa8734ff KS |
580 | if (udev_device_get_devpath_old(dev) != NULL) { |
581 | if (udev_device_rename_db(dev, udev_device_get_devpath(dev)) == 0) | |
582 | info(event->udev, "moved database from '%s' to '%s'\n", | |
583 | udev_device_get_devpath_old(dev), udev_device_get_devpath(dev)); | |
584 | } | |
d46f37fd KS |
585 | |
586 | /* add device node */ | |
aa8734ff KS |
587 | if (major(udev_device_get_devnum(dev)) != 0 && |
588 | (strcmp(udev_device_get_action(dev), "add") == 0 || strcmp(udev_device_get_action(dev), "change") == 0)) { | |
589 | char filename[UTIL_PATH_SIZE]; | |
590 | struct udev_device *dev_old; | |
d46f37fd | 591 | |
aa8734ff | 592 | dbg(event->udev, "device node add '%s'\n", udev_device_get_devpath(dev)); |
d46f37fd | 593 | |
aa8734ff KS |
594 | udev_rules_get_name(rules, event); |
595 | if (event->ignore_device) { | |
596 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd KS |
597 | goto exit; |
598 | } | |
aa8734ff KS |
599 | |
600 | if (event->name[0] == '\0') { | |
601 | info(event->udev, "device node creation supressed\n"); | |
d46f37fd KS |
602 | goto exit; |
603 | } | |
604 | ||
aa8734ff KS |
605 | /* set device node name */ |
606 | util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); | |
607 | util_strlcat(filename, "/", sizeof(filename)); | |
608 | util_strlcat(filename, event->name, sizeof(filename)); | |
609 | udev_device_set_devnode(dev, filename); | |
610 | ||
d46f37fd | 611 | /* read current database entry; cleanup, if it is known device */ |
aa8734ff KS |
612 | dev_old = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); |
613 | if (dev_old != NULL) { | |
614 | info(event->udev, "device '%s' already in database, updating\n", | |
615 | udev_device_get_devpath(dev)); | |
616 | udev_node_update_old_links(dev, dev_old, event->test); | |
617 | udev_device_unref(dev_old); | |
d46f37fd KS |
618 | } |
619 | ||
aa8734ff | 620 | udev_device_update_db(dev); |
d46f37fd | 621 | |
aa8734ff KS |
622 | err = udev_node_add(dev, event->mode, event->owner, event->group, event->test); |
623 | if (err != 0) | |
624 | goto exit; | |
d46f37fd | 625 | |
d46f37fd KS |
626 | goto exit; |
627 | } | |
628 | ||
629 | /* add netif */ | |
aa8734ff KS |
630 | if (strcmp(udev_device_get_subsystem(dev), "net") == 0 && strcmp(udev_device_get_action(dev), "add") == 0) { |
631 | dbg(event->udev, "netif add '%s'\n", udev_device_get_devpath(dev)); | |
632 | ||
633 | udev_rules_get_name(rules, event); | |
634 | if (event->ignore_device) { | |
635 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd KS |
636 | goto exit; |
637 | } | |
aa8734ff KS |
638 | if (event->name[0] == '\0') { |
639 | info(event->udev, "device renaming supressed\n"); | |
d46f37fd KS |
640 | goto exit; |
641 | } | |
642 | ||
643 | /* look if we want to change the name of the netif */ | |
aa8734ff KS |
644 | if (strcmp(event->name, udev_device_get_sysname(dev)) != 0) { |
645 | char syspath[UTIL_PATH_SIZE]; | |
d46f37fd KS |
646 | char *pos; |
647 | ||
aa8734ff KS |
648 | err = rename_netif(event); |
649 | if (err != 0) | |
d46f37fd | 650 | goto exit; |
aa8734ff | 651 | info(event->udev, "renamed netif to '%s'\n", event->name); |
d46f37fd | 652 | |
aa8734ff KS |
653 | /* remember old name */ |
654 | udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); | |
d46f37fd KS |
655 | |
656 | /* now change the devpath, because the kernel device name has changed */ | |
aa8734ff KS |
657 | util_strlcpy(syspath, udev_device_get_syspath(dev), sizeof(syspath)); |
658 | pos = strrchr(syspath, '/'); | |
d46f37fd KS |
659 | if (pos != NULL) { |
660 | pos[1] = '\0'; | |
aa8734ff KS |
661 | util_strlcat(syspath, event->name, sizeof(syspath)); |
662 | udev_device_set_syspath(event->dev, syspath); | |
663 | udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); | |
664 | info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); | |
d46f37fd KS |
665 | } |
666 | } | |
667 | goto exit; | |
668 | } | |
669 | ||
670 | /* remove device node */ | |
aa8734ff KS |
671 | if (major(udev_device_get_devnum(dev)) != 0 && strcmp(udev_device_get_action(dev), "remove") == 0) { |
672 | /* import database entry and delete it */ | |
673 | udev_device_read_db(dev); | |
674 | if (!event->test) | |
675 | udev_device_delete_db(dev); | |
676 | ||
677 | if (udev_device_get_devnode(dev) == NULL) { | |
678 | char devnode[UTIL_PATH_SIZE]; | |
679 | ||
680 | info(event->udev, "'%s' not found in database, using kernel name '%s'\n", | |
681 | udev_device_get_syspath(dev), udev_device_get_sysname(dev)); | |
682 | util_strlcpy(devnode, udev_get_dev_path(event->udev), sizeof(devnode)); | |
683 | util_strlcat(devnode, "/", sizeof(devnode)); | |
684 | util_strlcat(devnode, udev_device_get_sysname(dev), sizeof(devnode)); | |
685 | udev_device_set_devnode(dev, devnode); | |
d46f37fd KS |
686 | } |
687 | ||
aa8734ff KS |
688 | udev_rules_get_run(rules, event); |
689 | if (event->ignore_device) { | |
690 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd KS |
691 | goto exit; |
692 | } | |
693 | ||
aa8734ff KS |
694 | if (udev_device_get_ignore_remove(dev)) { |
695 | info(event->udev, "ignore_remove for '%s'\n", udev_device_get_devnode(dev)); | |
d46f37fd KS |
696 | goto exit; |
697 | } | |
d46f37fd | 698 | |
aa8734ff | 699 | err = udev_node_remove(dev, event->test); |
d46f37fd KS |
700 | goto exit; |
701 | } | |
702 | ||
703 | /* default devices */ | |
aa8734ff KS |
704 | udev_rules_get_run(rules, event); |
705 | if (event->ignore_device) | |
706 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd | 707 | exit: |
aa8734ff | 708 | return err; |
d46f37fd | 709 | } |
2d73813e KS |
710 | |
711 | int udev_event_execute_run(struct udev_event *event) | |
712 | { | |
713 | struct udev_list_entry *list_entry; | |
714 | int err = 0; | |
715 | ||
716 | dbg(event->udev, "executing run list\n"); | |
717 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { | |
718 | const char *cmd = udev_list_entry_get_name(list_entry); | |
719 | ||
720 | if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { | |
721 | struct udev_monitor *monitor; | |
722 | ||
723 | monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); | |
724 | if (monitor == NULL) | |
725 | continue; | |
726 | udev_monitor_send_device(monitor, event->dev); | |
727 | udev_monitor_unref(monitor); | |
728 | } else { | |
729 | char program[UTIL_PATH_SIZE]; | |
730 | char **envp; | |
731 | ||
732 | util_strlcpy(program, cmd, sizeof(program)); | |
f1128767 | 733 | udev_event_apply_format(event, program, sizeof(program)); |
2d73813e KS |
734 | envp = udev_device_get_properties_envp(event->dev); |
735 | if (run_program(event->udev, program, envp, NULL, 0, NULL) != 0) { | |
736 | if (!udev_list_entry_get_flag(list_entry)) | |
737 | err = -1; | |
738 | } | |
739 | } | |
740 | } | |
741 | return err; | |
742 | } |