1 /* $OpenBSD: hotplugd.c,v 1.11 2009/06/26 01:06:04 kurt Exp $ */ 2 /* 3 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * Devices hot plugging daemon. 20 */ 21 22 #include <sys/types.h> 23 #include <sys/device.h> 24 #include <sys/wait.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <libgen.h> 30 #include <signal.h> 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <syslog.h> 36 #include <unistd.h> 37 #include <devattr.h> 38 39 #define _PATH_DEV_HOTPLUG "/dev/hotplug" 40 #define _PATH_ETC_HOTPLUG "/etc/hotplug" 41 #define _PATH_ETC_HOTPLUG_ATTACH _PATH_ETC_HOTPLUG "/attach" 42 #define _PATH_ETC_HOTPLUG_DETACH _PATH_ETC_HOTPLUG "/detach" 43 #define _LOG_TAG "hotplugd" 44 #define _LOG_FACILITY LOG_DAEMON 45 #define _LOG_OPT (LOG_NDELAY | LOG_PID) 46 47 enum obsd_devclass { 48 DV_DULL, /* generic, no special info */ 49 DV_CPU, /* CPU (carries resource utilization) */ 50 DV_DISK, /* disk drive (label, etc) */ 51 DV_IFNET, /* network interface */ 52 DV_TAPE, /* tape device */ 53 DV_TTY /* serial line interface (???) */ 54 }; 55 56 extern char *__progname; 57 58 volatile sig_atomic_t quit = 0; 59 60 void exec_script(const char *, int, const char *); 61 void sigchild(int); 62 void sigquit(int); 63 __dead2 void usage(void); 64 65 int 66 main(int argc, char *argv[]) 67 { 68 int ch, class, ret; 69 struct sigaction sact; 70 struct udev *udev; 71 struct udev_enumerate *udev_enum; 72 struct udev_list_entry *udev_le, *udev_le_first; 73 struct udev_monitor *udev_monitor; 74 struct udev_device *udev_dev; 75 enum obsd_devclass devclass; 76 const char *prop; 77 78 while ((ch = getopt(argc, argv, "?")) != -1) 79 switch (ch) { 80 case '?': 81 default: 82 usage(); 83 /* NOTREACHED */ 84 } 85 86 argc -= optind; 87 argv += optind; 88 if (argc > 0) 89 usage(); 90 91 udev = udev_new(); 92 if (udev == NULL) 93 err(1, "udev_new"); 94 95 bzero(&sact, sizeof(sact)); 96 sigemptyset(&sact.sa_mask); 97 sact.sa_flags = 0; 98 sact.sa_handler = sigquit; 99 sigaction(SIGINT, &sact, NULL); 100 sigaction(SIGQUIT, &sact, NULL); 101 sigaction(SIGTERM, &sact, NULL); 102 sact.sa_handler = SIG_IGN; 103 sigaction(SIGHUP, &sact, NULL); 104 sact.sa_handler = sigchild; 105 sact.sa_flags = SA_NOCLDSTOP; 106 sigaction(SIGCHLD, &sact, NULL); 107 108 openlog(_LOG_TAG, _LOG_OPT, _LOG_FACILITY); 109 110 if (daemon(0, 0) == -1) 111 err(1, "daemon"); 112 113 syslog(LOG_INFO, "started"); 114 115 udev_enum = udev_enumerate_new(udev); 116 if (udev_enum == NULL) 117 err(1, "udev_enumerate_new"); 118 119 ret = udev_enumerate_scan_devices(udev_enum); 120 if (ret != 0) 121 err(1, "udev_enumerate_scan_device ret = %d", ret); 122 123 udev_le_first = udev_enumerate_get_list_entry(udev_enum); 124 if (udev_le_first == NULL) 125 err(1, "udev_enumerate_get_list_entry error"); 126 127 udev_list_entry_foreach(udev_le, udev_le_first) { 128 udev_dev = udev_list_entry_get_device(udev_le); 129 130 class = atoi(udev_device_get_property_value(udev_dev, "devtype")); 131 devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL))); 132 133 syslog(LOG_INFO, "%s attached, class %d", 134 udev_device_get_devnode(udev_dev), devclass); 135 exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass, 136 udev_device_get_devnode(udev_dev)); 137 } 138 139 udev_enumerate_unref(udev_enum); 140 udev_monitor = udev_monitor_new(udev); 141 142 ret = udev_monitor_enable_receiving(udev_monitor); 143 if (ret != 0) 144 err(1, "udev_monitor_enable_receiving ret = %d", ret); 145 146 while (!quit) { 147 if ((udev_dev = udev_monitor_receive_device(udev_monitor)) == NULL) { 148 syslog(LOG_ERR, "read: %m"); 149 exit(1); 150 } 151 152 prop = udev_device_get_action(udev_dev); 153 class = atoi(udev_device_get_property_value(udev_dev, "devtype")); 154 devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL))); 155 156 if (strcmp(prop, "attach") == 0) { 157 syslog(LOG_INFO, "%s attached, class %d", 158 udev_device_get_devnode(udev_dev), devclass); 159 exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass, 160 udev_device_get_devnode(udev_dev)); 161 } else if (strcmp(prop, "detach") == 0) { 162 syslog(LOG_INFO, "%s detached, class %d", 163 udev_device_get_devnode(udev_dev), devclass); 164 exec_script(_PATH_ETC_HOTPLUG_DETACH, devclass, 165 udev_device_get_devnode(udev_dev)); 166 } else { 167 syslog(LOG_NOTICE, "unknown event (%s)", prop); 168 } 169 } 170 171 syslog(LOG_INFO, "terminated"); 172 173 closelog(); 174 175 udev_monitor_unref(udev_monitor); 176 udev_unref(udev); 177 178 return (0); 179 } 180 181 void 182 exec_script(const char *file, int class, const char *name) 183 { 184 char strclass[8]; 185 pid_t pid; 186 187 snprintf(strclass, sizeof(strclass), "%d", class); 188 189 if (access(file, X_OK | R_OK)) { 190 syslog(LOG_ERR, "could not access %s", file); 191 return; 192 } 193 194 if ((pid = fork()) == -1) { 195 syslog(LOG_ERR, "fork: %m"); 196 return; 197 } 198 if (pid == 0) { 199 /* child process */ 200 execl(file, basename(strdup(file)), strclass, name, NULL); 201 syslog(LOG_ERR, "execl %s: %m", file); 202 _exit(1); 203 /* NOTREACHED */ 204 } 205 } 206 207 /* ARGSUSED */ 208 void 209 sigchild(int signum __unused) 210 { 211 int saved_errno, status; 212 pid_t pid; 213 214 saved_errno = errno; 215 216 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) { 217 if (pid == -1) { 218 if (errno == EINTR) 219 continue; 220 if (errno != ECHILD) 221 /* XXX syslog_r() */ 222 syslog(LOG_ERR, "waitpid: %m"); 223 break; 224 } 225 226 if (WIFEXITED(status)) { 227 if (WEXITSTATUS(status) != 0) { 228 /* XXX syslog_r() */ 229 syslog(LOG_NOTICE, "child exit status: %d", 230 WEXITSTATUS(status)); 231 } 232 } else { 233 /* XXX syslog_r() */ 234 syslog(LOG_NOTICE, "child is terminated abnormally"); 235 } 236 } 237 238 errno = saved_errno; 239 } 240 241 /* ARGSUSED */ 242 void 243 sigquit(int signum __unused) 244 { 245 quit = 1; 246 } 247 248 __dead2 void 249 usage(void) 250 { 251 fprintf(stderr, "usage: %s [-d device]\n", __progname); 252 exit(1); 253 } 254