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/hotplug.h> 25 #include <sys/wait.h> 26 27 #include <err.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <libgen.h> 31 #include <signal.h> 32 #include <stdarg.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <syslog.h> 37 #include <unistd.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 extern char *__progname; 49 50 volatile sig_atomic_t quit = 0; 51 const char *device = _PATH_DEV_HOTPLUG; 52 int devfd = -1; 53 54 void exec_script(const char *, int, char *); 55 void sigchild(int); 56 void sigquit(int); 57 __dead void usage(void); 58 59 int 60 main(int argc, char *argv[]) 61 { 62 int ch; 63 struct sigaction sact; 64 struct hotplug_event he; 65 66 while ((ch = getopt(argc, argv, "d:")) != -1) 67 switch (ch) { 68 case 'd': 69 device = optarg; 70 break; 71 case '?': 72 default: 73 usage(); 74 /* NOTREACHED */ 75 } 76 77 argc -= optind; 78 argv += optind; 79 if (argc > 0) 80 usage(); 81 82 if ((devfd = open(device, O_RDONLY)) == -1) 83 err(1, "%s", device); 84 85 bzero(&sact, sizeof(sact)); 86 sigemptyset(&sact.sa_mask); 87 sact.sa_flags = 0; 88 sact.sa_handler = sigquit; 89 sigaction(SIGINT, &sact, NULL); 90 sigaction(SIGQUIT, &sact, NULL); 91 sigaction(SIGTERM, &sact, NULL); 92 sact.sa_handler = SIG_IGN; 93 sigaction(SIGHUP, &sact, NULL); 94 sact.sa_handler = sigchild; 95 sact.sa_flags = SA_NOCLDSTOP; 96 sigaction(SIGCHLD, &sact, NULL); 97 98 openlog(_LOG_TAG, _LOG_OPT, _LOG_FACILITY); 99 if (daemon(0, 0) == -1) 100 err(1, "daemon"); 101 102 syslog(LOG_INFO, "started"); 103 104 while (!quit) { 105 if (read(devfd, &he, sizeof(he)) == -1) { 106 if (errno == EINTR) 107 /* ignore */ 108 continue; 109 syslog(LOG_ERR, "read: %m"); 110 exit(1); 111 } 112 113 switch (he.he_type) { 114 case HOTPLUG_DEVAT: 115 syslog(LOG_INFO, "%s attached, class %d", 116 he.he_devname, he.he_devclass); 117 exec_script(_PATH_ETC_HOTPLUG_ATTACH, he.he_devclass, 118 he.he_devname); 119 break; 120 case HOTPLUG_DEVDT: 121 syslog(LOG_INFO, "%s detached, class %d", 122 he.he_devname, he.he_devclass); 123 exec_script(_PATH_ETC_HOTPLUG_DETACH, he.he_devclass, 124 he.he_devname); 125 break; 126 default: 127 syslog(LOG_NOTICE, "unknown event (0x%x)", he.he_type); 128 } 129 } 130 131 syslog(LOG_INFO, "terminated"); 132 133 closelog(); 134 close(devfd); 135 136 return (0); 137 } 138 139 void 140 exec_script(const char *file, int class, char *name) 141 { 142 char strclass[8]; 143 pid_t pid; 144 145 snprintf(strclass, sizeof(strclass), "%d", class); 146 147 if (access(file, X_OK | R_OK)) { 148 syslog(LOG_ERR, "could not access %s", file); 149 return; 150 } 151 152 if ((pid = fork()) == -1) { 153 syslog(LOG_ERR, "fork: %m"); 154 return; 155 } 156 if (pid == 0) { 157 /* child process */ 158 execl(file, basename(file), strclass, name, (char *)NULL); 159 syslog(LOG_ERR, "execl %s: %m", file); 160 _exit(1); 161 /* NOTREACHED */ 162 } 163 } 164 165 /* ARGSUSED */ 166 void 167 sigchild(int signum __unused) 168 { 169 struct syslog_data sdata = SYSLOG_DATA_INIT; 170 int saved_errno, status; 171 pid_t pid; 172 173 saved_errno = errno; 174 175 sdata.log_tag = _LOG_TAG; 176 sdata.log_fac = _LOG_FACILITY; 177 sdata.log_stat = _LOG_OPT; 178 179 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) { 180 if (pid == -1) { 181 if (errno == EINTR) 182 continue; 183 if (errno != ECHILD) 184 syslog_r(LOG_ERR, &sdata, "waitpid: %m"); 185 break; 186 } 187 188 if (WIFEXITED(status)) { 189 if (WEXITSTATUS(status) != 0) { 190 syslog_r(LOG_NOTICE, &sdata, 191 "child exit status: %d", 192 WEXITSTATUS(status)); 193 } 194 } else { 195 syslog_r(LOG_NOTICE, &sdata, 196 "child is terminated abnormally"); 197 } 198 } 199 200 errno = saved_errno; 201 } 202 203 /* ARGSUSED */ 204 void 205 sigquit(int signum __unused) 206 { 207 quit = 1; 208 } 209 210 __dead void 211 usage(void) 212 { 213 fprintf(stderr, "usage: %s [-d device]\n", __progname); 214 exit(1); 215 } 216