1/* $NetBSD: devpubd.c,v 1.1 2011/08/29 11:38:48 mrg 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\ 38Jared D. McNeill <jmcneill@invisible.ca>. All rights reserved."); 39__RCSID("$NetBSD: devpubd.c,v 1.1 2011/08/29 11:38:48 mrg Exp $"); 40 41#include <sys/types.h> 42#include <sys/ioctl.h> 43#include <sys/drvctlio.h> 44#include <sys/wait.h> 45#include <sys/poll.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 57static int drvctl_fd = -1; 58static const char devpubd_script[] = DEVPUBD_RUN_HOOKS; 59 60#define DEVPUBD_ATTACH_EVENT "device-attach" 61#define DEVPUBD_DETACH_EVENT "device-detach" 62 63__dead static void 64devpubd_exec(const char *path, const char *event, const char *device) 65{ 66 int error; 67 68 error = execl(path, path, event, device, NULL); 69 if (error) { 70 syslog(LOG_ERR, "couldn't exec '%s %s %s': %m", 71 path, event, device); 72 exit(EXIT_FAILURE); 73 } 74 75 exit(EXIT_SUCCESS); 76} 77 78static void 79devpubd_eventhandler(const char *event, const char *device) 80{ 81 pid_t pid; 82 int status; 83 84 syslog(LOG_DEBUG, "event = '%s', device = '%s'", event, device); 85 86 pid = fork(); 87 switch (pid) { 88 case -1: 89 syslog(LOG_ERR, "fork failed: %m"); 90 break; 91 case 0: 92 devpubd_exec(devpubd_script, event, device); 93 /* NOTREACHED */ 94 default: 95 if (waitpid(pid, &status, 0) == -1) { 96 syslog(LOG_ERR, "waitpid(%d) failed: %m", pid); 97 break; 98 } 99 if (WIFEXITED(status) && WEXITSTATUS(status)) { 100 syslog(LOG_WARNING, "%s exited with status %d", 101 devpubd_script, WEXITSTATUS(status)); 102 } else if (WIFSIGNALED(status)) { 103 syslog(LOG_WARNING, "%s received signal %d", 104 devpubd_script, WTERMSIG(status)); 105 } 106 break; 107 } 108} 109 110__dead static void 111devpubd_eventloop(void) 112{ 113 const char *event, *device; 114 prop_dictionary_t ev; 115 int res; 116 117 assert(drvctl_fd != -1); 118 119 for (;;) { 120 res = prop_dictionary_recv_ioctl(drvctl_fd, DRVGETEVENT, &ev); 121 if (res) 122 err(EXIT_FAILURE, "DRVGETEVENT failed"); 123 prop_dictionary_get_cstring_nocopy(ev, "event", &event); 124 prop_dictionary_get_cstring_nocopy(ev, "device", &device); 125 126 printf("%s: event='%s', device='%s'\n", __func__, 127 event, device); 128 129 devpubd_eventhandler(event, device); 130 131 prop_object_release(ev); 132 } 133} 134 135static void 136devpubd_probe(const char *device) 137{ 138 struct devlistargs laa; 139 size_t len, children, n; 140 void *p; 141 int error; 142 143 assert(drvctl_fd != -1); 144 145 memset(&laa, 0, sizeof(laa)); 146 if (device) 147 strlcpy(laa.l_devname, device, sizeof(laa.l_devname)); 148 149 /* Get the child device count for this device */ 150 error = ioctl(drvctl_fd, DRVLISTDEV, &laa); 151 if (error) { 152 syslog(LOG_ERR, "DRVLISTDEV failed: %m"); 153 return; 154 } 155 156child_count_changed: 157 /* If this device has no children, return */ 158 if (laa.l_children == 0) 159 return; 160 161 /* Allocate a buffer large enough to hold the child device names */ 162 p = laa.l_childname; 163 children = laa.l_children; 164 165 len = children * sizeof(laa.l_childname[0]); 166 laa.l_childname = realloc(laa.l_childname, len); 167 if (laa.l_childname == NULL) { 168 syslog(LOG_ERR, "couldn't allocate %zu bytes", len); 169 laa.l_childname = p; 170 goto done; 171 } 172 173 /* Get a list of child devices */ 174 error = ioctl(drvctl_fd, DRVLISTDEV, &laa); 175 if (error) { 176 syslog(LOG_ERR, "DRVLISTDEV failed: %m"); 177 goto done; 178 } 179 180 /* If the child count changed between DRVLISTDEV calls, retry */ 181 if (children != laa.l_children) 182 goto child_count_changed; 183 184 /* 185 * For each child device, first post an attach event and 186 * then scan each one for additional devices. 187 */ 188 for (n = 0; n < laa.l_children; n++) 189 devpubd_eventhandler(DEVPUBD_ATTACH_EVENT, laa.l_childname[n]); 190 for (n = 0; n < laa.l_children; n++) 191 devpubd_probe(laa.l_childname[n]); 192 193done: 194 free(laa.l_childname); 195 return; 196} 197 198__dead static void 199usage(void) 200{ 201 fprintf(stderr, "usage: %s [-f]\n", getprogname()); 202 exit(EXIT_FAILURE); 203} 204 205int 206main(int argc, char *argv[]) 207{ 208 bool fflag = false; 209 int ch; 210 211 setprogname(argv[0]); 212 213 while ((ch = getopt(argc, argv, "fh")) != -1) { 214 switch (ch) { 215 case 'f': 216 fflag = true; 217 break; 218 case 'h': 219 default: 220 usage(); 221 /* NOTREACHED */ 222 } 223 } 224 argc -= optind; 225 argv += optind; 226 227 if (argc) 228 usage(); 229 /* NOTREACHED */ 230 231 drvctl_fd = open(DRVCTLDEV, O_RDWR); 232 if (drvctl_fd == -1) 233 err(EXIT_FAILURE, "couldn't open " DRVCTLDEV); 234 235 if (!fflag) { 236 if (daemon(0, 0) == -1) { 237 err(EXIT_FAILURE, "couldn't fork"); 238 } 239 } 240 241 /* Look for devices that are already present */ 242 devpubd_probe(NULL); 243 244 devpubd_eventloop(); 245 246 return EXIT_SUCCESS; 247} 248