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