chiark / gitweb /
[PATCH] Clean up the namedev interface a bit, making the code smaller...
[elogind.git] / udev.c
1 /*
2  * udev.c
3  *
4  * Userspace devfs
5  *
6  * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
7  *
8  *
9  *      This program is free software; you can redistribute it and/or modify it
10  *      under the terms of the GNU General Public License as published by the
11  *      Free Software Foundation version 2 of the License.
12  * 
13  *      This program is distributed in the hope that it will be useful, but
14  *      WITHOUT ANY WARRANTY; without even the implied warranty of
15  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *      General Public License for more details.
17  * 
18  *      You should have received a copy of the GNU General Public License along
19  *      with this program; if not, write to the Free Software Foundation, Inc.,
20  *      675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <errno.h>
30
31 #include "udev.h"
32 #include "udev_version.h"
33 #include "namedev.h"
34 #include "libsysfs/libsysfs.h"
35
36
37 static char sysfs_path[SYSFS_PATH_MAX];
38
39 static char *get_action(void)
40 {
41         char *action;
42
43         action = getenv("ACTION");
44         return action;
45 }
46
47
48 static char *get_device(void)
49 {
50         char *device;
51
52         device = getenv("DEVPATH");
53         return device;
54 }
55
56 /* 
57  * Right now the major/minor of a device is stored in a file called
58  * "dev" in sysfs.
59  * The number is stored as:
60  *      MMmm
61  *              MM is the major
62  *              mm is the minor
63  *              The value is in hex.
64  * Yes, this will probably change when we go to a bigger major/minor
65  * range, and will have to be changed at that time.
66  */
67 static int get_major_minor(struct sysfs_class_device *class_dev, int *major, int *minor)
68 {
69         char temp[3];
70         int retval = 0;
71
72         char *dev;
73
74         dev = sysfs_get_value_from_attributes(class_dev->directory->attributes, "dev");
75         if (dev == NULL)
76                 return -ENODEV;
77
78         dbg("dev = %s", dev);
79
80         temp[0] = dev[0];
81         temp[1] = dev[1];
82         temp[2] = 0x00;
83         *major = (int)strtol(&temp[0], NULL, 16);
84
85         temp[0] = dev[2];
86         temp[1] = dev[3];
87         temp[2] = 0x00;
88         *minor = (int)strtol(&temp[0], NULL, 16);
89
90         dbg("found major = %d, minor = %d", *major, *minor);
91
92         retval = 0;
93         return retval;
94 }
95
96 /*
97  * Here would go a call to the naming deamon, to get the name we want to have
98  * for this device.  But for now, let's just default to whatever the kernel is
99  * calling the device as that will keep the "old-style" naming policy
100  */
101 static char *get_name(char *dev, int major, int minor)
102 {
103         static char name[100];
104         char *temp;
105
106         temp = strrchr(dev, '/');
107         if (temp == NULL)
108                 return NULL;
109         strncpy(name, &temp[1], sizeof(name));
110
111         dbg("name is %s", name);
112
113         return &name[0];
114 }
115
116 /*
117  * We also want to add some permissions here, and possibly some symlinks
118  */
119 static int create_node(char *name, char type, int major, int minor, int mode)
120 {
121         char *argv[7];
122         char mode_string[100];
123         char type_string[3];
124         char major_string[20];
125         char minor_string[20];
126         char filename[255];
127         int retval = 0;
128
129         strncpy(filename, UDEV_ROOT, sizeof(filename));
130         strncat(filename, name, sizeof(filename));
131
132         snprintf(mode_string, sizeof(mode_string), "--mode=%#o", mode);
133         snprintf(type_string, sizeof(type_string), "%c", type);
134         snprintf(major_string, sizeof(major_string), "%d", major);
135         snprintf(minor_string, sizeof(minor_string), "%d", minor);
136         
137         argv[0] = MKNOD;
138         argv[1] = mode_string;
139         argv[2] = filename;
140         argv[3] = type_string;
141         argv[4] = major_string;
142         argv[5] = minor_string;
143         argv[6] = NULL;
144         dbg ("executing %s %s %s %s %s %s",
145                 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
146         switch (fork()) {
147                 case 0:
148                         /* we are the child, so lets run the program */
149                         execv (MKNOD, argv);
150                         exit(0);
151                         break;
152                 case (-1):
153                         dbg ("fork failed.");
154                         retval = -EFAULT;
155                         break;
156                 default:
157                         break;
158         }
159         return retval;
160 }
161
162 /*
163  * We also want to clean up any symlinks that were created in create_node()
164  */
165 static int delete_node(char *name)
166 {
167         char filename[255];
168
169         strncpy(filename, UDEV_ROOT, sizeof(filename));
170         strncat(filename, name, sizeof(filename));
171
172         dbg("unlinking %s", filename);
173         return unlink(filename);
174 }
175
176 struct sysfs_class_device *get_class_dev(char *device_name)
177 {
178         char dev_path[SYSFS_PATH_MAX];
179         struct sysfs_class_device *class_dev;
180
181         strcpy(dev_path, sysfs_path);
182         strcat(dev_path, device_name);
183
184         dbg("looking at %s", dev_path);
185
186         /* open up the sysfs class device for this thing... */
187         class_dev = sysfs_open_class_device(dev_path);
188         if (class_dev == NULL) {
189                 dbg ("sysfs_open_class_device failed");
190                 return NULL;
191         }
192         dbg("class_dev->name = %s", class_dev->name);
193
194         return class_dev;
195 }
196
197 static int add_device(char *device, char *subsystem)
198 {
199         struct sysfs_class_device *class_dev;
200         struct device_attr attr;
201         //char *name;
202         int major;
203         int minor;
204         char type;
205         //int mode;
206         int retval = -EINVAL;
207
208         /* for now, the block layer is the only place where block devices are */
209         if (strcmp(subsystem, "block") == 0)
210                 type = 'b';
211         else
212                 type = 'c';
213
214         class_dev = get_class_dev(device);
215         if (class_dev == NULL)
216                 goto exit;
217
218         retval = namedev_name_device(class_dev, &attr);
219         if (retval)
220                 return retval;
221
222         retval = get_major_minor(class_dev, &major, &minor);
223         if (retval) {
224                 dbg ("get_major_minor failed");
225                 goto exit;
226         }
227
228         sysfs_close_class_device(class_dev);
229
230         return create_node(attr.name, type, major, minor, attr.mode);
231
232 exit:
233         return retval;
234 }
235
236 static int remove_device(char *device)
237 {
238         char *name;
239         int retval = 0;
240
241         name = get_name(device, 0, 0);
242         if (name == NULL) {
243                 dbg ("get_name failed");
244                 retval = -ENODEV;
245                 goto exit;
246         }
247
248         return delete_node(name);
249
250 exit:
251         return retval;
252 }
253         
254 static int udev_init(void)
255 {
256         int retval;
257
258         retval = sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX);
259         dbg("sysfs_path = %s", sysfs_path);
260         return retval;
261 }
262
263 int main(int argc, char *argv[])
264 {
265         char *action;
266         char *device;
267         int retval = -EINVAL;
268         
269         if (argc != 2) {
270                 dbg ("unknown number of arguments");
271                 goto exit;
272         }
273
274         device = get_device();
275         if (!device) {
276                 dbg ("no device?");
277                 goto exit;
278         }
279         dbg("looking at %s", device);
280
281         /* we only care about class devices and block stuff */
282         if (!strstr(device, "class") &&
283             !strstr(device, "block")) {
284                 dbg("not block or class");
285                 goto exit;
286         }
287         
288         /* sleep for a second or two to give the kernel a chance to
289          * create the dev file
290          */
291         sleep(1);
292
293         udev_init();
294         namedev_init();
295
296         action = get_action();
297         if (!action) {
298                 dbg ("no action?");
299                 goto exit;
300         }
301
302         if (strcmp(action, "add") == 0)
303                 return add_device(device, argv[1]);
304
305         if (strcmp(action, "remove") == 0)
306                 return remove_device(device);
307
308         dbg("Unknown action: %s", action);
309         return -EINVAL;
310
311         retval = 0;
312 exit:   
313         return retval;
314 }
315