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