]>
git.ipfire.org Git - thirdparty/systemd.git/blob - extras/cdsymlinks.c
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 Darren Salt <linux@youmustbejoking.demon.co.uk>
26 #include <sys/types.h>
33 static const char *progname
;
35 /* This file provides us with our devices and capabilities information. */
36 #define CDROM_INFO "/proc/sys/dev/cdrom/info"
38 /* This file contains our default settings. */
39 #define CONFIGURATION "/etc/udev/cdsymlinks.conf"
40 /* Default output types configuration, in the presence of an empty list */
41 #define OUTPUT_DEFAULT "CD CDRW DVD DVDRW DVDRAM"
47 struct list_item_t
*next
;
51 /* List root. Note offset of list_t->head and list_item_t->next */
53 struct list_item_t
*head
, *tail
;
56 /* Configuration variables */
57 static struct list_t allowed_output
= {0};
58 static int numbered_links
= 1;
60 /* Available devices */
61 static struct list_t Devices
= {0};
63 /* Devices' capabilities in full (same order as available devices list).
64 * There's no cap_CD; all are assumed able to read CDs.
66 static struct list_t cap_DVDRAM
= {0}, cap_DVDRW
= {0}, cap_DVD
= {0},
67 cap_CDRW
= {0}, cap_CDR
= {0}, cap_CDWMRW
= {0},
70 /* Device capabilities by name */
71 static struct list_t dev_DVDRAM
= {0}, dev_DVDRW
= {0}, dev_DVD
= {0},
72 dev_CDRW
= {0}, dev_CDR
= {0}, dev_CDWMRW
= {0},
74 #define dev_CD Devices
78 * Some library-like bits first...
82 errexit (const char *reason
)
84 fprintf (stderr
, "%s: %s: %s\n", progname
, reason
, strerror (errno
));
90 msgexit (const char *reason
)
92 fprintf (stderr
, "%s: %s\n", progname
, reason
);
98 errwarn (const char *reason
)
100 fprintf (stderr
, "%s: warning: %s: %s\n", progname
, reason
, strerror (errno
));
105 msgwarn (const char *reason
)
107 fprintf (stderr
, "%s: warning: %s\n", progname
, reason
);
112 xmalloc (size_t size
)
114 void *mem
= malloc (size
);
116 msgexit ("malloc failed");
122 xstrdup (const char *text
)
124 char *mem
= xmalloc (strlen (text
) + 1);
125 return strcpy (mem
, text
);
129 /* Append a string to a list. The string is duplicated. */
131 list_append (struct list_t
*list
, const char *data
)
133 struct list_item_t
*node
= xmalloc (sizeof (struct list_item_t
));
136 list
->tail
->next
= node
;
140 node
->data
= xstrdup (data
);
144 /* Prepend a string to a list. The string is duplicated. */
146 list_prepend (struct list_t
*list
, const char *data
)
148 struct list_item_t
*node
= xmalloc (sizeof (struct list_item_t
));
149 node
->next
= list
->head
;
153 node
->data
= xstrdup (data
);
157 /* Delete a lists's contents, freeing claimed memory */
159 list_delete (struct list_t
*list
)
161 struct list_item_t
*node
= list
->head
;
164 struct list_item_t
*n
= node
;
169 list
->tail
= list
->head
= NULL
;
173 /* Print out a list on one line, each item space-prefixed, no LF */
175 list_print (const struct list_t
*list
, FILE *stream
)
177 const struct list_item_t
*node
= (const struct list_item_t
*)list
;
178 while ((node
= node
->next
) != NULL
)
179 fprintf (stream
, " %s", node
->data
);
183 /* Return the nth item in a list (count from 0)
184 * If there aren't enough items in the list, return the requested default
186 static const struct list_item_t
*
187 list_nth (const struct list_t
*list
, size_t nth
)
189 const struct list_item_t
*node
= list
->head
;
199 /* Return the first matching item in a list, or NULL */
200 static const struct list_item_t
*
201 list_search (const struct list_t
*list
, const char *data
)
203 const struct list_item_t
*node
= list
->head
;
206 if (!strcmp (node
->data
, data
))
214 /* Split up a string on whitespace & assign the resulting tokens to a list.
215 * Ignore everything up until the first colon (if present).
218 list_assign_split (struct list_t
*list
, char *text
)
220 char *token
= strchr (text
, ':');
221 token
= strtok (token
? token
+ 1 : text
, " \t");
224 list_prepend (list
, token
);
225 token
= strtok (0, " \t\n");
231 /* Gather the default settings. */
235 FILE *conf
= fopen (CONFIGURATION
, "r");
239 errwarn ("error accessing configuration");
245 while (getline (&text
, &textlen
, conf
) != -1)
248 int len
= strlen (text
);
249 if (len
&& text
[len
- 1] == '\n')
251 if (len
&& text
[len
- 1] == '\r')
255 char *token
= text
+ strspn (text
, " \t");
256 if (!*token
|| *token
== '#')
258 switch (len
= wordexp (text
, &p
, 0))
261 msgexit ("malloc failed");
265 if (!strncmp (p
.we_wordv
[0], "OUTPUT=", 7))
267 list_delete (&allowed_output
);
268 list_assign_split (&allowed_output
, p
.we_wordv
[0] + 7);
270 else if (!strncmp (p
.we_wordv
[0], "NUMBERED_LINKS=", 14))
271 numbered_links
= atoi (p
.we_wordv
[0] + 14);
276 msgwarn ("syntax error in configuration file");
281 errwarn ("error accessing configuration");
283 errwarn ("error accessing configuration");
286 if (!allowed_output
.head
)
288 char *dflt
= strdup (OUTPUT_DEFAULT
);
289 list_assign_split (&allowed_output
, dflt
);
295 /* From information supplied by the kernel:
296 * + get the names of the available devices
297 * + populate our capability lists
298 * Order is significant: device item N maps to each capability item N.
301 populate_capability_lists (void)
303 FILE *info
= fopen (CDROM_INFO
, "r");
308 errexit ("error accessing CD/DVD info");
314 while (getline (&text
, &textlen
, info
) != -1)
316 if (!strncasecmp (text
, "drive name", 10))
317 list_assign_split (&Devices
, text
);
318 else if (!strncasecmp (text
, "Can write DVD-RAM", 17))
319 list_assign_split (&cap_DVDRAM
, text
);
320 else if (!strncasecmp (text
, "Can write DVD-R", 15))
321 list_assign_split (&cap_DVDRW
, text
);
322 else if (!strncasecmp (text
, "Can read DVD", 12))
323 list_assign_split (&cap_DVD
, text
);
324 else if (!strncasecmp (text
, "Can write CD-RW", 15))
325 list_assign_split (&cap_CDRW
, text
);
326 else if (!strncasecmp (text
, "Can write CD-R", 14))
327 list_assign_split (&cap_CDR
, text
);
328 else if (!strncasecmp (text
, "Can read MRW", 14))
329 list_assign_split (&cap_CDMRW
, text
);
330 else if (!strncasecmp (text
, "Can write MRW", 14))
331 list_assign_split (&cap_CDWMRW
, text
);
334 errexit ("error accessing CD/DVD info");
340 /* Write out the links of type LINK which should be created for device NAME,
341 * taking into account existing links and the capability list for type LINK.
344 do_output (const char *name
, const char *link
, const struct list_t
*dev
)
346 const struct list_item_t
*i
= (const struct list_item_t
*)dev
;
352 size_t link_len
= strlen (link
);
353 DIR *dir
= opendir ("/dev");
355 errexit ("error reading /dev");
357 struct list_t devls
= {0}; /* symlinks whose name matches LINK */
358 struct list_t devlinks
= {0}; /* those symlinks' targets */
359 struct dirent
*entry
;
360 while ((entry
= readdir (dir
)) != NULL
)
362 if (strncmp (entry
->d_name
, link
, link_len
))
363 continue; /* wrong name: ignore it */
365 /* The rest of the name must be null or consist entirely of digits. */
366 const char *p
= entry
->d_name
+ link_len
- 1;
371 continue; /* wrong format - ignore */
373 /* Assume that it's a symlink and try to read its target. */
374 char buf
[sizeof (entry
->d_name
)];
375 int r
= readlink (entry
->d_name
, buf
, sizeof (buf
) - 1);
379 continue; /* not a symlink - ignore */
380 errexit ("error reading link in /dev");
382 /* We have the name and the target, so update our lists. */
384 list_append (&devls
, entry
->d_name
);
385 list_append (&devlinks
, buf
);
388 errexit ("error reading /dev");
390 errexit ("error closing /dev");
392 /* Now we write our output... */
394 while ((i
= i
->next
) != NULL
)
396 int isdev
= !strcmp (name
, i
->data
); /* current dev == target dev? */
399 const struct list_item_t
*l
= (const struct list_item_t
*)&devlinks
;
401 /* First, we look for existing symlinks to the target device. */
402 while (++li
, (l
= l
->next
) != NULL
)
404 if (strcmp (l
->data
, i
->data
))
406 /* Existing symlink found - don't output a new one.
407 * If ISDEV, we output the name of the existing symlink.
411 printf (" %s", list_nth (&devls
, li
)->data
);
414 /* If we found no existing symlinks for the target device... */
418 snprintf (buf
, sizeof (buf
), count
? "%s%d" : "%s", link
, count
);
419 /* Find the next available (not present) symlink name.
420 * We always need to do this for reasons of output consistency: if a
421 * symlink is created by udev as a result of use of this program, we
422 * DON'T want different output!
424 while (list_search (&devls
, buf
))
425 snprintf (buf
, sizeof (buf
), "%s%d", link
, ++count
);
426 /* If ISDEV, output it. */
427 if (isdev
&& (numbered_links
|| count
== 0))
429 /* If the link isn't in our "existing links" list, add it and increment
432 if (!list_search (&devls
, buf
))
434 list_append (&devls
, buf
);
440 list_delete (&devls
);
441 list_delete (&devlinks
);
445 /* Populate a device list from a capabilities list. */
447 populate_device_list (struct list_t
*out
, const struct list_t
*caps
)
449 const struct list_item_t
*cap
, *dev
;
450 cap
= (const struct list_item_t
*)caps
;
451 dev
= (const struct list_item_t
*)&Devices
;
452 while ((cap
= cap
->next
) != NULL
&& (dev
= dev
->next
) != NULL
)
453 if (cap
->data
[0] != '0')
454 list_append (out
, dev
->data
);
459 main (int argc
, char *argv
[])
462 debug
= argc
> 2 && !strcmp (argv
[2], "-d");
464 if (argc
< 2 || argc
> 2 + debug
)
465 msgexit ("usage: cdsymlinks DEVICE [-d]");
468 errexit ("can't chdir /dev");
471 populate_capability_lists ();
473 /* Construct the device lists from the capability lists. */
474 populate_device_list (&dev_DVDRAM
, &cap_DVDRAM
);
475 populate_device_list (&dev_DVDRW
, &cap_DVDRW
);
476 populate_device_list (&dev_DVD
, &cap_DVD
);
477 populate_device_list (&dev_CDRW
, &cap_CDRW
);
478 populate_device_list (&dev_CDR
, &cap_CDR
);
479 populate_device_list (&dev_CDWMRW
, &cap_CDWMRW
);
480 populate_device_list (&dev_CDMRW
, &cap_CDMRW
);
481 /* (All devices can read CDs.) */
485 #define printdev(DEV) \
486 printf ("%-7s:", #DEV); \
487 list_print (&cap_##DEV, stdout); \
488 list_print (&dev_##DEV, stdout); \
492 const struct list_item_t
*item
= (const struct list_item_t
*)&Devices
;
493 while ((item
= item
->next
) != NULL
)
494 printf (" %s", item
->data
);
505 printf ("CDROM : (all)");
506 item
= (const struct list_item_t
*)&dev_CD
;
507 while ((item
= item
->next
) != NULL
)
508 printf (" %s", item
->data
);
512 /* Write the symlink names. */
513 if (list_search (&allowed_output
, "CD"))
514 do_output (argv
[1], "cdrom", &dev_CD
);
515 if (list_search (&allowed_output
, "CDR"))
516 do_output (argv
[1], "cd-r", &dev_CDR
);
517 if (list_search (&allowed_output
, "CDRW"))
518 do_output (argv
[1], "cdrw", &dev_CDRW
);
519 if (list_search (&allowed_output
, "DVD"))
520 do_output (argv
[1], "dvd", &dev_DVD
);
521 if (list_search (&allowed_output
, "DVDRW"))
522 do_output (argv
[1], "dvdrw", &dev_DVDRW
);
523 if (list_search (&allowed_output
, "DVDRAM"))
524 do_output (argv
[1], "dvdram", &dev_DVDRAM
);
525 if (list_search (&allowed_output
, "CDMRW"))
526 do_output (argv
[1], "cdmrw", &dev_CDMRW
);
527 if (list_search (&allowed_output
, "CDWMRW"))
528 do_output (argv
[1], "cdwmrw", &dev_CDWMRW
);