chiark / gitweb /
b8a21a25ae5f48bb4b866f5316901a86c209920a
[elogind.git] / namedev.c
1 /*
2  * namedev.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 <stddef.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <errno.h>
32
33 #include "list.h"
34 #include "udev.h"
35 #include "udev_version.h"
36 #include "namedev.h"
37 #include "libsysfs/libsysfs.h"
38
39 #define TYPE_LABEL      "LABEL"
40 #define TYPE_NUMBER     "NUMBER"
41 #define TYPE_TOPOLOGY   "TOPOLOGY"
42 #define TYPE_REPLACE    "REPLACE"
43
44 enum config_type {
45         KERNEL_NAME     = 0,    /* must be 0 to let memset() default to this value */
46         LABEL           = 1,
47         NUMBER          = 2,
48         TOPOLOGY        = 3,
49         REPLACE         = 4,
50 };
51
52 #define BUS_SIZE        30
53 #define FILE_SIZE       50
54 #define VALUE_SIZE      100
55 #define ID_SIZE         50
56 #define PLACE_SIZE      50
57
58
59 struct config_device {
60         struct list_head node;
61
62         enum config_type type;
63
64         char bus[BUS_SIZE];
65         char sysfs_file[FILE_SIZE];
66         char sysfs_value[VALUE_SIZE];
67         char id[ID_SIZE];
68         char place[PLACE_SIZE];
69         char kernel_name[NAME_SIZE];
70         
71         /* what to set the device to */
72         int mode;
73         char name[NAME_SIZE];
74         char owner[OWNER_SIZE];
75         char group[GROUP_SIZE];
76 };
77
78
79 static LIST_HEAD(config_device_list);
80
81 static char sysfs_path[SYSFS_PATH_MAX];
82
83 static void dump_dev(struct config_device *dev)
84 {
85         switch (dev->type) {
86         case KERNEL_NAME:
87                 dbg("KERNEL name ='%s'"
88                         " owner = '%s', group = '%s', mode = '%#o'",
89                         dev->name, 
90                         dev->owner, dev->group, dev->mode);
91                 break;
92         case LABEL:
93                 dbg("LABEL name = '%s', bus = '%s', sysfs_file = '%s', sysfs_value = '%s'"
94                         " owner = '%s', group = '%s', mode = '%#o'",
95                         dev->name, dev->bus, dev->sysfs_file, dev->sysfs_value,
96                         dev->owner, dev->group, dev->mode);
97                 break;
98         case NUMBER:
99                 dbg("NUMBER name = '%s', bus = '%s', id = '%s'"
100                         " owner = '%s', group = '%s', mode = '%#o'",
101                         dev->name, dev->bus, dev->id,
102                         dev->owner, dev->group, dev->mode);
103                 break;
104         case TOPOLOGY:
105                 dbg("TOPOLOGY name = '%s', bus = '%s', place = '%s'"
106                         " owner = '%s', group = '%s', mode = '%#o'",
107                         dev->name, dev->bus, dev->place,
108                         dev->owner, dev->group, dev->mode);
109                 break;
110         case REPLACE:
111                 dbg("REPLACE name = %s, kernel_name = %s"
112                         " owner = '%s', group = '%s', mode = '%#o'",
113                         dev->name, dev->kernel_name,
114                         dev->owner, dev->group, dev->mode);
115                 break;
116         default:
117                 dbg("Unknown type of device!");
118         }
119 }
120
121 #define copy_var(a, b, var)             \
122         if (b->var)                     \
123                 a->var = b->var;
124
125 #define copy_string(a, b, var)          \
126         if (strlen(b->var))             \
127                 strcpy(a->var, b->var);
128
129 static int add_dev(struct config_device *new_dev)
130 {
131         struct list_head *tmp;
132         struct config_device *tmp_dev;
133
134         /* loop through the whole list of devices to see if we already have
135          * this one... */
136         list_for_each(tmp, &config_device_list) {
137                 struct config_device *dev = list_entry(tmp, struct config_device, node);
138                 if (strcmp(dev->name, new_dev->name) == 0) {
139                         /* the same, copy the new info into this structure */
140                         copy_var(dev, new_dev, type);
141                         copy_var(dev, new_dev, mode);
142                         copy_string(dev, new_dev, bus);
143                         copy_string(dev, new_dev, sysfs_file);
144                         copy_string(dev, new_dev, sysfs_value);
145                         copy_string(dev, new_dev, id);
146                         copy_string(dev, new_dev, place);
147                         copy_string(dev, new_dev, kernel_name);
148                         copy_string(dev, new_dev, owner);
149                         copy_string(dev, new_dev, group);
150                         return 0;
151                 }
152         }
153
154         /* not found, lets create a new structure, and add it to the list */
155         tmp_dev = malloc(sizeof(*tmp_dev));
156         if (!tmp_dev)
157                 return -ENOMEM;
158         memcpy(tmp_dev, new_dev, sizeof(*tmp_dev));
159         list_add(&tmp_dev->node, &config_device_list);
160         //dump_dev(tmp_dev);
161         return 0;
162 }
163
164 static void dump_dev_list(void)
165 {
166         struct list_head *tmp;
167
168         list_for_each(tmp, &config_device_list) {
169                 struct config_device *dev = list_entry(tmp, struct config_device, node);
170                 dump_dev(dev);
171         }
172 }
173
174 static int get_value(const char *left, char **orig_string, char **ret_string)
175 {
176         char *temp;
177         char *string = *orig_string;
178
179         /* eat any whitespace */
180         while (isspace(*string))
181                 ++string;
182
183         /* split based on '=' */
184         temp = strsep(&string, "=");
185         if (strcasecmp(temp, left) == 0) {
186                 /* got it, now strip off the '"' */
187                 while (isspace(*string))
188                         ++string;
189                 if (*string == '"')
190                         ++string;
191                 temp = strsep(&string, "\"");
192                 *ret_string = temp;
193                 *orig_string = string;
194                 return 0;
195         }
196         return -ENODEV;
197 }
198         
199 static int get_pair(char **orig_string, char **left, char **right)
200 {
201         char *temp;
202         char *string = *orig_string;
203
204         /* eat any whitespace */
205         while (isspace(*string))
206                 ++string;
207
208         /* split based on '=' */
209         temp = strsep(&string, "=");
210         *left = temp;
211
212         /* take the right side and strip off the '"' */
213         while (isspace(*string))
214                 ++string;
215         if (*string == '"')
216                 ++string;
217         temp = strsep(&string, "\"");
218         *right = temp;
219         *orig_string = string;
220         
221         return 0;
222 }
223
224 static int namedev_init_config(void)
225 {
226         char filename[255];
227         char line[255];
228         char *temp;
229         char *temp2;
230         char *temp3;
231         FILE *fd;
232         int retval = 0;
233         struct config_device dev;
234
235         strcpy(filename, NAMEDEV_CONFIG_ROOT NAMEDEV_CONFIG_FILE);
236         dbg("opening %s to read as permissions config", filename);
237         fd = fopen(filename, "r");
238         if (fd == NULL) {
239                 dbg("Can't open %s", filename);
240                 return -ENODEV;
241         }
242
243         /* loop through the whole file */
244         while (1) {
245                 /* get a line */
246                 temp = fgets(line, sizeof(line), fd);
247                 if (temp == NULL)
248                         break;
249
250                 dbg("read %s", temp);
251
252                 /* eat the whitespace at the beginning of the line */
253                 while (isspace(*temp))
254                         ++temp;
255
256                 /* no more line? */
257                 if (*temp == 0x00)
258                         continue;
259
260                 /* see if this is a comment */
261                 if (*temp == COMMENT_CHARACTER)
262                         continue;
263
264                 memset(&dev, 0x00, sizeof(struct config_device));
265
266                 /* parse the line */
267                 temp2 = strsep(&temp, ",");
268                 if (strcasecmp(temp2, TYPE_LABEL) == 0) {
269                         /* label type */
270                         dev.type = LABEL;
271
272                         /* BUS="bus" */
273                         retval = get_value("BUS", &temp, &temp3);
274                         if (retval)
275                                 continue;
276                         strcpy(dev.bus, temp3);
277
278                         /* file="value" */
279                         temp2 = strsep(&temp, ",");
280                         retval = get_pair(&temp, &temp2, &temp3);
281                         if (retval)
282                                 continue;
283                         strcpy(dev.sysfs_file, temp2);
284                         strcpy(dev.sysfs_value, temp3);
285
286                         /* NAME="new_name" */
287                         temp2 = strsep(&temp, ",");
288                         retval = get_value("NAME", &temp, &temp3);
289                         if (retval)
290                                 continue;
291                         strcpy(dev.name, temp3);
292
293                         dbg("LABEL name = '%s', bus = '%s', sysfs_file = '%s', sysfs_value = '%s'", dev.name, dev.bus, dev.sysfs_file, dev.sysfs_value);
294                 }
295
296                 if (strcasecmp(temp2, TYPE_NUMBER) == 0) {
297                         /* number type */
298                         dev.type = NUMBER;
299
300                         /* BUS="bus" */
301                         retval = get_value("BUS", &temp, &temp3);
302                         if (retval)
303                                 continue;
304                         strcpy(dev.bus, temp3);
305
306                         /* ID="id" */
307                         temp2 = strsep(&temp, ",");
308                         retval = get_value("id", &temp, &temp3);
309                         if (retval)
310                                 continue;
311                         strcpy(dev.id, temp3);
312
313                         /* NAME="new_name" */
314                         temp2 = strsep(&temp, ",");
315                         retval = get_value("NAME", &temp, &temp3);
316                         if (retval)
317                                 continue;
318                         strcpy(dev.name, temp3);
319
320                         dbg("NUMBER name = '%s', bus = '%s', id = '%s'", dev.name, dev.bus, dev.id);
321                 }
322
323                 if (strcasecmp(temp2, TYPE_TOPOLOGY) == 0) {
324                         /* number type */
325                         dev.type = TOPOLOGY;
326
327                         /* BUS="bus" */
328                         retval = get_value("BUS", &temp, &temp3);
329                         if (retval)
330                                 continue;
331                         strcpy(dev.bus, temp3);
332
333                         /* PLACE="place" */
334                         temp2 = strsep(&temp, ",");
335                         retval = get_value("place", &temp, &temp3);
336                         if (retval)
337                                 continue;
338                         strcpy(dev.place, temp3);
339
340                         /* NAME="new_name" */
341                         temp2 = strsep(&temp, ",");
342                         retval = get_value("NAME", &temp, &temp3);
343                         if (retval)
344                                 continue;
345                         strcpy(dev.name, temp3);
346
347                         dbg("TOPOLOGY name = '%s', bus = '%s', place = '%s'", dev.name, dev.bus, dev.place);
348                 }
349
350                 if (strcasecmp(temp2, TYPE_REPLACE) == 0) {
351                         /* number type */
352                         dev.type = REPLACE;
353
354                         /* KERNEL="kernel_name" */
355                         retval = get_value("KERNEL", &temp, &temp3);
356                         if (retval)
357                                 continue;
358                         strcpy(dev.kernel_name, temp3);
359
360                         /* NAME="new_name" */
361                         temp2 = strsep(&temp, ",");
362                         retval = get_value("NAME", &temp, &temp3);
363                         if (retval)
364                                 continue;
365                         strcpy(dev.name, temp3);
366                         dbg("REPLACE name = %s, kernel_name = %s", dev.name, dev.kernel_name);
367                 }
368
369                 retval = add_dev(&dev);
370                 if (retval) {
371                         dbg("add_dev returned with error %d", retval);
372                         goto exit;
373                 }
374         }
375
376 exit:
377         fclose(fd);
378         return retval;
379 }       
380
381
382 static int namedev_init_permissions(void)
383 {
384         char filename[255];
385         char line[255];
386         char *temp;
387         char *temp2;
388         FILE *fd;
389         int retval = 0;
390         struct config_device dev;
391
392         strcpy(filename, NAMEDEV_CONFIG_ROOT NAMEDEV_CONFIG_PERMISSION_FILE);
393         dbg("opening %s to read as permissions config", filename);
394         fd = fopen(filename, "r");
395         if (fd == NULL) {
396                 dbg("Can't open %s", filename);
397                 return -ENODEV;
398         }
399
400         /* loop through the whole file */
401         while (1) {
402                 /* get a line */
403                 temp = fgets(line, sizeof(line), fd);
404                 if (temp == NULL)
405                         break;
406
407                 dbg("read %s", temp);
408
409                 /* eat the whitespace at the beginning of the line */
410                 while (isspace(*temp))
411                         ++temp;
412
413                 /* no more line? */
414                 if (*temp == 0x00)
415                         continue;
416
417                 /* see if this is a comment */
418                 if (*temp == COMMENT_CHARACTER)
419                         continue;
420
421                 memset(&dev, 0x00, sizeof(dev));
422
423                 /* parse the line */
424                 temp2 = strsep(&temp, ":");
425                 strncpy(dev.name, temp2, sizeof(dev.name));
426
427                 temp2 = strsep(&temp, ":");
428                 strncpy(dev.owner, temp2, sizeof(dev.owner));
429
430                 temp2 = strsep(&temp, ":");
431                 strncpy(dev.group, temp2, sizeof(dev.owner));
432
433                 dev.mode = strtol(temp, NULL, 8);
434
435                 dbg("name = %s, owner = %s, group = %s, mode = %#o", dev.name, dev.owner, dev.group, dev.mode);
436                 retval = add_dev(&dev);
437                 if (retval) {
438                         dbg("add_dev returned with error %d", retval);
439                         goto exit;
440                 }
441         }
442
443 exit:
444         fclose(fd);
445         return retval;
446 }       
447
448
449 static int get_major_minor(struct sysfs_class_device *class_dev, int *major, int *minor)
450 {
451         char temp[3];
452         int retval = 0;
453
454         char *dev;
455
456         dev = sysfs_get_value_from_attributes(class_dev->directory->attributes, "dev");
457         if (dev == NULL)
458                 return -ENODEV;
459
460         dbg("dev = %s", dev);
461
462         temp[0] = dev[0];
463         temp[1] = dev[1];
464         temp[2] = 0x00;
465         *major = (int)strtol(&temp[0], NULL, 16);
466
467         temp[0] = dev[2];
468         temp[1] = dev[3];
469         temp[2] = 0x00;
470         *minor = (int)strtol(&temp[0], NULL, 16);
471
472         dbg("found major = %d, minor = %d", *major, *minor);
473
474         retval = 0;
475         return retval;
476 }
477
478 static int get_attr(struct sysfs_class_device *class_dev, struct device_attr *attr)
479 {
480         struct list_head *tmp;
481         int retval = 0;
482
483         retval = get_major_minor(class_dev, &attr->major, &attr->minor);
484         if (retval) {
485                 dbg ("get_major_minor failed");
486                 goto exit;
487         }
488
489         list_for_each(tmp, &config_device_list) {
490                 struct config_device *dev = list_entry(tmp, struct config_device, node);
491                 if (strcmp(dev->name, class_dev->name) == 0) {
492                         attr->mode = dev->mode;
493                         strcpy(attr->owner, dev->owner);
494                         strcpy(attr->group, dev->group);
495                         /* FIXME  put the proper name here!!! */
496                         strcpy(attr->name, dev->name);
497                         dbg("%s - owner = %s, group = %s, mode = %#o", dev->name, dev->owner, dev->group, dev->mode);
498                         goto exit;
499                 }
500         }
501         attr->mode = 0666;
502         attr->owner[0] = 0x00;
503         attr->group[0] = 0x00;
504         strcpy(attr->name, class_dev->name);
505 exit:
506         return retval;
507 }
508
509 int namedev_name_device(char *device_name, struct device_attr *attr)
510 {
511         char dev_path[SYSFS_PATH_MAX];
512         struct sysfs_class_device *class_dev;
513         int retval;
514
515         strcpy(dev_path, sysfs_path);
516         strcat(dev_path, device_name);
517
518         dbg("looking at %s", dev_path);
519
520         /* open up the sysfs class device for this thing... */
521         class_dev = sysfs_open_class_device(dev_path);
522         if (class_dev == NULL) {
523                 dbg ("sysfs_open_class_device failed");
524                 return -ENODEV;
525         }
526         dbg("class_dev->name = %s", class_dev->name);
527         
528         retval = get_attr(class_dev, attr);
529         if (retval) {
530                 dbg ("get_attr failed");
531                 goto exit;
532         }
533 exit:
534         sysfs_close_class_device(class_dev);
535         return retval;
536 }
537
538 int namedev_init(void)
539 {
540         int retval;
541         
542         retval = sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX);
543         if (retval)
544                 return retval;
545         dbg("sysfs_path = %s", sysfs_path);
546
547         retval = namedev_init_config();
548         if (retval)
549                 return retval;
550
551         retval = namedev_init_permissions();
552         if (retval)
553                 return retval;
554
555         dump_dev_list();
556         return retval;
557 }
558
559