chiark / gitweb /
path_id: fix loop for SAS devices
[elogind.git] / udev_device.c
1 /*
2  * udev_device.c - main udev data object
3  *
4  * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  *      This program is free software; you can redistribute it and/or modify it
7  *      under the terms of the GNU General Public License as published by the
8  *      Free Software Foundation version 2 of the License.
9  * 
10  *      This program is distributed in the hope that it will be useful, but
11  *      WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *      General Public License for more details.
14  * 
15  *      You should have received a copy of the GNU General Public License along
16  *      with this program; if not, write to the Free Software Foundation, Inc.,
17  *      675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  */
20
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <stropts.h>
30 #include <net/if.h>
31 #include <linux/sockios.h>
32
33 #include "udev.h"
34 #include "udev_rules.h"
35
36
37 struct udevice *udev_device_init(void)
38 {
39         struct udevice *udev;
40
41         udev = malloc(sizeof(struct udevice));
42         if (udev == NULL)
43                 return NULL;
44         memset(udev, 0x00, sizeof(struct udevice));
45
46         INIT_LIST_HEAD(&udev->symlink_list);
47         INIT_LIST_HEAD(&udev->run_list);
48         INIT_LIST_HEAD(&udev->env_list);
49
50         /* set sysfs device to local storage, can be overridden if needed */
51         udev->dev = &udev->dev_local;
52
53         /* default node permissions */
54         udev->mode = 0660;
55         strcpy(udev->owner, "root");
56         strcpy(udev->group, "root");
57
58         return udev;
59 }
60
61 void udev_device_cleanup(struct udevice *udev)
62 {
63         name_list_cleanup(&udev->symlink_list);
64         name_list_cleanup(&udev->run_list);
65         name_list_cleanup(&udev->env_list);
66         free(udev);
67 }
68
69 dev_t udev_device_get_devt(struct udevice *udev)
70 {
71         const char *attr;
72         unsigned int major, minor;
73
74         /* read it from sysfs  */
75         attr = sysfs_attr_get_value(udev->dev->devpath, "dev");
76         if (attr != NULL) {
77                 if (sscanf(attr, "%u:%u", &major, &minor) == 2)
78                         return makedev(major, minor);
79         }
80         return makedev(0, 0);
81 }
82
83 static int rename_net_if(struct udevice *udev)
84 {
85         int sk;
86         struct ifreq ifr;
87         int retval;
88
89         info("changing net interface name from '%s' to '%s'", udev->dev->kernel_name, udev->name);
90         if (udev->test_run)
91                 return 0;
92
93         sk = socket(PF_INET, SOCK_DGRAM, 0);
94         if (sk < 0) {
95                 err("error opening socket: %s", strerror(errno));
96                 return -1;
97         }
98
99         memset(&ifr, 0x00, sizeof(struct ifreq));
100         strlcpy(ifr.ifr_name, udev->dev->kernel_name, IFNAMSIZ);
101         strlcpy(ifr.ifr_newname, udev->name, IFNAMSIZ);
102
103         retval = ioctl(sk, SIOCSIFNAME, &ifr);
104         if (retval != 0)
105                 err("error changing net interface name: %s", strerror(errno));
106         close(sk);
107
108         return retval;
109 }
110
111 int udev_device_event(struct udev_rules *rules, struct udevice *udev)
112 {
113         int retval = 0;
114
115         /* add device node */
116         if (major(udev->devt) != 0 && strcmp(udev->action, "add") == 0) {
117                 struct udevice *udev_old;
118
119                 dbg("device node add '%s'", udev->dev->devpath);
120
121                 udev_rules_get_name(rules, udev);
122                 if (udev->ignore_device) {
123                         info("device event will be ignored");
124                         goto exit;
125                 }
126                 if (udev->name[0] == '\0') {
127                         info("device node creation supressed");
128                         goto exit;
129                 }
130
131                 /* read current database entry, we may want to cleanup symlinks */
132                 udev_old = udev_device_init();
133                 if (udev_old != NULL) {
134                         if (udev_db_get_device(udev_old, udev->dev->devpath) == 0) {
135                                 info("device '%s' already known, remove possible symlinks", udev->dev->devpath);
136                                 udev_node_remove_symlinks(udev_old);
137                         }
138                         udev_device_cleanup(udev_old);
139                 }
140
141                 /* create node and symlinks, store record in database */
142                 retval = udev_node_add(udev, udev_old);
143                 if (retval == 0)
144                         udev_db_add_device(udev);
145                 goto exit;
146         }
147
148         /* add netif */
149         if (strcmp(udev->dev->subsystem, "net") == 0 && strcmp(udev->action, "add") == 0) {
150                 dbg("netif add '%s'", udev->dev->devpath);
151                 udev_rules_get_name(rules, udev);
152                 if (udev->ignore_device) {
153                         info("device event will be ignored");
154                         goto exit;
155                 }
156
157                 /* look if we want to change the name of the netif */
158                 if (strcmp(udev->name, udev->dev->kernel_name) != 0) {
159                         char *pos;
160
161                         retval = rename_net_if(udev);
162                         if (retval != 0)
163                                 goto exit;
164                         info("renamed netif to '%s'", udev->name);
165
166                         /* export old name */
167                         setenv("INTERFACE_OLD", udev->dev->kernel_name, 1);
168
169                         /* now fake the devpath, because the kernel name changed silently */
170                         pos = strrchr(udev->dev->devpath, '/');
171                         if (pos != NULL) {
172                                 pos[1] = '\0';
173                                 strlcat(udev->dev->devpath, udev->name, sizeof(udev->dev->devpath));
174                                 strlcpy(udev->dev->kernel_name, udev->name, sizeof(udev->dev->kernel_name));
175                                 setenv("DEVPATH", udev->dev->devpath, 1);
176                                 setenv("INTERFACE", udev->name, 1);
177                         }
178                 }
179                 goto exit;
180         }
181
182         /* remove device node */
183         if (major(udev->devt) != 0 && strcmp(udev->action, "remove") == 0) {
184                 struct name_entry *name_loop;
185
186                 /* import and delete database entry */
187                 if (udev_db_get_device(udev, udev->dev->devpath) == 0) {
188                         udev_db_delete_device(udev);
189                         if (udev->ignore_remove) {
190                                 dbg("remove event for '%s' requested to be ignored by rule", udev->name);
191                                 return 0;
192                         }
193                         /* restore stored persistent data */
194                         list_for_each_entry(name_loop, &udev->env_list, node)
195                                 putenv(name_loop->name);
196                 } else {
197                         dbg("'%s' not found in database, using kernel name '%s'", udev->dev->devpath, udev->dev->kernel_name);
198                         strlcpy(udev->name, udev->dev->kernel_name, sizeof(udev->name));
199                 }
200
201                 udev_rules_get_run(rules, udev);
202                 if (udev->ignore_device) {
203                         info("device event will be ignored");
204                         goto exit;
205                 }
206
207                 retval = udev_node_remove(udev);
208                 goto exit;
209         }
210
211         /* default devices */
212         udev_rules_get_run(rules, udev);
213         if (udev->ignore_device)
214                 info("device event will be ignored");
215
216 exit:
217         return retval;
218 }