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 #include "compat.h" 39 40 #define _PATH_DEV_HOTPLUG "/dev/hotplug" 41 #define _PATH_ETC_HOTPLUG "/etc/hotplug" 42 #define _PATH_ETC_HOTPLUG_ATTACH _PATH_ETC_HOTPLUG "/attach" 43 #define _PATH_ETC_HOTPLUG_DETACH _PATH_ETC_HOTPLUG "/detach" 44 #define _LOG_TAG "hotplugd" 45 #define _LOG_FACILITY LOG_DAEMON 46 #define _LOG_OPT (LOG_NDELAY | LOG_PID) 47 48 enum obsd_devclass { 49 DV_DULL, /* generic, no special info */ 50 DV_CPU, /* CPU (carries resource utilization) */ 51 DV_DISK, /* disk drive (label, etc) */ 52 DV_IFNET, /* network interface */ 53 DV_TAPE, /* tape device */ 54 DV_TTY /* serial line interface (???) */ 55 }; 56 57 extern char *__progname; 58 59 volatile sig_atomic_t quit = 0; 60 61 void exec_script(const char *, int, const char *); 62 void sigchild(int); 63 void sigquit(int); 64 __dead void usage(void); 65 66 int 67 main(int argc, char *argv[]) 68 { 69 int ch, class, ret; 70 struct sigaction sact; 71 struct udev *udev; 72 struct udev_enumerate *udev_enum; 73 struct udev_list_entry *udev_le, *udev_le_first; 74 struct udev_monitor *udev_monitor; 75 struct udev_device *udev_dev; 76 enum obsd_devclass devclass; 77 const char *prop; 78 79 while ((ch = getopt(argc, argv, "?")) != -1) 80 switch (ch) { 81 case '?': 82 default: 83 usage(); 84 /* NOTREACHED */ 85 } 86 87 argc -= optind; 88 argv += optind; 89 if (argc > 0) 90 usage(); 91 92 udev = udev_new(); 93 if (udev == NULL) 94 err(1, "udev_new"); 95 96 bzero(&sact, sizeof(sact)); 97 sigemptyset(&sact.sa_mask); 98 sact.sa_flags = 0; 99 sact.sa_handler = sigquit; 100 sigaction(SIGINT, &sact, NULL); 101 sigaction(SIGQUIT, &sact, NULL); 102 sigaction(SIGTERM, &sact, NULL); 103 sact.sa_handler = SIG_IGN; 104 sigaction(SIGHUP, &sact, NULL); 105 sact.sa_handler = sigchild; 106 sact.sa_flags = SA_NOCLDSTOP; 107 sigaction(SIGCHLD, &sact, NULL); 108 109 openlog(_LOG_TAG, _LOG_OPT, _LOG_FACILITY); 110 111 if (daemon(0, 0) == -1) 112 err(1, "daemon"); 113 114 syslog(LOG_INFO, "started"); 115 116 udev_enum = udev_enumerate_new(udev); 117 if (udev_enum == NULL) 118 err(1, "udev_enumerate_new"); 119 120 ret = udev_enumerate_scan_devices(udev_enum); 121 if (ret != 0) 122 err(1, "udev_enumerate_scan_device ret = %d", ret); 123 124 udev_le_first = udev_enumerate_get_list_entry(udev_enum); 125 if (udev_le_first == NULL) 126 err(1, "udev_enumerate_get_list_entry error"); 127 128 udev_list_entry_foreach(udev_le, udev_le_first) { 129 udev_dev = udev_list_entry_get_device(udev_le); 130 131 class = atoi(udev_device_get_property_value(udev_dev, "devtype")); 132 devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL))); 133 134 syslog(LOG_INFO, "%s attached, class %d", 135 udev_device_get_devnode(udev_dev), devclass); 136 exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass, 137 udev_device_get_devnode(udev_dev)); 138 } 139 140 udev_enumerate_unref(udev_enum); 141 udev_monitor = udev_monitor_new(udev); 142 143 ret = udev_monitor_enable_receiving(udev_monitor); 144 if (ret != 0) 145 err(1, "udev_monitor_enable_receiving ret = %d", ret); 146 147 while (!quit) { 148 if ((udev_dev = udev_monitor_receive_device(udev_monitor)) == NULL) { 149 syslog(LOG_ERR, "read: %m"); 150 exit(1); 151 } 152 153 prop = udev_device_get_action(udev_dev); 154 class = atoi(udev_device_get_property_value(udev_dev, "devtype")); 155 devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL))); 156 157 if (strcmp(prop, "attach") == 0) { 158 syslog(LOG_INFO, "%s attached, class %d", 159 udev_device_get_devnode(udev_dev), devclass); 160 exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass, 161 udev_device_get_devnode(udev_dev)); 162 } else if (strcmp(prop, "detach") == 0) { 163 syslog(LOG_INFO, "%s detached, class %d", 164 udev_device_get_devnode(udev_dev), devclass); 165 exec_script(_PATH_ETC_HOTPLUG_DETACH, devclass, 166 udev_device_get_devnode(udev_dev)); 167 } else { 168 syslog(LOG_NOTICE, "unknown event (%s)", prop); 169 } 170 } 171 172 syslog(LOG_INFO, "terminated"); 173 174 closelog(); 175 176 udev_monitor_unref(udev_monitor); 177 udev_unref(udev); 178 179 return (0); 180 } 181 182 void 183 exec_script(const char *file, int class, const char *name) 184 { 185 char strclass[8]; 186 pid_t pid; 187 188 snprintf(strclass, sizeof(strclass), "%d", class); 189 190 if (access(file, X_OK | R_OK)) { 191 syslog(LOG_ERR, "could not access %s", file); 192 return; 193 } 194 195 if ((pid = fork()) == -1) { 196 syslog(LOG_ERR, "fork: %m"); 197 return; 198 } 199 if (pid == 0) { 200 /* child process */ 201 execl(file, basename(file), strclass, name, (char *)NULL); 202 syslog(LOG_ERR, "execl %s: %m", file); 203 _exit(1); 204 /* NOTREACHED */ 205 } 206 } 207 208 /* ARGSUSED */ 209 void 210 sigchild(int signum __unused) 211 { 212 struct syslog_data sdata = SYSLOG_DATA_INIT; 213 int saved_errno, status; 214 pid_t pid; 215 216 saved_errno = errno; 217 218 sdata.log_tag = _LOG_TAG; 219 sdata.log_fac = _LOG_FACILITY; 220 sdata.log_stat = _LOG_OPT; 221 222 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) { 223 if (pid == -1) { 224 if (errno == EINTR) 225 continue; 226 if (errno != ECHILD) 227 syslog_r(LOG_ERR, &sdata, "waitpid: %m"); 228 break; 229 } 230 231 if (WIFEXITED(status)) { 232 if (WEXITSTATUS(status) != 0) { 233 syslog_r(LOG_NOTICE, &sdata, 234 "child exit status: %d", 235 WEXITSTATUS(status)); 236 } 237 } else { 238 syslog_r(LOG_NOTICE, &sdata, 239 "child is terminated abnormally"); 240 } 241 } 242 243 errno = saved_errno; 244 } 245 246 /* ARGSUSED */ 247 void 248 sigquit(int signum __unused) 249 { 250 quit = 1; 251 } 252 253 __dead void 254 usage(void) 255 { 256 fprintf(stderr, "usage: %s [-d device]\n", __progname); 257 exit(1); 258 } 259