]> git.ipfire.org Git - thirdparty/systemd.git/blob - namedev.c
86685f8393fd05859f6e3d327ef18ebe2523b3c4
[thirdparty/systemd.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 #include <sys/wait.h>
33
34 #include "list.h"
35 #include "udev.h"
36 #include "udev_version.h"
37 #include "namedev.h"
38 #include "libsysfs/libsysfs.h"
39 #include "klibc_fixups.h"
40
41 LIST_HEAD(config_device_list);
42
43 /* s2 may end with '*' to match everything */
44 static int strncmp_wildcard(char *s1, char *s2, int max)
45 {
46 int len = strlen(s2);
47 if (len > max)
48 len = max;
49 if (s2[len-1] == '*')
50 len--;
51 else
52 len = max;
53 return strncmp(s1, s2, len);
54 }
55
56 #define copy_var(a, b, var) \
57 if (b->var) \
58 a->var = b->var;
59
60 #define copy_string(a, b, var) \
61 if (strlen(b->var)) \
62 strcpy(a->var, b->var);
63
64 int add_config_dev(struct config_device *new_dev)
65 {
66 struct list_head *tmp;
67 struct config_device *tmp_dev;
68
69 /* update the values if we already have the device */
70 list_for_each(tmp, &config_device_list) {
71 struct config_device *dev = list_entry(tmp, struct config_device, node);
72 if (strncmp_wildcard(dev->name, new_dev->name, sizeof(dev->name)))
73 continue;
74 if (strncmp(dev->bus, new_dev->bus, sizeof(dev->name)))
75 continue;
76 copy_var(dev, new_dev, type);
77 copy_var(dev, new_dev, mode);
78 copy_string(dev, new_dev, bus);
79 copy_string(dev, new_dev, sysfs_file);
80 copy_string(dev, new_dev, sysfs_value);
81 copy_string(dev, new_dev, id);
82 copy_string(dev, new_dev, place);
83 copy_string(dev, new_dev, kernel_name);
84 copy_string(dev, new_dev, exec_program);
85 copy_string(dev, new_dev, owner);
86 copy_string(dev, new_dev, group);
87 return 0;
88 }
89
90 /* not found, add new structure to the device list */
91 tmp_dev = malloc(sizeof(*tmp_dev));
92 if (!tmp_dev)
93 return -ENOMEM;
94 memcpy(tmp_dev, new_dev, sizeof(*tmp_dev));
95 list_add_tail(&tmp_dev->node, &config_device_list);
96 //dump_config_dev(tmp_dev);
97 return 0;
98 }
99
100 static mode_t get_default_mode(struct sysfs_class_device *class_dev)
101 {
102 /* just default everyone to rw for the world! */
103 return 0666;
104 }
105
106 static void build_kernel_number(struct sysfs_class_device *class_dev, struct udevice *udev)
107 {
108 char *dig;
109
110 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
111 dig = class_dev->name + strlen(class_dev->name);
112 while (isdigit(*(dig-1)))
113 dig--;
114 strfieldcpy(udev->kernel_number, dig);
115 dbg("kernel_number='%s'", udev->kernel_number);
116 }
117
118 static void apply_format(struct udevice *udev, unsigned char *string)
119 {
120 char name[NAME_SIZE];
121 char *pos;
122
123 while (1) {
124 pos = strchr(string, '%');
125
126 if (pos) {
127 strfieldcpy(name, pos+2);
128 *pos = 0x00;
129 switch (pos[1]) {
130 case 'b':
131 if (strlen(udev->bus_id) == 0)
132 break;
133 strcat(pos, udev->bus_id);
134 dbg("substitute bus_id '%s'", udev->bus_id);
135 break;
136 case 'n':
137 if (strlen(udev->kernel_number) == 0)
138 break;
139 strcat(pos, udev->kernel_number);
140 dbg("substitute kernel number '%s'", udev->kernel_number);
141 break;
142 case 'D':
143 if (strlen(udev->kernel_number) == 0) {
144 strcat(pos, "disk");
145 break;
146 }
147 strcat(pos, "part");
148 strcat(pos, udev->kernel_number);
149 dbg("substitute kernel number '%s'", udev->kernel_number);
150 break;
151 case 'm':
152 sprintf(pos, "%u", udev->minor);
153 dbg("substitute minor number '%u'", udev->minor);
154 break;
155 case 'M':
156 sprintf(pos, "%u", udev->major);
157 dbg("substitute major number '%u'", udev->major);
158 break;
159 case 'c':
160 if (strlen(udev->callout_value) == 0)
161 break;
162 strcat(pos, udev->callout_value);
163 dbg("substitute callout output '%s'", udev->callout_value);
164 break;
165 default:
166 dbg("unknown substitution type '%%%c'", pos[1]);
167 break;
168 }
169 strcat(string, name);
170 } else
171 break;
172 }
173 }
174
175
176 static int exec_callout(struct config_device *dev, char *value, int len)
177 {
178 int retval;
179 int res;
180 int status;
181 int fds[2];
182 pid_t pid;
183 int value_set = 0;
184 char buffer[256];
185 char *arg;
186 char *args[CALLOUT_MAXARG];
187 int i;
188
189 dbg("callout to '%s'", dev->exec_program);
190 retval = pipe(fds);
191 if (retval != 0) {
192 dbg("pipe failed");
193 return -1;
194 }
195 pid = fork();
196 if (pid == -1) {
197 dbg("fork failed");
198 return -1;
199 }
200
201 if (pid == 0) {
202 /* child */
203 close(STDOUT_FILENO);
204 dup(fds[1]); /* dup write side of pipe to STDOUT */
205 if (strchr(dev->exec_program, ' ')) {
206 /* callout with arguments */
207 arg = dev->exec_program;
208 for (i=0; i < CALLOUT_MAXARG-1; i++) {
209 args[i] = strsep(&arg, " ");
210 if (args[i] == NULL)
211 break;
212 }
213 if (args[i]) {
214 dbg("too many args - %d", i);
215 args[i] = NULL;
216 }
217 retval = execve(args[0], args, main_envp);
218 } else {
219 retval = execve(dev->exec_program, main_argv, main_envp);
220 }
221 if (retval != 0) {
222 dbg("child execve failed");
223 exit(1);
224 }
225 return -1; /* avoid compiler warning */
226 } else {
227 /* parent reads from fds[0] */
228 close(fds[1]);
229 retval = 0;
230 while (1) {
231 res = read(fds[0], buffer, sizeof(buffer) - 1);
232 if (res <= 0)
233 break;
234 buffer[res] = '\0';
235 if (res > len) {
236 dbg("callout len %d too short", len);
237 retval = -1;
238 }
239 if (value_set) {
240 dbg("callout value already set");
241 retval = -1;
242 } else {
243 value_set = 1;
244 strncpy(value, buffer, len);
245 }
246 }
247 dbg("callout returned '%s'", value);
248 close(fds[0]);
249 res = wait(&status);
250 if (res < 0) {
251 dbg("wait failed result %d", res);
252 retval = -1;
253 }
254
255 #ifndef __KLIBC__
256 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
257 dbg("callout program status 0x%x", status);
258 retval = -1;
259 }
260 #endif
261 }
262 return retval;
263 }
264
265 static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
266 {
267 struct config_device *dev;
268 struct list_head *tmp;
269
270 list_for_each(tmp, &config_device_list) {
271 dev = list_entry(tmp, struct config_device, node);
272 if (dev->type != CALLOUT)
273 continue;
274
275 if (sysfs_device) {
276 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
277 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
278 continue;
279 }
280
281 /* substitute anything that needs to be in the program name */
282 apply_format(udev, dev->exec_program);
283 if (exec_callout(dev, udev->callout_value, NAME_SIZE))
284 continue;
285 if (strncmp_wildcard(udev->callout_value, dev->id, NAME_SIZE) != 0)
286 continue;
287 strfieldcpy(udev->name, dev->name);
288 if (dev->mode != 0) {
289 udev->mode = dev->mode;
290 strfieldcpy(udev->owner, dev->owner);
291 strfieldcpy(udev->group, dev->group);
292 }
293 dbg("callout returned matching value '%s', '%s' becomes '%s'"
294 " - owner='%s', group='%s', mode=%#o",
295 dev->id, class_dev->name, udev->name,
296 dev->owner, dev->group, dev->mode);
297 return 0;
298 }
299 return -ENODEV;
300 }
301
302 static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
303 {
304 struct sysfs_attribute *tmpattr = NULL;
305 struct config_device *dev;
306 struct list_head *tmp;
307
308 list_for_each(tmp, &config_device_list) {
309 dev = list_entry(tmp, struct config_device, node);
310 if (dev->type != LABEL)
311 continue;
312
313 if (sysfs_device) {
314 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
315 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
316 continue;
317 }
318
319 dbg("look for device attribute '%s'", dev->sysfs_file);
320 /* try to find the attribute in the class device directory */
321 tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file);
322 if (tmpattr)
323 goto label_found;
324
325 /* look in the class device directory if present */
326 if (sysfs_device) {
327 tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file);
328 if (tmpattr)
329 goto label_found;
330 }
331
332 continue;
333
334 label_found:
335 tmpattr->value[strlen(tmpattr->value)-1] = 0x00;
336 dbg("compare attribute '%s' value '%s' with '%s'",
337 dev->sysfs_file, tmpattr->value, dev->sysfs_value);
338 if (strcmp(dev->sysfs_value, tmpattr->value) != 0)
339 continue;
340
341 strfieldcpy(udev->name, dev->name);
342 if (dev->mode != 0) {
343 udev->mode = dev->mode;
344 strfieldcpy(udev->owner, dev->owner);
345 strfieldcpy(udev->group, dev->group);
346 }
347 dbg("found matching attribute '%s', '%s' becomes '%s' "
348 "- owner='%s', group='%s', mode=%#o",
349 dev->sysfs_file, class_dev->name, udev->name,
350 dev->owner, dev->group, dev->mode);
351
352 return 0;
353 }
354 return -ENODEV;
355 }
356
357 static int do_number(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
358 {
359 struct config_device *dev;
360 struct list_head *tmp;
361 char path[SYSFS_PATH_MAX];
362 int found;
363 char *temp = NULL;
364
365 /* we have to have a sysfs device for NUMBER to work */
366 if (!sysfs_device)
367 return -ENODEV;
368
369 list_for_each(tmp, &config_device_list) {
370 dev = list_entry(tmp, struct config_device, node);
371 if (dev->type != NUMBER)
372 continue;
373
374 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
375 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
376 continue;
377
378 found = 0;
379 strfieldcpy(path, sysfs_device->path);
380 temp = strrchr(path, '/');
381 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
382 if (strstr(temp, dev->id) != NULL) {
383 found = 1;
384 } else {
385 *temp = 0x00;
386 temp = strrchr(path, '/');
387 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
388 if (strstr(temp, dev->id) != NULL)
389 found = 1;
390 }
391 if (!found)
392 continue;
393 strfieldcpy(udev->name, dev->name);
394 if (dev->mode != 0) {
395 udev->mode = dev->mode;
396 strfieldcpy(udev->owner, dev->owner);
397 strfieldcpy(udev->group, dev->group);
398 }
399 dbg("found matching id '%s', '%s' becomes '%s'"
400 " - owner='%s', group ='%s', mode=%#o",
401 dev->id, class_dev->name, udev->name,
402 dev->owner, dev->group, dev->mode);
403 return 0;
404 }
405 return -ENODEV;
406 }
407
408 static int do_topology(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
409 {
410 struct config_device *dev;
411 struct list_head *tmp;
412 char path[SYSFS_PATH_MAX];
413 int found;
414 char *temp = NULL;
415
416 /* we have to have a sysfs device for TOPOLOGY to work */
417 if (!sysfs_device)
418 return -ENODEV;
419
420 list_for_each(tmp, &config_device_list) {
421 dev = list_entry(tmp, struct config_device, node);
422 if (dev->type != TOPOLOGY)
423 continue;
424
425 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
426 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
427 continue;
428
429 found = 0;
430 strfieldcpy(path, sysfs_device->path);
431 temp = strrchr(path, '/');
432 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
433 if (strstr(temp, dev->place) != NULL) {
434 found = 1;
435 } else {
436 *temp = 0x00;
437 temp = strrchr(path, '/');
438 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
439 if (strstr(temp, dev->place) != NULL)
440 found = 1;
441 }
442 if (!found)
443 continue;
444
445 strfieldcpy(udev->name, dev->name);
446 if (dev->mode != 0) {
447 udev->mode = dev->mode;
448 strfieldcpy(udev->owner, dev->owner);
449 strfieldcpy(udev->group, dev->group);
450 }
451 dbg("found matching place '%s', '%s' becomes '%s'"
452 " - owner='%s', group ='%s', mode=%#o",
453 dev->place, class_dev->name, udev->name,
454 dev->owner, dev->group, dev->mode);
455 return 0;
456 }
457 return -ENODEV;
458 }
459
460 static int do_replace(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
461 {
462 struct config_device *dev;
463 struct list_head *tmp;
464
465 list_for_each(tmp, &config_device_list) {
466 dev = list_entry(tmp, struct config_device, node);
467 if (dev->type != REPLACE)
468 continue;
469
470 dbg("compare name '%s' with '%s'", dev->kernel_name, class_dev->name);
471 if (strncmp_wildcard(class_dev->name, dev->kernel_name, NAME_SIZE) != 0)
472 continue;
473
474 strfieldcpy(udev->name, dev->name);
475 if (dev->mode != 0) {
476 udev->mode = dev->mode;
477 strfieldcpy(udev->owner, dev->owner);
478 strfieldcpy(udev->group, dev->group);
479 }
480 dbg("found name, '%s' becomes '%s'"
481 " - owner='%s', group='%s', mode = %#o",
482 dev->kernel_name, udev->name,
483 dev->owner, dev->group, dev->mode);
484
485 return 0;
486 }
487 return -ENODEV;
488 }
489
490 static void do_kernelname(struct sysfs_class_device *class_dev, struct udevice *udev)
491 {
492 struct config_device *dev;
493 struct list_head *tmp;
494 int len;
495
496 strfieldcpy(udev->name, class_dev->name);
497 /* look for permissions */
498 list_for_each(tmp, &config_device_list) {
499 dev = list_entry(tmp, struct config_device, node);
500 len = strlen(dev->name);
501 if (strncmp_wildcard(class_dev->name, dev->name, sizeof(dev->name)))
502 continue;
503 if (dev->mode != 0) {
504 dbg("found permissions for '%s'", class_dev->name);
505 udev->mode = dev->mode;
506 strfieldcpy(udev->owner, dev->owner);
507 strfieldcpy(udev->group, dev->group);
508 }
509 }
510 }
511
512 int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev)
513 {
514 struct sysfs_device *sysfs_device = NULL;
515 struct sysfs_class_device *class_dev_parent = NULL;
516 int retval = 0;
517 char *temp = NULL;
518
519 udev->mode = 0;
520
521 /* find the sysfs_device for this class device */
522 /* Wouldn't it really be nice if libsysfs could do this for us? */
523 if (class_dev->sysdevice) {
524 sysfs_device = class_dev->sysdevice;
525 } else {
526 /* bah, let's go backwards up a level to see if the device is there,
527 * as block partitions don't point to the physical device. Need to fix that
528 * up in the kernel...
529 */
530 if (strstr(class_dev->path, "block")) {
531 dbg("looking at block device");
532 if (isdigit(class_dev->path[strlen(class_dev->path)-1])) {
533 char path[SYSFS_PATH_MAX];
534
535 dbg("really is a partition");
536 strfieldcpy(path, class_dev->path);
537 temp = strrchr(path, '/');
538 *temp = 0x00;
539 dbg("looking for a class device at '%s'", path);
540 class_dev_parent = sysfs_open_class_device(path);
541 if (class_dev_parent == NULL) {
542 dbg("sysfs_open_class_device at '%s' failed", path);
543 } else {
544 dbg("class_dev_parent->name='%s'", class_dev_parent->name);
545 if (class_dev_parent->sysdevice)
546 sysfs_device = class_dev_parent->sysdevice;
547 }
548 }
549 }
550 }
551
552 if (sysfs_device) {
553 dbg("sysfs_device->path='%s'", sysfs_device->path);
554 dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id);
555 dbg("sysfs_device->bus='%s'", sysfs_device->bus);
556 strfieldcpy(udev->bus_id, sysfs_device->bus_id);
557 } else {
558 dbg("class_dev->name = '%s'", class_dev->name);
559 }
560
561 build_kernel_number(class_dev, udev);
562
563 /* rules are looked at in priority order */
564 retval = do_callout(class_dev, udev, sysfs_device);
565 if (retval == 0)
566 goto found;
567
568 retval = do_label(class_dev, udev, sysfs_device);
569 if (retval == 0)
570 goto found;
571
572 retval = do_number(class_dev, udev, sysfs_device);
573 if (retval == 0)
574 goto found;
575
576 retval = do_topology(class_dev, udev, sysfs_device);
577 if (retval == 0)
578 goto found;
579
580 retval = do_replace(class_dev, udev, sysfs_device);
581 if (retval == 0)
582 goto found;
583
584 do_kernelname(class_dev, udev);
585 goto done;
586
587 found:
588 /* substitute placeholder in NAME */
589 apply_format(udev, udev->name);
590
591 done:
592 /* mode was never set above */
593 if (!udev->mode) {
594 udev->mode = get_default_mode(class_dev);
595 udev->owner[0] = 0x00;
596 udev->group[0] = 0x00;
597 }
598
599 if (class_dev_parent)
600 sysfs_close_class_device(class_dev_parent);
601
602 return 0;
603 }
604
605 int namedev_init(void)
606 {
607 int retval;
608
609 retval = namedev_init_config();
610 if (retval)
611 return retval;
612
613 retval = namedev_init_permissions();
614 if (retval)
615 return retval;
616
617 dump_config_dev_list();
618 return retval;
619 }