]>
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 | { | |
55 | udev_list_cleanup(event->udev, &event->run_list); | |
56 | dbg(event->udev, "free event %p\n", event); | |
57 | free(event); | |
58 | } | |
59 | ||
d46f37fd KS |
60 | static void kernel_log(struct ifreq ifr) |
61 | { | |
62 | int klog; | |
63 | FILE *f; | |
64 | ||
65 | klog = open("/dev/kmsg", O_WRONLY); | |
66 | if (klog < 0) | |
67 | return; | |
68 | ||
69 | f = fdopen(klog, "w"); | |
70 | if (f == NULL) { | |
71 | close(klog); | |
72 | return; | |
73 | } | |
74 | ||
75 | fprintf(f, "<6>udev: renamed network interface %s to %s\n", | |
76 | ifr.ifr_name, ifr.ifr_newname); | |
77 | fclose(f); | |
78 | } | |
79 | ||
aa8734ff | 80 | static int rename_netif(struct udev_event *event) |
d46f37fd | 81 | { |
aa8734ff | 82 | struct udev_device *dev = event->dev; |
d46f37fd KS |
83 | int sk; |
84 | struct ifreq ifr; | |
aa8734ff | 85 | int err; |
d46f37fd | 86 | |
aa8734ff KS |
87 | info(event->udev, "changing net interface name from '%s' to '%s'\n", |
88 | udev_device_get_sysname(dev), event->name); | |
89 | if (event->test) | |
d46f37fd KS |
90 | return 0; |
91 | ||
92 | sk = socket(PF_INET, SOCK_DGRAM, 0); | |
93 | if (sk < 0) { | |
aa8734ff | 94 | err(event->udev, "error opening socket: %m\n"); |
d46f37fd KS |
95 | return -1; |
96 | } | |
97 | ||
98 | memset(&ifr, 0x00, sizeof(struct ifreq)); | |
aa8734ff KS |
99 | util_strlcpy(ifr.ifr_name, udev_device_get_sysname(dev), IFNAMSIZ); |
100 | util_strlcpy(ifr.ifr_newname, event->name, IFNAMSIZ); | |
101 | err = ioctl(sk, SIOCSIFNAME, &ifr); | |
102 | if (err == 0) | |
d46f37fd KS |
103 | kernel_log(ifr); |
104 | else { | |
105 | int loop; | |
106 | ||
107 | /* see if the destination interface name already exists */ | |
108 | if (errno != EEXIST) { | |
aa8734ff | 109 | err(event->udev, "error changing netif name %s to %s: %m\n", |
659353f5 | 110 | ifr.ifr_name, ifr.ifr_newname); |
d46f37fd KS |
111 | goto exit; |
112 | } | |
113 | ||
114 | /* free our own name, another process may wait for us */ | |
aa8734ff | 115 | util_strlcpy(ifr.ifr_newname, udev_device_get_sysname(dev), IFNAMSIZ); |
31c1f537 | 116 | util_strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ); |
aa8734ff KS |
117 | err = ioctl(sk, SIOCSIFNAME, &ifr); |
118 | if (err != 0) { | |
119 | err(event->udev, "error changing netif name %s to %s: %m\n", | |
659353f5 | 120 | ifr.ifr_name, ifr.ifr_newname); |
d46f37fd KS |
121 | goto exit; |
122 | } | |
123 | ||
124 | /* wait 30 seconds for our target to become available */ | |
31c1f537 | 125 | util_strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ); |
aa8734ff | 126 | util_strlcpy(ifr.ifr_newname, udev_device_get_devnode(dev), IFNAMSIZ); |
d46f37fd KS |
127 | loop = 30 * 20; |
128 | while (loop--) { | |
aa8734ff KS |
129 | err = ioctl(sk, SIOCSIFNAME, &ifr); |
130 | if (err == 0) { | |
d46f37fd KS |
131 | kernel_log(ifr); |
132 | break; | |
133 | } | |
134 | ||
135 | if (errno != EEXIST) { | |
aa8734ff | 136 | err(event->udev, "error changing net interface name %s to %s: %m\n", |
659353f5 | 137 | ifr.ifr_name, ifr.ifr_newname); |
d46f37fd KS |
138 | break; |
139 | } | |
aa8734ff KS |
140 | dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", |
141 | udev_device_get_devnode(dev), (30 * 20) - loop); | |
d46f37fd KS |
142 | usleep(1000 * 1000 / 20); |
143 | } | |
144 | } | |
145 | ||
146 | exit: | |
147 | close(sk); | |
aa8734ff | 148 | return err; |
d46f37fd KS |
149 | } |
150 | ||
aa8734ff | 151 | int udev_event_run(struct udev_event *event, struct udev_rules *rules) |
d46f37fd | 152 | { |
aa8734ff KS |
153 | struct udev_device *dev = event->dev; |
154 | int err = 0; | |
d46f37fd | 155 | |
aa8734ff KS |
156 | if (udev_device_get_devpath_old(dev) != NULL) { |
157 | if (udev_device_rename_db(dev, udev_device_get_devpath(dev)) == 0) | |
158 | info(event->udev, "moved database from '%s' to '%s'\n", | |
159 | udev_device_get_devpath_old(dev), udev_device_get_devpath(dev)); | |
160 | } | |
d46f37fd KS |
161 | |
162 | /* add device node */ | |
aa8734ff KS |
163 | if (major(udev_device_get_devnum(dev)) != 0 && |
164 | (strcmp(udev_device_get_action(dev), "add") == 0 || strcmp(udev_device_get_action(dev), "change") == 0)) { | |
165 | char filename[UTIL_PATH_SIZE]; | |
166 | struct udev_device *dev_old; | |
d46f37fd | 167 | |
aa8734ff | 168 | dbg(event->udev, "device node add '%s'\n", udev_device_get_devpath(dev)); |
d46f37fd | 169 | |
aa8734ff KS |
170 | udev_rules_get_name(rules, event); |
171 | if (event->ignore_device) { | |
172 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd KS |
173 | goto exit; |
174 | } | |
aa8734ff KS |
175 | |
176 | if (event->name[0] == '\0') { | |
177 | info(event->udev, "device node creation supressed\n"); | |
d46f37fd KS |
178 | goto exit; |
179 | } | |
180 | ||
aa8734ff KS |
181 | /* set device node name */ |
182 | util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); | |
183 | util_strlcat(filename, "/", sizeof(filename)); | |
184 | util_strlcat(filename, event->name, sizeof(filename)); | |
185 | udev_device_set_devnode(dev, filename); | |
186 | ||
d46f37fd | 187 | /* read current database entry; cleanup, if it is known device */ |
aa8734ff KS |
188 | dev_old = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); |
189 | if (dev_old != NULL) { | |
190 | info(event->udev, "device '%s' already in database, updating\n", | |
191 | udev_device_get_devpath(dev)); | |
192 | udev_node_update_old_links(dev, dev_old, event->test); | |
193 | udev_device_unref(dev_old); | |
d46f37fd KS |
194 | } |
195 | ||
aa8734ff | 196 | udev_device_update_db(dev); |
d46f37fd | 197 | |
aa8734ff KS |
198 | err = udev_node_add(dev, event->mode, event->owner, event->group, event->test); |
199 | if (err != 0) | |
200 | goto exit; | |
d46f37fd | 201 | |
d46f37fd KS |
202 | goto exit; |
203 | } | |
204 | ||
205 | /* add netif */ | |
aa8734ff KS |
206 | if (strcmp(udev_device_get_subsystem(dev), "net") == 0 && strcmp(udev_device_get_action(dev), "add") == 0) { |
207 | dbg(event->udev, "netif add '%s'\n", udev_device_get_devpath(dev)); | |
208 | ||
209 | udev_rules_get_name(rules, event); | |
210 | if (event->ignore_device) { | |
211 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd KS |
212 | goto exit; |
213 | } | |
aa8734ff KS |
214 | if (event->name[0] == '\0') { |
215 | info(event->udev, "device renaming supressed\n"); | |
d46f37fd KS |
216 | goto exit; |
217 | } | |
218 | ||
219 | /* look if we want to change the name of the netif */ | |
aa8734ff KS |
220 | if (strcmp(event->name, udev_device_get_sysname(dev)) != 0) { |
221 | char syspath[UTIL_PATH_SIZE]; | |
d46f37fd KS |
222 | char *pos; |
223 | ||
aa8734ff KS |
224 | err = rename_netif(event); |
225 | if (err != 0) | |
d46f37fd | 226 | goto exit; |
aa8734ff | 227 | info(event->udev, "renamed netif to '%s'\n", event->name); |
d46f37fd | 228 | |
aa8734ff KS |
229 | /* remember old name */ |
230 | udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); | |
d46f37fd KS |
231 | |
232 | /* now change the devpath, because the kernel device name has changed */ | |
aa8734ff KS |
233 | util_strlcpy(syspath, udev_device_get_syspath(dev), sizeof(syspath)); |
234 | pos = strrchr(syspath, '/'); | |
d46f37fd KS |
235 | if (pos != NULL) { |
236 | pos[1] = '\0'; | |
aa8734ff KS |
237 | util_strlcat(syspath, event->name, sizeof(syspath)); |
238 | udev_device_set_syspath(event->dev, syspath); | |
239 | udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); | |
240 | info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); | |
d46f37fd KS |
241 | } |
242 | } | |
243 | goto exit; | |
244 | } | |
245 | ||
246 | /* remove device node */ | |
aa8734ff KS |
247 | if (major(udev_device_get_devnum(dev)) != 0 && strcmp(udev_device_get_action(dev), "remove") == 0) { |
248 | /* import database entry and delete it */ | |
249 | udev_device_read_db(dev); | |
250 | if (!event->test) | |
251 | udev_device_delete_db(dev); | |
252 | ||
253 | if (udev_device_get_devnode(dev) == NULL) { | |
254 | char devnode[UTIL_PATH_SIZE]; | |
255 | ||
256 | info(event->udev, "'%s' not found in database, using kernel name '%s'\n", | |
257 | udev_device_get_syspath(dev), udev_device_get_sysname(dev)); | |
258 | util_strlcpy(devnode, udev_get_dev_path(event->udev), sizeof(devnode)); | |
259 | util_strlcat(devnode, "/", sizeof(devnode)); | |
260 | util_strlcat(devnode, udev_device_get_sysname(dev), sizeof(devnode)); | |
261 | udev_device_set_devnode(dev, devnode); | |
d46f37fd KS |
262 | } |
263 | ||
aa8734ff KS |
264 | udev_rules_get_run(rules, event); |
265 | if (event->ignore_device) { | |
266 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd KS |
267 | goto exit; |
268 | } | |
269 | ||
aa8734ff KS |
270 | if (udev_device_get_ignore_remove(dev)) { |
271 | info(event->udev, "ignore_remove for '%s'\n", udev_device_get_devnode(dev)); | |
d46f37fd KS |
272 | goto exit; |
273 | } | |
d46f37fd | 274 | |
aa8734ff | 275 | err = udev_node_remove(dev, event->test); |
d46f37fd KS |
276 | goto exit; |
277 | } | |
278 | ||
279 | /* default devices */ | |
aa8734ff KS |
280 | udev_rules_get_run(rules, event); |
281 | if (event->ignore_device) | |
282 | info(event->udev, "device event will be ignored\n"); | |
d46f37fd | 283 | exit: |
aa8734ff | 284 | return err; |
d46f37fd | 285 | } |