devpubd.c revision 1.2 1 /* $NetBSD: devpubd.c,v 1.2 2011/09/16 15:42:56 joerg Exp $ */
2
3 /*-
4 * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) 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\
38 Jared D. McNeill <jmcneill (at) invisible.ca>. All rights reserved.");
39 __RCSID("$NetBSD: devpubd.c,v 1.2 2011/09/16 15:42:56 joerg 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
57 static int drvctl_fd = -1;
58 static 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
64 devpubd_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
78 static void
79 devpubd_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
111 devpubd_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
135 static void
136 devpubd_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
156 child_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
193 done:
194 free(laa.l_childname);
195 return;
196 }
197
198 __dead static void
199 usage(void)
200 {
201 fprintf(stderr, "usage: %s [-f]\n", getprogname());
202 exit(EXIT_FAILURE);
203 }
204
205 int
206 main(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