1 /* $NetBSD: devpubd.c,v 1.4 2015/02/15 21:46:49 christos 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.4 2015/02/15 21:46:49 christos 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 #include <sys/poll.h> 47 48 #include <assert.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <syslog.h> 56 #include <unistd.h> 57 58 static int drvctl_fd = -1; 59 static const char devpubd_script[] = DEVPUBD_RUN_HOOKS; 60 61 struct devpubd_probe_event { 62 char *device; 63 TAILQ_ENTRY(devpubd_probe_event) entries; 64 }; 65 66 static TAILQ_HEAD(, devpubd_probe_event) devpubd_probe_events; 67 68 #define DEVPUBD_ATTACH_EVENT "device-attach" 69 #define DEVPUBD_DETACH_EVENT "device-detach" 70 71 __dead static void 72 devpubd_exec(const char *path, char * const *argv) 73 { 74 int error; 75 76 error = execv(path, argv); 77 if (error) { 78 syslog(LOG_ERR, "couldn't exec '%s': %m", path); 79 exit(EXIT_FAILURE); 80 } 81 82 exit(EXIT_SUCCESS); 83 } 84 85 static void 86 devpubd_eventhandler(const char *event, const char **device) 87 { 88 char **argv; 89 pid_t pid; 90 int status; 91 size_t i, ndevs; 92 93 for (ndevs = 0, i = 0; device[i] != NULL; i++) { 94 ++ndevs; 95 syslog(LOG_DEBUG, "event = '%s', device = '%s'", event, 96 device[i]); 97 } 98 99 argv = calloc(3 + ndevs, sizeof(*argv)); 100 argv[0] = __UNCONST(devpubd_script); 101 argv[1] = __UNCONST(event); 102 for (i = 0; i < ndevs; i++) { 103 argv[2 + i] = __UNCONST(device[i]); 104 } 105 argv[2 + i] = NULL; 106 107 pid = fork(); 108 switch (pid) { 109 case -1: 110 syslog(LOG_ERR, "fork failed: %m"); 111 break; 112 case 0: 113 devpubd_exec(devpubd_script, argv); 114 /* NOTREACHED */ 115 default: 116 if (waitpid(pid, &status, 0) == -1) { 117 syslog(LOG_ERR, "waitpid(%d) failed: %m", pid); 118 break; 119 } 120 if (WIFEXITED(status) && WEXITSTATUS(status)) { 121 syslog(LOG_WARNING, "%s exited with status %d", 122 devpubd_script, WEXITSTATUS(status)); 123 } else if (WIFSIGNALED(status)) { 124 syslog(LOG_WARNING, "%s received signal %d", 125 devpubd_script, WTERMSIG(status)); 126 } 127 break; 128 } 129 130 free(argv); 131 } 132 133 __dead static void 134 devpubd_eventloop(void) 135 { 136 const char *event, *device[2]; 137 prop_dictionary_t ev; 138 int res; 139 140 assert(drvctl_fd != -1); 141 142 device[1] = NULL; 143 144 for (;;) { 145 res = prop_dictionary_recv_ioctl(drvctl_fd, DRVGETEVENT, &ev); 146 if (res) 147 err(EXIT_FAILURE, "DRVGETEVENT failed"); 148 prop_dictionary_get_cstring_nocopy(ev, "event", &event); 149 prop_dictionary_get_cstring_nocopy(ev, "device", &device[0]); 150 151 printf("%s: event='%s', device='%s'\n", __func__, 152 event, device[0]); 153 154 devpubd_eventhandler(event, device); 155 156 prop_object_release(ev); 157 } 158 } 159 160 static void 161 devpubd_probe(const char *device) 162 { 163 struct devlistargs laa; 164 size_t len, children, n; 165 void *p; 166 int error; 167 168 assert(drvctl_fd != -1); 169 170 memset(&laa, 0, sizeof(laa)); 171 if (device) 172 strlcpy(laa.l_devname, device, sizeof(laa.l_devname)); 173 174 /* Get the child device count for this device */ 175 error = ioctl(drvctl_fd, DRVLISTDEV, &laa); 176 if (error) { 177 syslog(LOG_ERR, "DRVLISTDEV failed: %m"); 178 return; 179 } 180 181 child_count_changed: 182 /* If this device has no children, return */ 183 if (laa.l_children == 0) 184 return; 185 186 /* Allocate a buffer large enough to hold the child device names */ 187 p = laa.l_childname; 188 children = laa.l_children; 189 190 len = children * sizeof(laa.l_childname[0]); 191 laa.l_childname = realloc(laa.l_childname, len); 192 if (laa.l_childname == NULL) { 193 syslog(LOG_ERR, "couldn't allocate %zu bytes", len); 194 laa.l_childname = p; 195 goto done; 196 } 197 198 /* Get a list of child devices */ 199 error = ioctl(drvctl_fd, DRVLISTDEV, &laa); 200 if (error) { 201 syslog(LOG_ERR, "DRVLISTDEV failed: %m"); 202 goto done; 203 } 204 205 /* If the child count changed between DRVLISTDEV calls, retry */ 206 if (children != laa.l_children) 207 goto child_count_changed; 208 209 /* 210 * For each child device, queue an attach event and 211 * then scan each one for additional devices. 212 */ 213 for (n = 0; n < laa.l_children; n++) { 214 struct devpubd_probe_event *ev = calloc(1, sizeof(*ev)); 215 ev->device = strdup(laa.l_childname[n]); 216 TAILQ_INSERT_TAIL(&devpubd_probe_events, ev, entries); 217 } 218 for (n = 0; n < laa.l_children; n++) 219 devpubd_probe(laa.l_childname[n]); 220 221 done: 222 free(laa.l_childname); 223 return; 224 } 225 226 static void 227 devpubd_init(void) 228 { 229 struct devpubd_probe_event *ev; 230 const char **devs; 231 size_t ndevs, i; 232 233 TAILQ_INIT(&devpubd_probe_events); 234 devpubd_probe(NULL); 235 ndevs = 0; 236 TAILQ_FOREACH(ev, &devpubd_probe_events, entries) { 237 ++ndevs; 238 } 239 devs = calloc(ndevs + 1, sizeof(*devs)); 240 i = 0; 241 TAILQ_FOREACH(ev, &devpubd_probe_events, entries) { 242 devs[i++] = ev->device; 243 } 244 devpubd_eventhandler(DEVPUBD_ATTACH_EVENT, devs); 245 free(devs); 246 while ((ev = TAILQ_FIRST(&devpubd_probe_events)) != NULL) { 247 TAILQ_REMOVE(&devpubd_probe_events, ev, entries); 248 free(ev->device); 249 free(ev); 250 } 251 } 252 253 __dead static void 254 usage(void) 255 { 256 fprintf(stderr, "usage: %s [-f]\n", getprogname()); 257 exit(EXIT_FAILURE); 258 } 259 260 int 261 main(int argc, char *argv[]) 262 { 263 bool fflag = false; 264 int ch; 265 266 setprogname(argv[0]); 267 268 while ((ch = getopt(argc, argv, "fh")) != -1) { 269 switch (ch) { 270 case 'f': 271 fflag = true; 272 break; 273 case 'h': 274 default: 275 usage(); 276 /* NOTREACHED */ 277 } 278 } 279 argc -= optind; 280 argv += optind; 281 282 if (argc) 283 usage(); 284 /* NOTREACHED */ 285 286 drvctl_fd = open(DRVCTLDEV, O_RDWR); 287 if (drvctl_fd == -1) 288 err(EXIT_FAILURE, "couldn't open " DRVCTLDEV); 289 290 /* Look for devices that are already present */ 291 devpubd_init(); 292 293 if (!fflag) { 294 if (daemon(0, 0) == -1) { 295 err(EXIT_FAILURE, "couldn't fork"); 296 } 297 } 298 299 devpubd_eventloop(); 300 301 return EXIT_SUCCESS; 302 } 303