chiark / gitweb /
07c02211a05e2f15d65dbdf723839da78262d0cd
[elogind.git] / udev / udev-event.c
1 /*
2  * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
3  *
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.
8  *
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/>.
16  */
17
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"
32
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
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
80 static int rename_netif(struct udev_event *event)
81 {
82         struct udev_device *dev = event->dev;
83         int sk;
84         struct ifreq ifr;
85         int err;
86
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)
90                 return 0;
91
92         sk = socket(PF_INET, SOCK_DGRAM, 0);
93         if (sk < 0) {
94                 err(event->udev, "error opening socket: %m\n");
95                 return -1;
96         }
97
98         memset(&ifr, 0x00, sizeof(struct ifreq));
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)
103                 kernel_log(ifr);
104         else {
105                 int loop;
106
107                 /* see if the destination interface name already exists */
108                 if (errno != EEXIST) {
109                         err(event->udev, "error changing netif name %s to %s: %m\n",
110                             ifr.ifr_name, ifr.ifr_newname);
111                         goto exit;
112                 }
113
114                 /* free our own name, another process may wait for us */
115                 util_strlcpy(ifr.ifr_newname, udev_device_get_sysname(dev), IFNAMSIZ);
116                 util_strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ);
117                 err = ioctl(sk, SIOCSIFNAME, &ifr);
118                 if (err != 0) {
119                         err(event->udev, "error changing netif name %s to %s: %m\n",
120                             ifr.ifr_name, ifr.ifr_newname);
121                         goto exit;
122                 }
123
124                 /* wait 30 seconds for our target to become available */
125                 util_strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ);
126                 util_strlcpy(ifr.ifr_newname, udev_device_get_devnode(dev), IFNAMSIZ);
127                 loop = 30 * 20;
128                 while (loop--) {
129                         err = ioctl(sk, SIOCSIFNAME, &ifr);
130                         if (err == 0) {
131                                 kernel_log(ifr);
132                                 break;
133                         }
134
135                         if (errno != EEXIST) {
136                                 err(event->udev, "error changing net interface name %s to %s: %m\n",
137                                     ifr.ifr_name, ifr.ifr_newname);
138                                 break;
139                         }
140                         dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n",
141                             udev_device_get_devnode(dev), (30 * 20) - loop);
142                         usleep(1000 * 1000 / 20);
143                 }
144         }
145
146 exit:
147         close(sk);
148         return err;
149 }
150
151 int udev_event_run(struct udev_event *event, struct udev_rules *rules)
152 {
153         struct udev_device *dev = event->dev;
154         int err = 0;
155
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         }
161
162         /* add device node */
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;
167
168                 dbg(event->udev, "device node add '%s'\n", udev_device_get_devpath(dev));
169
170                 udev_rules_get_name(rules, event);
171                 if (event->ignore_device) {
172                         info(event->udev, "device event will be ignored\n");
173                         goto exit;
174                 }
175
176                 if (event->name[0] == '\0') {
177                         info(event->udev, "device node creation supressed\n");
178                         goto exit;
179                 }
180
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
187                 /* read current database entry; cleanup, if it is known device */
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);
194                 }
195
196                 udev_device_update_db(dev);
197
198                 err = udev_node_add(dev, event->mode, event->owner, event->group, event->test);
199                 if (err != 0)
200                         goto exit;
201
202                 goto exit;
203         }
204
205         /* add netif */
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");
212                         goto exit;
213                 }
214                 if (event->name[0] == '\0') {
215                         info(event->udev, "device renaming supressed\n");
216                         goto exit;
217                 }
218
219                 /* look if we want to change the name of the netif */
220                 if (strcmp(event->name, udev_device_get_sysname(dev)) != 0) {
221                         char syspath[UTIL_PATH_SIZE];
222                         char *pos;
223
224                         err = rename_netif(event);
225                         if (err != 0)
226                                 goto exit;
227                         info(event->udev, "renamed netif to '%s'\n", event->name);
228
229                         /* remember old name */
230                         udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev));
231
232                         /* now change the devpath, because the kernel device name has changed */
233                         util_strlcpy(syspath, udev_device_get_syspath(dev), sizeof(syspath));
234                         pos = strrchr(syspath, '/');
235                         if (pos != NULL) {
236                                 pos[1] = '\0';
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));
241                         }
242                 }
243                 goto exit;
244         }
245
246         /* remove device node */
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);
262                 }
263
264                 udev_rules_get_run(rules, event);
265                 if (event->ignore_device) {
266                         info(event->udev, "device event will be ignored\n");
267                         goto exit;
268                 }
269
270                 if (udev_device_get_ignore_remove(dev)) {
271                         info(event->udev, "ignore_remove for '%s'\n", udev_device_get_devnode(dev));
272                         goto exit;
273                 }
274
275                 err = udev_node_remove(dev, event->test);
276                 goto exit;
277         }
278
279         /* default devices */
280         udev_rules_get_run(rules, event);
281         if (event->ignore_device)
282                 info(event->udev, "device event will be ignored\n");
283 exit:
284         return err;
285 }