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