udevtrigger.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
  3. * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the
  7. * Free Software Foundation version 2 of the License.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. *
  18. */
  19. #include <stdlib.h>
  20. #include <stddef.h>
  21. #include <stdarg.h>
  22. #include <stdio.h>
  23. #include <stdbool.h>
  24. #include <unistd.h>
  25. #include <getopt.h>
  26. #include <errno.h>
  27. #include <dirent.h>
  28. #include <fcntl.h>
  29. #include <syslog.h>
  30. #include <sys/stat.h>
  31. #include <sys/types.h>
  32. #include <string.h>
  33. #define PATH_SIZE 512
  34. #ifndef strlcpy
  35. #define strlcpy(d,s,l) (strncpy(d,s,l), (d)[(l)-1] = '\0')
  36. #endif
  37. #ifndef strlcat
  38. #define strlcat(d,s,l) strncat(d,s,(l)-strlen(d)-1)
  39. #endif
  40. static int verbose;
  41. static int dry_run;
  42. static void log_message(int priority, const char *format, ...)
  43. {
  44. va_list args;
  45. va_start(args, format);
  46. vsyslog(priority, format, args);
  47. va_end(args);
  48. }
  49. #undef err
  50. #define err(format, arg...) \
  51. do { \
  52. log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \
  53. } while (0)
  54. #undef info
  55. #define info(format, arg...) \
  56. do { \
  57. log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \
  58. } while (0)
  59. #ifdef UDEV_DEBUG
  60. #undef dbg
  61. #define dbg(format, arg...) \
  62. do { \
  63. log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \
  64. } while (0)
  65. #else
  66. #define dbg(...) do {} while(0)
  67. #endif
  68. static void trigger_uevent(const char *devpath)
  69. {
  70. char filename[PATH_SIZE];
  71. int fd;
  72. strlcpy(filename, "/sys", sizeof(filename));
  73. strlcat(filename, devpath, sizeof(filename));
  74. strlcat(filename, "/uevent", sizeof(filename));
  75. if (verbose)
  76. printf("%s\n", devpath);
  77. if (dry_run)
  78. return;
  79. fd = open(filename, O_WRONLY);
  80. if (fd < 0) {
  81. dbg("error on opening %s: %m\n", filename);
  82. return;
  83. }
  84. if (write(fd, "add", 3) < 0)
  85. info("error on triggering %s: %m\n", filename);
  86. close(fd);
  87. }
  88. static int sysfs_resolve_link(char *devpath, size_t size)
  89. {
  90. char link_path[PATH_SIZE];
  91. char link_target[PATH_SIZE];
  92. int len;
  93. int i;
  94. int back;
  95. strlcpy(link_path, "/sys", sizeof(link_path));
  96. strlcat(link_path, devpath, sizeof(link_path));
  97. len = readlink(link_path, link_target, sizeof(link_target) - 1);
  98. if (len <= 0)
  99. return -1;
  100. link_target[len] = '\0';
  101. dbg("path link '%s' points to '%s'", devpath, link_target);
  102. for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
  103. ;
  104. dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back);
  105. for (i = 0; i <= back; i++) {
  106. char *pos = strrchr(devpath, '/');
  107. if (pos == NULL)
  108. return -1;
  109. pos[0] = '\0';
  110. }
  111. dbg("after moving back '%s'", devpath);
  112. strlcat(devpath, "/", size);
  113. strlcat(devpath, &link_target[back * 3], size);
  114. return 0;
  115. }
  116. static bool device_has_attribute(const char *path, const char *attr,
  117. mode_t mode)
  118. {
  119. char filename[PATH_SIZE];
  120. struct stat statbuf;
  121. strlcpy(filename, path, sizeof(filename));
  122. strlcat(filename, attr, sizeof(filename));
  123. if (stat(filename, &statbuf) < 0)
  124. return false;
  125. if (!(statbuf.st_mode & mode))
  126. return false;
  127. return true;
  128. }
  129. static int device_list_insert(const char *path)
  130. {
  131. char devpath[PATH_SIZE];
  132. struct stat statbuf;
  133. dbg("add '%s'" , path);
  134. /* we only have a device, if we have a dev and an uevent file */
  135. if (!device_has_attribute(path, "/dev", S_IRUSR) ||
  136. !device_has_attribute(path, "/uevent", S_IWUSR))
  137. return -1;
  138. strlcpy(devpath, &path[4], sizeof(devpath));
  139. /* resolve possible link to real target */
  140. if (lstat(path, &statbuf) < 0)
  141. return -1;
  142. if (S_ISLNK(statbuf.st_mode))
  143. if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0)
  144. return -1;
  145. trigger_uevent(devpath);
  146. return 0;
  147. }
  148. static void scan_subdir(const char *base, const char *subdir,
  149. bool insert, int depth)
  150. {
  151. DIR *dir;
  152. struct dirent *dent;
  153. dir = opendir(base);
  154. if (dir == NULL)
  155. return;
  156. for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
  157. char dirname[PATH_SIZE];
  158. if (dent->d_name[0] == '.')
  159. continue;
  160. strlcpy(dirname, base, sizeof(dirname));
  161. strlcat(dirname, "/", sizeof(dirname));
  162. strlcat(dirname, dent->d_name, sizeof(dirname));
  163. if (insert) {
  164. int err;
  165. err = device_list_insert(dirname);
  166. if (err)
  167. continue;
  168. }
  169. if (subdir)
  170. strlcat(dirname, subdir, sizeof(base));
  171. if (depth)
  172. scan_subdir(dirname, NULL, true, depth - 1);
  173. }
  174. closedir(dir);
  175. }
  176. int main(int argc, char *argv[], char *envp[])
  177. {
  178. struct stat statbuf;
  179. int option;
  180. openlog("udevtrigger", LOG_PID | LOG_CONS, LOG_DAEMON);
  181. while (1) {
  182. option = getopt(argc, argv, "vnh");
  183. if (option == -1)
  184. break;
  185. switch (option) {
  186. case 'v':
  187. verbose = 1;
  188. break;
  189. case 'n':
  190. dry_run = 1;
  191. break;
  192. case 'h':
  193. printf("Usage: udevtrigger OPTIONS\n"
  194. " -v print the list of devices while running\n"
  195. " -n do not actually trigger the events\n"
  196. " -h print this text\n"
  197. "\n");
  198. goto exit;
  199. default:
  200. goto exit;
  201. }
  202. }
  203. /* if we have /sys/subsystem, forget all the old stuff */
  204. scan_subdir("/sys/bus", "/devices", false, 1);
  205. scan_subdir("/sys/class", NULL, false, 1);
  206. /* scan "block" if it isn't a "class" */
  207. if (stat("/sys/class/block", &statbuf) != 0)
  208. scan_subdir("/sys/block", NULL, true, 1);
  209. exit:
  210. closelog();
  211. return 0;
  212. }