chiark / gitweb /
libudev: get rid of udev_sysfs.c
[elogind.git] / udev / udev_device_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 #include "udev_rules.h"
33
34
35 static void kernel_log(struct ifreq ifr)
36 {
37         int klog;
38         FILE *f;
39
40         klog = open("/dev/kmsg", O_WRONLY);
41         if (klog < 0)
42                 return;
43
44         f = fdopen(klog, "w");
45         if (f == NULL) {
46                 close(klog);
47                 return;
48         }
49
50         fprintf(f, "<6>udev: renamed network interface %s to %s\n",
51                 ifr.ifr_name, ifr.ifr_newname);
52         fclose(f);
53 }
54
55 static int rename_netif(struct udevice *udevice)
56 {
57         int sk;
58         struct ifreq ifr;
59         int retval;
60
61         info(udevice->udev, "changing net interface name from '%s' to '%s'\n", udevice->dev->kernel, udevice->name);
62         if (udevice->test_run)
63                 return 0;
64
65         sk = socket(PF_INET, SOCK_DGRAM, 0);
66         if (sk < 0) {
67                 err(udevice->udev, "error opening socket: %s\n", strerror(errno));
68                 return -1;
69         }
70
71         memset(&ifr, 0x00, sizeof(struct ifreq));
72         strlcpy(ifr.ifr_name, udevice->dev->kernel, IFNAMSIZ);
73         strlcpy(ifr.ifr_newname, udevice->name, IFNAMSIZ);
74         retval = ioctl(sk, SIOCSIFNAME, &ifr);
75         if (retval == 0)
76                 kernel_log(ifr);
77         else {
78                 int loop;
79
80                 /* see if the destination interface name already exists */
81                 if (errno != EEXIST) {
82                         err(udevice->udev, "error changing netif name %s to %s: %s\n", ifr.ifr_name, ifr.ifr_newname, strerror(errno));
83                         goto exit;
84                 }
85
86                 /* free our own name, another process may wait for us */
87                 strlcpy(ifr.ifr_newname, udevice->dev->kernel, IFNAMSIZ);
88                 strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ);
89                 retval = ioctl(sk, SIOCSIFNAME, &ifr);
90                 if (retval != 0) {
91                         err(udevice->udev, "error changing netif name %s to %s: %s\n", ifr.ifr_name, ifr.ifr_newname, strerror(errno));
92                         goto exit;
93                 }
94
95                 /* wait 30 seconds for our target to become available */
96                 strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ);
97                 strlcpy(ifr.ifr_newname, udevice->name, IFNAMSIZ);
98                 loop = 30 * 20;
99                 while (loop--) {
100                         retval = ioctl(sk, SIOCSIFNAME, &ifr);
101                         if (retval == 0) {
102                                 kernel_log(ifr);
103                                 break;
104                         }
105
106                         if (errno != EEXIST) {
107                                 err(udevice->udev, "error changing net interface name %s to %s: %s\n",
108                                     ifr.ifr_name, ifr.ifr_newname, strerror(errno));
109                                 break;
110                         }
111                         dbg(udevice->udev, "wait for netif '%s' to become free, loop=%i\n", udevice->name, (30 * 20) - loop);
112                         usleep(1000 * 1000 / 20);
113                 }
114         }
115
116 exit:
117         close(sk);
118         return retval;
119 }
120
121 int udev_device_event(struct udev_rules *rules, struct udevice *udevice)
122 {
123         int retval = 0;
124
125         if (udevice->devpath_old != NULL)
126                 if (udev_db_rename(udevice->udev, udevice->devpath_old, udevice->dev->devpath) == 0)
127                         info(udevice->udev, "moved database from '%s' to '%s'\n", udevice->devpath_old, udevice->dev->devpath);
128
129         /* add device node */
130         if (major(udevice->devt) != 0 &&
131             (strcmp(udevice->action, "add") == 0 || strcmp(udevice->action, "change") == 0)) {
132                 struct udevice *udevice_old;
133
134                 dbg(udevice->udev, "device node add '%s'\n", udevice->dev->devpath);
135
136                 udev_rules_get_name(rules, udevice);
137                 if (udevice->ignore_device) {
138                         info(udevice->udev, "device event will be ignored\n");
139                         goto exit;
140                 }
141                 if (udevice->name[0] == '\0') {
142                         info(udevice->udev, "device node creation supressed\n");
143                         goto exit;
144                 }
145
146                 /* read current database entry; cleanup, if it is known device */
147                 udevice_old = udev_device_init(udevice->udev);
148                 if (udevice_old != NULL) {
149                         udevice_old->test_run = udevice->test_run;
150                         if (udev_db_get_device(udevice_old, udevice->dev->devpath) == 0) {
151                                 info(udevice->udev, "device '%s' already in database, cleanup\n", udevice->dev->devpath);
152                                 udev_db_delete_device(udevice_old);
153                         } else {
154                                 udev_device_cleanup(udevice_old);
155                                 udevice_old = NULL;
156                         }
157                 }
158
159                 /* create node */
160                 retval = udev_node_add(udevice);
161                 if (retval != 0)
162                         goto exit;
163
164                 /* store in database */
165                 udev_db_add_device(udevice);
166
167                 /* create, replace, delete symlinks according to priority */
168                 udev_node_update_symlinks(udevice, udevice_old);
169
170                 if (udevice_old != NULL)
171                         udev_device_cleanup(udevice_old);
172                 goto exit;
173         }
174
175         /* add netif */
176         if (strcmp(udevice->dev->subsystem, "net") == 0 && strcmp(udevice->action, "add") == 0) {
177                 dbg(udevice->udev, "netif add '%s'\n", udevice->dev->devpath);
178                 udev_rules_get_name(rules, udevice);
179                 if (udevice->ignore_device) {
180                         info(udevice->udev, "device event will be ignored\n");
181                         goto exit;
182                 }
183                 if (udevice->name[0] == '\0') {
184                         info(udevice->udev, "device renaming supressed\n");
185                         goto exit;
186                 }
187
188                 /* look if we want to change the name of the netif */
189                 if (strcmp(udevice->name, udevice->dev->kernel) != 0) {
190                         char devpath[PATH_MAX];
191                         char *pos;
192
193                         retval = rename_netif(udevice);
194                         if (retval != 0)
195                                 goto exit;
196                         info(udevice->udev, "renamed netif to '%s'\n", udevice->name);
197
198                         /* export old name */
199                         setenv("INTERFACE_OLD", udevice->dev->kernel, 1);
200
201                         /* now change the devpath, because the kernel device name has changed */
202                         strlcpy(devpath, udevice->dev->devpath, sizeof(devpath));
203                         pos = strrchr(devpath, '/');
204                         if (pos != NULL) {
205                                 pos[1] = '\0';
206                                 strlcat(devpath, udevice->name, sizeof(devpath));
207                                 sysfs_device_set_values(udevice->udev, udevice->dev, devpath, NULL, NULL);
208                                 setenv("DEVPATH", udevice->dev->devpath, 1);
209                                 setenv("INTERFACE", udevice->name, 1);
210                                 info(udevice->udev, "changed devpath to '%s'\n", udevice->dev->devpath);
211                         }
212                 }
213                 goto exit;
214         }
215
216         /* remove device node */
217         if (major(udevice->devt) != 0 && strcmp(udevice->action, "remove") == 0) {
218                 struct name_entry *name_loop;
219
220                 /* import database entry, and delete it */
221                 if (udev_db_get_device(udevice, udevice->dev->devpath) == 0) {
222                         udev_db_delete_device(udevice);
223                         /* restore stored persistent data */
224                         list_for_each_entry(name_loop, &udevice->env_list, node)
225                                 putenv(name_loop->name);
226                 } else {
227                         dbg(udevice->udev, "'%s' not found in database, using kernel name '%s'\n",
228                             udevice->dev->devpath, udevice->dev->kernel);
229                         strlcpy(udevice->name, udevice->dev->kernel, sizeof(udevice->name));
230                 }
231
232                 udev_rules_get_run(rules, udevice);
233                 if (udevice->ignore_device) {
234                         info(udevice->udev, "device event will be ignored\n");
235                         goto exit;
236                 }
237
238                 if (udevice->ignore_remove) {
239                         info(udevice->udev, "ignore_remove for '%s'\n", udevice->name);
240                         goto exit;
241                 }
242                 /* remove the node */
243                 retval = udev_node_remove(udevice);
244
245                 /* delete or restore symlinks according to priority */
246                 udev_node_update_symlinks(udevice, NULL);
247                 goto exit;
248         }
249
250         /* default devices */
251         udev_rules_get_run(rules, udevice);
252         if (udevice->ignore_device)
253                 info(udevice->udev, "device event will be ignored\n");
254
255 exit:
256         return retval;
257 }