3 * Map cdrom, cd-r, cdrw, dvd, dvdrw, dvdram to suitable devices.
4 * Prefers cd* for DVD-incapable and cdrom and dvd for read-only devices.
5 * First parameter is the kernel device name.
6 * Second parameter, if present, must be "-d" => output the full mapping.
9 * BUS="ide", KERNEL="hd[a-z]", PROGRAM="/etc/udev/cdsymlinks.sh %k", SYMLINK="%c{1} %c{2} %c{3} %c{4} %c{5} %c{6}"
10 * BUS="scsi", KERNEL="sr[0-9]*", PROGRAM="/etc/udev/cdsymlinks.sh %k", SYMLINK="%c{1} %c{2} %c{3} %c{4} %c{5} %c{6}"
11 * BUS="scsi", KERNEL="scd[0-9]*", PROGRAM="/etc/udev/cdsymlinks.sh %k", SYMLINK="%c{1} %c{2} %c{3} %c{4} %c{5} %c{6}"
12 * (this last one is "just in case")
14 * (c) 2004, 2005 Darren Salt <linux@youmustbejoking.demon.co.uk>
17 * - J A Magallon <jamagallon@able.es> (bug fixes)
19 * Last modified: 2005-02-15
31 #include <sys/types.h>
38 static const char *progname;
40 /* This file provides us with our devices and capabilities information. */
41 #define CDROM_INFO "/proc/sys/dev/cdrom/info"
43 /* This file contains our default settings. */
44 #define CONFIGURATION "/etc/udev/cdsymlinks.conf"
45 /* Default output types configuration, in the presence of an empty list */
46 #define OUTPUT_DEFAULT "CD CDRW DVD DVDRW DVDRAM"
52 struct list_item_t *next;
56 /* List root. Note offset of list_t->head and list_item_t->next */
58 struct list_item_t *head, *tail;
61 /* Configuration variables */
62 static struct list_t allowed_output = {0};
63 static int numbered_links = 1;
64 static int link_zero = 0;
66 /* Available devices */
67 static struct list_t Devices = {0};
69 /* Devices' capabilities in full (same order as available devices list).
70 * There's no cap_CD; all are assumed able to read CDs.
72 static struct list_t cap_DVDRAM = {0}, cap_DVDRW = {0}, cap_DVD = {0},
73 cap_CDRW = {0}, cap_CDR = {0}, cap_CDWMRW = {0},
74 cap_CDMRW = {0}, cap_CDRAM = {0};
76 /* Device capabilities by name */
77 static struct list_t dev_DVDRAM = {0}, dev_DVDRW = {0}, dev_DVD = {0},
78 dev_CDRW = {0}, dev_CDR = {0}, dev_CDWMRW = {0},
79 dev_CDMRW = {0}, dev_CDRAM = {0};
80 #define dev_CD Devices
83 struct list_t *cap, *dev;
84 const char label[8], symlink[8];
89 #define CAPDEV(X) &cap_##X, &dev_##X
91 static const cap_dev_t cap_dev_info[] = {
92 { NULL, &dev_CD, "CD", "cdrom", NULL, 0 },
93 { CAPDEV(CDR), "CDR", "cd-r", "Can write CD-R:", 15 },
94 { CAPDEV(CDRW), "CDRW", "cdrw", "Can write CD-RW:", 16 },
95 { CAPDEV(DVD), "DVD", "dvd", "Can read DVD:", 13 },
96 { CAPDEV(DVDRW), "DVDRW", "dvdrw", "Can write DVD-R:", 16 },
97 { CAPDEV(DVDRAM), "DVDRAM", "dvdram", "Can write DVD-RAM:", 18 },
98 { CAPDEV(CDMRW), "CDMRW", "cdm", "Can read MRW:", 13 }, /* CDC-MRW R */
99 { CAPDEV(CDWMRW), "CDWMRW", "cdmrw", "Can write MRW:", 14 }, /* CDC-MRW W */
100 { CAPDEV(CDRAM), "CDRAM", "cdram", "Can write RAM:", 14 }, /* CDC-RAM W */
104 #define foreach_cap_dev(loop) \
105 for ((loop) = cap_dev_info; (loop)->label[0]; ++(loop))
106 #define foreach_cap_dev_noCD(loop) \
107 for ((loop) = cap_dev_info + 1; (loop)->label[0]; ++(loop))
110 * Some library-like bits first...
114 errexit (const char *reason)
116 fprintf (stderr, "%s: %s: %s\n", progname, reason, strerror (errno));
122 msgexit (const char *reason)
124 fprintf (stderr, "%s: %s\n", progname, reason);
130 errwarn (const char *reason)
132 fprintf (stderr, "%s: warning: %s: %s\n", progname, reason, strerror (errno));
137 msgwarn (const char *reason)
139 fprintf (stderr, "%s: warning: %s\n", progname, reason);
144 xmalloc (size_t size)
146 void *mem = malloc (size);
148 msgexit ("malloc failed");
154 xstrdup (const char *text)
156 char *mem = xmalloc (strlen (text) + 1);
157 return strcpy (mem, text);
161 /* Append a string to a list. The string is duplicated. */
163 list_append (struct list_t *list, const char *data)
165 struct list_item_t *node = xmalloc (sizeof (struct list_item_t));
168 list->tail->next = node;
172 node->data = xstrdup (data);
176 /* Prepend a string to a list. The string is duplicated. */
178 list_prepend (struct list_t *list, const char *data)
180 struct list_item_t *node = xmalloc (sizeof (struct list_item_t));
181 node->next = list->head;
185 node->data = xstrdup (data);
189 /* Delete a lists's contents, freeing claimed memory */
191 list_delete (struct list_t *list)
193 struct list_item_t *node = list->head;
196 struct list_item_t *n = node;
201 list->tail = list->head = NULL;
205 /* Print out a list on one line, each item space-prefixed, no LF */
207 list_print (const struct list_t *list, FILE *stream)
209 const struct list_item_t *node = (const struct list_item_t *)list;
210 while ((node = node->next) != NULL)
211 fprintf (stream, " %s", node->data);
215 /* Return the nth item in a list (count from 0)
216 * If there aren't enough items in the list, return the requested default
218 static const struct list_item_t *
219 list_nth (const struct list_t *list, size_t nth)
221 const struct list_item_t *node = list->head;
231 /* Return the first matching item in a list, or NULL */
232 static const struct list_item_t *
233 list_search (const struct list_t *list, const char *data)
235 const struct list_item_t *node = list->head;
238 if (!strcmp (node->data, data))
246 /* Split up a string on whitespace & assign the resulting tokens to a list.
247 * Ignore everything up until the first colon (if present).
250 list_assign_split (struct list_t *list, char *text)
252 char *token = strchr (text, ':');
253 token = strtok (token ? token + 1 : text, " \t\n");
256 list_prepend (list, token);
257 token = strtok (0, " \t\n");
263 /* Gather the default settings. */
267 FILE *conf = fopen (CONFIGURATION, "r");
271 errwarn ("error accessing configuration");
277 while (getline (&text, &textlen, conf) != -1)
280 int len = strlen (text);
281 if (len && text[len - 1] == '\n')
283 if (len && text[len - 1] == '\r')
287 char *token = text + strspn (text, " \t");
288 if (!*token || *token == '#')
290 switch (len = wordexp (text, &p, 0))
293 msgexit ("malloc failed");
297 if (!strncmp (p.we_wordv[0], "OUTPUT=", 7))
299 list_delete (&allowed_output);
300 list_assign_split (&allowed_output, p.we_wordv[0] + 7);
302 else if (!strncmp (p.we_wordv[0], "NUMBERED_LINKS=", 15))
303 numbered_links = atoi (p.we_wordv[0] + 15);
304 else if (!strncmp (p.we_wordv[0], "LINK_ZERO=", 15))
305 link_zero = atoi (p.we_wordv[0] + 15);
310 msgwarn ("syntax error in configuration file");
315 errwarn ("error accessing configuration");
317 errwarn ("error accessing configuration");
320 if (!allowed_output.head)
322 char *dflt = strdup (OUTPUT_DEFAULT);
323 list_assign_split (&allowed_output, dflt);
329 /* From information supplied by the kernel:
330 * + get the names of the available devices
331 * + populate our capability lists
332 * Order is significant: device item N maps to each capability item N.
335 populate_capability_lists (void)
337 FILE *info = fopen (CDROM_INFO, "r");
342 errexit ("error accessing CD/DVD info");
348 while (getline (&text, &textlen, info) != -1)
350 if (!strncasecmp (text, "drive name", 10))
351 list_assign_split (&Devices, text);
354 const cap_dev_t *cap;
355 foreach_cap_dev_noCD (cap)
356 if (!strncasecmp (text, cap->captext, cap->captextlen))
358 list_assign_split (cap->cap, text);
364 errexit ("error accessing CD/DVD info");
370 /* Write out the links of type LINK which should be created for device NAME,
371 * taking into account existing links and the capability list for type LINK.
374 do_output (const char *name, const char *link, const struct list_t *dev,
377 const struct list_item_t *i = (const struct list_item_t *)dev;
383 size_t link_len = strlen (link);
384 DIR *dir = opendir ("/dev");
386 errexit ("error reading /dev");
388 struct list_t devls = {0}; /* symlinks whose name matches LINK */
389 struct list_t devlinks = {0}; /* those symlinks' targets */
390 struct dirent *entry;
391 while ((entry = readdir (dir)) != NULL)
393 if (strncmp (entry->d_name, link, link_len))
394 continue; /* wrong name: ignore it */
396 /* The rest of the name must be null or consist entirely of digits. */
397 const char *p = entry->d_name + link_len - 1;
402 continue; /* wrong format - ignore */
404 /* Assume that it's a symlink and try to read its target. */
405 char buf[sizeof (entry->d_name)];
406 int r = readlink (entry->d_name, buf, sizeof (buf) - 1);
410 continue; /* not a symlink - ignore */
411 errexit ("error reading link in /dev");
413 /* We have the name and the target, so update our lists. */
415 list_append (&devls, entry->d_name);
416 list_append (&devlinks, buf);
419 errexit ("error reading /dev");
421 errexit ("error closing /dev");
423 /* Now we write our output... */
425 while ((i = i->next) != NULL)
427 int isdev = !strcmp (name, i->data); /* current dev == target dev? */
430 const struct list_item_t *l = (const struct list_item_t *)&devlinks;
432 /* First, we look for existing symlinks to the target device. */
433 while (++li, (l = l->next) != NULL)
435 if (strcmp (l->data, i->data))
437 /* Existing symlink found - don't output a new one.
438 * If ISDEV, we output the name of the existing symlink.
444 printf (" %s", list_nth (&devls, li)->data);
447 /* If we found no existing symlinks for the target device... */
451 snprintf (buf, sizeof (buf), count || do_link_zero ? "%s%d" : "%s",
453 /* Find the next available (not present) symlink name.
454 * We always need to do this for reasons of output consistency: if a
455 * symlink is created by udev as a result of use of this program, we
456 * DON'T want different output!
458 while (list_search (&devls, buf))
462 snprintf (buf, sizeof (buf), "%s%d", link, ++count);
464 /* If ISDEV, output it. */
465 if (isdev && (numbered_links || count == 0))
467 /* If the link isn't in our "existing links" list, add it and increment
470 if (!list_search (&devls, buf))
474 list_append (&devls, buf);
480 list_delete (&devls);
481 list_delete (&devlinks);
485 /* Populate a device list from a capabilities list. */
487 populate_device_list (struct list_t *out, const struct list_t *caps)
489 const struct list_item_t *cap, *dev;
490 cap = (const struct list_item_t *)caps;
491 dev = (const struct list_item_t *)&Devices;
492 while ((cap = cap->next) != NULL && (dev = dev->next) != NULL)
493 if (cap->data[0] != '0')
494 list_append (out, dev->data);
499 main (int argc, char *argv[])
501 const cap_dev_t *capdev;
504 debug = argc > 2 && !strcmp (argv[2], "-d");
506 if (argc < 2 || argc > 2 + debug)
507 msgexit ("usage: cdsymlinks DEVICE [-d]");
510 errexit ("can't chdir /dev");
513 populate_capability_lists ();
515 /* Construct the device lists from the capability lists.
516 * (We assume that all relevant devices can read CDs.)
518 foreach_cap_dev_noCD (capdev)
519 populate_device_list (capdev->dev, capdev->cap);
524 const struct list_item_t *item = (const struct list_item_t *)&Devices;
525 while ((item = item->next) != NULL)
526 printf (" %s", item->data);
528 printf ("\nCDROM : (all)");
529 item = (const struct list_item_t *)&dev_CD;
530 while ((item = item->next) != NULL)
531 printf (" %s", item->data);
534 foreach_cap_dev_noCD (capdev)
536 printf ("%-10s:", capdev->label);
537 list_print (capdev->cap, stdout);
538 list_print (capdev->dev, stdout);
544 /* Write the symlink names. */
545 foreach_cap_dev (capdev)
546 if (list_search (&allowed_output, capdev->label))
548 do_output (argv[1], capdev->symlink, capdev->dev, 0);
550 do_output (argv[1], capdev->symlink, capdev->dev, 1);