mdev.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /* mdev.c - Populate /dev directory and handle hotplug events
  2. *
  3. * Copyright 2005, 2008 Rob Landley <rob@landley.net>
  4. * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
  5. USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
  6. config MDEV
  7. bool "mdev"
  8. default n
  9. help
  10. usage: mdev [-s]
  11. Create devices in /dev using information from /sys.
  12. -s Scan all entries in /sys to populate /dev
  13. config MDEV_CONF
  14. bool "Configuration file for mdev"
  15. default y
  16. depends on MDEV
  17. help
  18. The mdev config file (/etc/mdev.conf) contains lines that look like:
  19. hd[a-z][0-9]* 0:3 660
  20. (sd[a-z]) root:disk 660 =usb_storage
  21. Each line must contain three whitespace separated fields. The first
  22. field is a regular expression matching one or more device names,
  23. the second and third fields are uid:gid and file permissions for
  24. matching devices. Fourth field is optional. It could be used to change
  25. device name (prefix '='), path (prefix '=' and postfix '/') or create a
  26. symlink (prefix '>').
  27. */
  28. #include "toys.h"
  29. #include <stdbool.h>
  30. // mknod in /dev based on a path like "/sys/block/hda/hda1"
  31. static void make_device(char *path)
  32. {
  33. char *device_name = 0, *custom_name = 0, *s, *temp;
  34. bool create_symlink = false;
  35. int major = 0, minor = 0, type, len, fd, mode = 0660;
  36. uid_t uid = 0;
  37. gid_t gid = 0;
  38. if (path) {
  39. // Try to read major/minor string, returning if we can't
  40. temp = strrchr(path, '/');
  41. fd = open(path, O_RDONLY);
  42. *temp = 0;
  43. len = read(fd, toybuf, 64);
  44. close(fd);
  45. if (len<1) return;
  46. toybuf[len] = 0;
  47. // Determine device type, major and minor
  48. type = path[5]=='c' ? S_IFCHR : S_IFBLK;
  49. sscanf(toybuf, "%u:%u", &major, &minor);
  50. } else {
  51. // if (!path), do hotplug
  52. if (!(temp = getenv("MODALIAS"))) xrun((char *[]){"modprobe", temp, 0});
  53. if (!(temp = getenv("SUBSYSTEM"))) return;
  54. type = strcmp(temp, "block") ? S_IFCHR : S_IFBLK;
  55. if (!(temp = getenv("MAJOR"))) return;
  56. sscanf(temp, "%u", &major);
  57. if (!(temp = getenv("MINOR"))) return;
  58. sscanf(temp, "%u", &minor);
  59. if (!(path = getenv("DEVPATH"))) return;
  60. device_name = getenv("DEVNAME");
  61. }
  62. if (!device_name)
  63. device_name = strrchr(path, '/') + 1;
  64. // as in linux/drivers/base/core.c, device_get_devnode()
  65. while ((temp = strchr(device_name, '!'))) {
  66. *temp = '/';
  67. }
  68. // If we have a config file, look up permissions for this device
  69. if (CFG_MDEV_CONF) {
  70. char *conf, *pos, *end;
  71. bool optional_field_valid = false;
  72. // mmap the config file
  73. if (-1!=(fd = open("/etc/mdev.conf", O_RDONLY))) {
  74. int line = 0;
  75. len = fdlength(fd);
  76. conf = xmmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
  77. // Loop through lines in mmaped file
  78. for (pos = conf; pos-conf<len;) {
  79. int field;
  80. char *end2;
  81. line++;
  82. // find end of this line
  83. for(end = pos; end-conf<len && *end!='\n'; end++);
  84. // Four fields (last is optional): regex, uid:gid, mode [, name|path ]
  85. // For example: (sd[a-z])1 root:disk 660 =usb_storage_p1
  86. for (field = 4; field; field--) {
  87. // Skip whitespace
  88. while (pos<end && isspace(*pos)) pos++;
  89. if (pos==end || *pos=='#') break;
  90. for (end2 = pos;
  91. end2<end && !isspace(*end2) && *end2!='#'; end2++);
  92. switch(field) {
  93. // Regex to match this device
  94. case 4:
  95. {
  96. char *regex = strndup(pos, end2-pos);
  97. regex_t match;
  98. regmatch_t off;
  99. int result;
  100. // Is this it?
  101. xregcomp(&match, regex, REG_EXTENDED);
  102. result=regexec(&match, device_name, 1, &off, 0);
  103. regfree(&match);
  104. free(regex);
  105. // If not this device, skip rest of line
  106. if (result || off.rm_so
  107. || off.rm_eo!=strlen(device_name))
  108. goto end_line;
  109. break;
  110. }
  111. // uid:gid
  112. case 3:
  113. {
  114. char *s2;
  115. // Find :
  116. for(s = pos; s<end2 && *s!=':'; s++);
  117. if (s==end2) goto end_line;
  118. // Parse UID
  119. uid = strtoul(pos,&s2,10);
  120. if (s!=s2) {
  121. struct passwd *pass;
  122. char *str = strndup(pos, s-pos);
  123. pass = getpwnam(str);
  124. free(str);
  125. if (!pass) goto end_line;
  126. uid = pass->pw_uid;
  127. }
  128. s++;
  129. // parse GID
  130. gid = strtoul(s,&s2,10);
  131. if (end2!=s2) {
  132. struct group *grp;
  133. char *str = strndup(s, end2-s);
  134. grp = getgrnam(str);
  135. free(str);
  136. if (!grp) goto end_line;
  137. gid = grp->gr_gid;
  138. }
  139. break;
  140. }
  141. // mode
  142. case 2:
  143. {
  144. char *beg_pos = pos;
  145. mode = strtoul(pos, &pos, 8);
  146. if (pos == beg_pos) {
  147. // The line is bad because mode setting could not be
  148. // converted to numeric value.
  149. goto end_line;
  150. }
  151. break;
  152. }
  153. // Try to look for name or path (optional field)
  154. case 1:
  155. {
  156. if(pos < end2){
  157. //char *name = strndup(pos, end2-pos);
  158. char *name = malloc(end2-pos+1);
  159. if(name){
  160. strncpy(name, pos, end2-pos+1);
  161. name[end2-pos] = '\0';
  162. switch(name[0]){
  163. case '>':
  164. create_symlink = true;
  165. case '=':
  166. custom_name = strdup(&name[1]);
  167. break;
  168. case '!':
  169. device_name = NULL;
  170. break;
  171. default:
  172. free(name);
  173. goto end_line;
  174. }
  175. free(name);
  176. optional_field_valid = true;
  177. }
  178. }
  179. goto found_device;
  180. }
  181. }
  182. pos=end2;
  183. }
  184. end_line:
  185. // Did everything parse happily?
  186. // Note: Last field is optional.
  187. if ((field>1 || (field==1 && !optional_field_valid)) && field!=4)
  188. error_exit("Bad line %d", line);
  189. // Next line
  190. pos = ++end;
  191. }
  192. found_device:
  193. munmap(conf, len);
  194. }
  195. close(fd);
  196. }
  197. if(device_name) {
  198. if(custom_name) {
  199. sprintf(toybuf, "/dev/%s", custom_name);
  200. if(custom_name[strlen(custom_name) - 1] == '/') {
  201. DIR *dir;
  202. dir = opendir(toybuf);
  203. if(dir) closedir(dir);
  204. else mkdir(toybuf, 0755);
  205. }
  206. }
  207. else
  208. sprintf(toybuf, "/dev/%s", device_name);
  209. if ((temp=getenv("ACTION")) && !strcmp(temp, "remove")) {
  210. unlink(toybuf);
  211. return;
  212. }
  213. if (strchr(device_name, '/')) mkpath(toybuf);
  214. if (mknod(toybuf, mode | type, dev_makedev(major, minor)) &&
  215. errno != EEXIST)
  216. perror_exit("mknod %s failed", toybuf);
  217. if(create_symlink){
  218. char *symlink_name = malloc(sizeof("/dev/") + strlen(device_name) + 1);
  219. if(symlink_name == NULL)
  220. perror_exit("malloc failed while creating symlink to %s", toybuf);
  221. sprintf(symlink_name, "/dev/%s", device_name);
  222. if(symlink(toybuf, symlink_name)){
  223. free(symlink_name);
  224. perror_exit("symlink creation failed for %s", toybuf);
  225. }
  226. free(symlink_name);
  227. }
  228. if (type == S_IFBLK) close(open(toybuf, O_RDONLY)); // scan for partitions
  229. if (CFG_MDEV_CONF) mode=chown(toybuf, uid, gid);
  230. }
  231. }
  232. static int callback(struct dirtree *node)
  233. {
  234. // Entries in /sys/class/block aren't char devices, so skip 'em. (We'll
  235. // get block devices out of /sys/block.)
  236. if(!strcmp(node->name, "block")) return 0;
  237. // Does this directory have a "dev" entry in it?
  238. // This is path based because the hotplug callbacks are
  239. if (S_ISDIR(node->st.st_mode) || S_ISLNK(node->st.st_mode)) {
  240. int len=4;
  241. char *dev = dirtree_path(node, &len);
  242. strcpy(dev+len, "/dev");
  243. if (!access(dev, R_OK)) make_device(dev);
  244. free(dev);
  245. }
  246. // Circa 2.6.25 the entries more than 2 deep are all either redundant
  247. // (mouse#, event#) or unnamed (every usb_* entry is called "device").
  248. if (node->parent && node->parent->parent) return 0;
  249. return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
  250. }
  251. void mdev_main(void)
  252. {
  253. // Handle -s
  254. if (toys.optflags) {
  255. dirtree_read("/sys/class", callback);
  256. if (!access("/sys/block", R_OK)) dirtree_read("/sys/block", callback);
  257. } else { // hotplug support
  258. make_device(NULL);
  259. }
  260. }