1 /* $NetBSD: devpubd.c,v 1.6 2020/02/24 11:45:30 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the NetBSD 18 * Foundation, Inc. and its contributors. 19 * 4. Neither the name of The NetBSD Foundation nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 __COPYRIGHT("@(#) Copyright (c) 2011-2015\ 38 Jared D. McNeill <jmcneill@invisible.ca>. All rights reserved."); 39 __RCSID("$NetBSD: devpubd.c,v 1.6 2020/02/24 11:45:30 mlelstv Exp $"); 40 41 #include <sys/queue.h> 42 #include <sys/types.h> 43 #include <sys/ioctl.h> 44 #include <sys/drvctlio.h> 45 #include <sys/wait.h> 46 47 #include <assert.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <syslog.h> 55 #include <unistd.h> 56 57 static int drvctl_fd = -1; 58 static const char devpubd_script[] = DEVPUBD_RUN_HOOKS; 59 60 struct devpubd_probe_event { 61 char *device; 62 TAILQ_ENTRY(devpubd_probe_event) entries; 63 }; 64 65 static TAILQ_HEAD(, devpubd_probe_event) devpubd_probe_events; 66 67 #define DEVPUBD_ATTACH_EVENT "device-attach" 68 #define DEVPUBD_DETACH_EVENT "device-detach" 69 70 __dead static void 71 devpubd_exec(const char *path, char * const *argv) 72 { 73 int error; 74 75 error = execv(path, argv); 76 if (error) { 77 syslog(LOG_ERR, "couldn't exec '%s': %m", path); 78 exit(EXIT_FAILURE); 79 } 80 81 exit(EXIT_SUCCESS); 82 } 83 84 static void 85 devpubd_eventhandler(const char *event, const char **device) 86 { 87 char **argv; 88 pid_t pid; 89 int status; 90 size_t i, ndevs; 91 92 for (ndevs = 0, i = 0; device[i] != NULL; i++) { 93 ++ndevs; 94 syslog(LOG_DEBUG, "event = '%s', device = '%s'", event, 95 device[i]); 96 } 97 98 argv = calloc(3 + ndevs, sizeof(*argv)); 99 argv[0] = __UNCONST(devpubd_script); 100 argv[1] = __UNCONST(event); 101 for (i = 0; i < ndevs; i++) { 102 argv[2 + i] = __UNCONST(device[i]); 103 } 104 argv[2 + i] = NULL; 105 106 pid = fork(); 107 switch (pid) { 108 case -1: 109 syslog(LOG_ERR, "fork failed: %m"); 110 break; 111 case 0: 112 devpubd_exec(devpubd_script, argv); 113 /* NOTREACHED */ 114 default: 115 if (waitpid(pid, &status, 0) == -1) { 116 syslog(LOG_ERR, "waitpid(%d) failed: %m", pid); 117 break; 118 } 119 if (WIFEXITED(status) && WEXITSTATUS(status)) { 120 syslog(LOG_WARNING, "%s exited with status %d", 121 devpubd_script, WEXITSTATUS(status)); 122 } else if (WIFSIGNALED(status)) { 123 syslog(LOG_WARNING, "%s received signal %d", 124 devpubd_script, WTERMSIG(status)); 125 } 126 break; 127 } 128 129 free(argv); 130 } 131 132 __dead static void 133 devpubd_eventloop(void) 134 { 135 const char *event, *device[2]; 136 prop_dictionary_t ev; 137 int res; 138 139 assert(drvctl_fd != -1); 140 141 device[1] = NULL; 142 143 for (;;) { 144 res = prop_dictionary_recv_ioctl(drvctl_fd, DRVGETEVENT, &ev); 145 if (res) 146 err(EXIT_FAILURE, "DRVGETEVENT failed"); 147 prop_dictionary_get_cstring_nocopy(ev, "event", &event); 148 prop_dictionary_get_cstring_nocopy(ev, "device", &device[0]); 149 150 printf("%s: event='%s', device='%s'\n", __func__, 151 event, device[0]); 152 153 devpubd_eventhandler(event, device); 154 155 prop_object_release(ev); 156 } 157 } 158 159 static void 160 devpubd_probe(const char *device) 161 { 162 struct devlistargs laa; 163 size_t len, children, n; 164 void *p; 165 int error; 166 167 assert(drvctl_fd != -1); 168 169 memset(&laa, 0, sizeof(laa)); 170 if (device) 171 strlcpy(laa.l_devname, device, sizeof(laa.l_devname)); 172 173 /* Get the child device count for this device */ 174 error = ioctl(drvctl_fd, DRVLISTDEV, &laa); 175 if (error) { 176 syslog(LOG_ERR, "DRVLISTDEV failed: %m"); 177 return; 178 } 179 180 child_count_changed: 181 /* If this device has no children, return */ 182 if (laa.l_children == 0) 183 return; 184 185 /* Allocate a buffer large enough to hold the child device names */ 186 p = laa.l_childname; 187 children = laa.l_children; 188 189 len = children * sizeof(laa.l_childname[0]); 190 laa.l_childname = realloc(laa.l_childname, len); 191 if (laa.l_childname == NULL) { 192 syslog(LOG_ERR, "couldn't allocate %zu bytes", len); 193 laa.l_childname = p; 194 goto done; 195 } 196 197 /* Get a list of child devices */ 198 error = ioctl(drvctl_fd, DRVLISTDEV, &laa); 199 if (error) { 200 syslog(LOG_ERR, "DRVLISTDEV failed: %m"); 201 goto done; 202 } 203 204 /* If the child count changed between DRVLISTDEV calls, retry */ 205 if (children != laa.l_children) 206 goto child_count_changed; 207 208 /* 209 * For each child device, queue an attach event and 210 * then scan each one for additional devices. 211 */ 212 for (n = 0; n < laa.l_children; n++) { 213 struct devpubd_probe_event *ev = calloc(1, sizeof(*ev)); 214 ev->device = strdup(laa.l_childname[n]); 215 TAILQ_INSERT_TAIL(&devpubd_probe_events, ev, entries); 216 } 217 for (n = 0; n < laa.l_children; n++) 218 devpubd_probe(laa.l_childname[n]); 219 220 done: 221 free(laa.l_childname); 222 return; 223 } 224 225 static void 226 devpubd_init(void) 227 { 228 struct devpubd_probe_event *ev; 229 const char **devs; 230 size_t ndevs, i; 231 232 TAILQ_INIT(&devpubd_probe_events); 233 devpubd_probe(NULL); 234 ndevs = 0; 235 TAILQ_FOREACH(ev, &devpubd_probe_events, entries) { 236 ++ndevs; 237 } 238 devs = calloc(ndevs + 1, sizeof(*devs)); 239 i = 0; 240 TAILQ_FOREACH(ev, &devpubd_probe_events, entries) { 241 devs[i++] = ev->device; 242 } 243 devpubd_eventhandler(DEVPUBD_ATTACH_EVENT, devs); 244 free(devs); 245 while ((ev = TAILQ_FIRST(&devpubd_probe_events)) != NULL) { 246 TAILQ_REMOVE(&devpubd_probe_events, ev, entries); 247 free(ev->device); 248 free(ev); 249 } 250 } 251 252 __dead static void 253 usage(void) 254 { 255 fprintf(stderr, "usage: %s [-1f]\n", getprogname()); 256 exit(EXIT_FAILURE); 257 } 258 259 int 260 main(int argc, char *argv[]) 261 { 262 bool fflag = false; 263 bool once = false; 264 int ch; 265 266 setprogname(argv[0]); 267 268 while ((ch = getopt(argc, argv, "1fh")) != -1) { 269 switch (ch) { 270 case '1': 271 fflag = true; 272 once = true; 273 break; 274 case 'f': 275 fflag = true; 276 break; 277 case 'h': 278 default: 279 usage(); 280 /* NOTREACHED */ 281 } 282 } 283 argc -= optind; 284 argv += optind; 285 286 if (argc) 287 usage(); 288 /* NOTREACHED */ 289 290 drvctl_fd = open(DRVCTLDEV, O_RDWR); 291 if (drvctl_fd == -1) 292 err(EXIT_FAILURE, "couldn't open " DRVCTLDEV); 293 294 /* Look for devices that are already present */ 295 devpubd_init(); 296 297 if (!fflag) { 298 if (daemon(0, 0) == -1) { 299 err(EXIT_FAILURE, "couldn't fork"); 300 } 301 } 302 303 if (!once) 304 devpubd_eventloop(); 305 306 return EXIT_SUCCESS; 307 } 308