1 /* 2 * Copyright (c) 2010 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Alex Hornung <ahornung@gmail.com> 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 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #include <sys/types.h> 35 #include <sys/device.h> 36 #include <sys/wait.h> 37 #include <sys/socket.h> 38 #include <sys/ioctl.h> 39 #include <sys/poll.h> 40 #include <sys/queue.h> 41 #include <sys/un.h> 42 #include <cpu/inttypes.h> 43 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <libgen.h> 48 #include <regex.h> 49 #include <signal.h> 50 #include <stdarg.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <syslog.h> 55 #include <unistd.h> 56 #include <pthread.h> 57 58 #include <libprop/proplib.h> 59 #include <sys/udev.h> 60 #include "udevd.h" 61 62 int debugopt = 0; 63 64 static int udevfd; 65 66 extern pthread_mutex_t monitor_lock; 67 extern TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list; 68 extern TAILQ_HEAD(pdev_array_list_head, pdev_array_entry) pdev_array_list; 69 70 static void usage(void); 71 72 int match_dev_dict(prop_dictionary_t, prop_dictionary_t); 73 prop_dictionary_t find_dev_dict(int64_t, prop_dictionary_t, int *); 74 75 void udev_read_event(int); 76 prop_array_t udev_getdevs(int); 77 78 static 79 void 80 usage(void) 81 { 82 fprintf(stderr, "usage: udevd [-d]\n"); 83 exit(1); 84 } 85 86 int 87 match_dev_dict(prop_dictionary_t dict, prop_dictionary_t match_dict) 88 { 89 prop_number_t pn, pn2; 90 prop_string_t ps, ps2; 91 92 if (dict == NULL) 93 return 0; 94 95 if ((ps = prop_dictionary_get(dict, "name")) == NULL) 96 return 0; 97 if ((ps2 = prop_dictionary_get(match_dict, "name")) == NULL) 98 return 0; 99 if (!prop_string_equals(ps, ps2)) 100 return 0; 101 102 if ((pn = prop_dictionary_get(dict, "devnum")) == NULL) 103 return 0; 104 if ((pn2 = prop_dictionary_get(match_dict, "devnum")) == NULL) 105 return 0; 106 if (!prop_number_equals(pn, pn2)) 107 return 0; 108 109 if ((pn = prop_dictionary_get(dict, "kptr")) == NULL) 110 return 0; 111 if ((pn2 = prop_dictionary_get(match_dict, "kptr")) == NULL) 112 return 0; 113 if (!prop_number_equals(pn, pn2)) 114 return 0; 115 116 return 1; 117 } 118 119 prop_dictionary_t 120 find_dev_dict(int64_t generation, prop_dictionary_t match_dict, int *idx) 121 { 122 struct pdev_array_entry *pae; 123 prop_array_t pa; 124 prop_object_iterator_t iter; 125 prop_dictionary_t dict; 126 int i = 0; 127 128 if (generation == -1) 129 pae = pdev_array_entry_get_last(); 130 else 131 pae = pdev_array_entry_get(generation); 132 133 if (pae == NULL) 134 return NULL; 135 136 pa = pae->pdev_array; 137 138 iter = prop_array_iterator(pa); 139 if (iter == NULL) { 140 pdev_array_entry_unref(pae); 141 return NULL; 142 } 143 144 while ((dict = prop_object_iterator_next(iter)) != NULL) { 145 if (match_dev_dict(dict, match_dict)) 146 break; 147 ++i; 148 } 149 150 prop_object_iterator_release(iter); 151 152 if (idx != NULL) 153 *idx = i; 154 155 pdev_array_entry_unref(pae); 156 return dict; 157 } 158 159 void 160 udev_read_event(int fd) 161 { 162 struct pdev_array_entry *pae; 163 prop_dictionary_t dict, evdict, devdict; 164 prop_number_t pn; 165 prop_string_t ps; 166 prop_object_t po; 167 prop_array_t pa; 168 char *xml; 169 int n, idx, evtype; 170 size_t sz; 171 172 sz = 4096 * 1024; 173 174 xml = malloc(sz); /* 4 MB */ 175 again: 176 if ((n = read(fd, xml, sz)) <= 0) { 177 if (errno == ENOMEM) { 178 sz <<= 2; 179 realloc(xml, sz); 180 goto again; 181 } 182 free(xml); 183 return; 184 } 185 186 dict = prop_dictionary_internalize(xml); 187 free(xml); 188 if (dict == NULL) { 189 syslog(LOG_ERR, "internalization of xml failed"); 190 return; 191 } 192 193 pn = prop_dictionary_get(dict, "evtype"); 194 if (pn == NULL) { 195 syslog(LOG_ERR, "read_event: no key evtype"); 196 goto out; 197 } 198 199 evtype = prop_number_integer_value(pn); 200 201 evdict = prop_dictionary_get(dict, "evdict"); 202 if (evdict == NULL) { 203 syslog(LOG_ERR, "read_event: no key evdict"); 204 goto out; 205 } 206 207 switch (evtype) { 208 case UDEV_EVENT_ATTACH: 209 monitor_queue_event(dict); 210 pae = pdev_array_entry_get_last(); 211 pa = prop_array_copy(pae->pdev_array); 212 pdev_array_entry_unref(pae); 213 if (pa == NULL) 214 goto out; 215 prop_array_add(pa, evdict); 216 pdev_array_entry_insert(pa); 217 break; 218 219 case UDEV_EVENT_DETACH: 220 monitor_queue_event(dict); 221 if ((devdict = find_dev_dict(-1, evdict, &idx)) == NULL) 222 goto out; 223 pae = pdev_array_entry_get_last(); 224 pa = prop_array_copy(pae->pdev_array); 225 pdev_array_entry_unref(pae); 226 if (pa == NULL) 227 goto out; 228 prop_array_remove(pa, idx); 229 pdev_array_entry_insert(pa); 230 break; 231 232 case UDEV_EV_KEY_UPDATE: 233 if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL) 234 goto out; 235 if ((ps = prop_dictionary_get(evdict, "key")) == NULL) 236 goto out; 237 if ((po = prop_dictionary_get(evdict, "value")) == NULL) 238 goto out; 239 /* prop_object_retain(po); */ /* not necessary afaik */ 240 prop_dictionary_set(devdict, prop_string_cstring_nocopy(ps), po); 241 break; 242 243 case UDEV_EV_KEY_REMOVE: 244 if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL) 245 goto out; 246 if ((ps = prop_dictionary_get(evdict, "key")) == NULL) 247 goto out; 248 prop_dictionary_remove(devdict, prop_string_cstring_nocopy(ps)); 249 break; 250 251 default: 252 syslog(LOG_ERR, "read_event: unknown evtype %d", evtype); 253 } 254 255 out: 256 prop_object_release(dict); 257 return; 258 } 259 260 prop_array_t 261 udev_getdevs(int devfd) 262 { 263 prop_dictionary_t pd, rpd; 264 prop_string_t ps; 265 prop_array_t pa; 266 267 pd = prop_dictionary_create(); 268 if (pd == NULL) { 269 err(1, "prop_dictionary_create()"); 270 } 271 272 ps = prop_string_create_cstring("getdevs"); 273 if (ps == NULL) { 274 prop_object_release(pd); 275 err(1, "prop_string_create_cstring()"); 276 } 277 278 if (prop_dictionary_set(pd, "command", ps) == false) { 279 prop_object_release(ps); 280 prop_object_release(pd); 281 err(1, "prop_dictionary_set()"); 282 } 283 284 prop_object_release(ps); 285 286 /* Send dictionary to kernel space */ 287 if (prop_dictionary_sendrecv_ioctl(pd, devfd, UDEVPROP, &rpd) != 0) 288 err(1, "prop_array_recv_ioctl()"); 289 290 prop_object_release(pd); 291 292 pa = prop_dictionary_get(rpd, "array"); 293 if (pa == NULL) 294 goto out; 295 prop_object_retain(pa); 296 297 out: 298 prop_object_release(rpd); 299 return pa; 300 } 301 302 static void 303 killed(int sig __unused) 304 { 305 syslog(LOG_ERR, "udevd stopped"); 306 unlink("/var/run/udevd.pid"); 307 pdev_array_clean(); 308 } 309 310 int 311 ignore_signal(int signum) 312 { 313 struct sigaction act; 314 int ret; 315 316 act.sa_handler = SIG_IGN; 317 sigemptyset(&act.sa_mask); 318 act.sa_flags = 0; 319 320 ret = sigaction(signum, &act, NULL); 321 return ret; 322 } 323 324 static int 325 set_killed_signal(void) 326 { 327 struct sigaction act; 328 int ret; 329 330 act.sa_handler = killed; 331 sigemptyset(&act.sa_mask); 332 act.sa_flags = 0; 333 334 ret = sigaction(SIGTERM, &act, NULL); 335 return ret; 336 } 337 338 int main(int argc, char *argv[]) 339 { 340 int error __unused, i, r, s; 341 struct pollfd fds[NFDS]; 342 FILE *pidf; 343 int ch = 0; 344 345 while ((ch = getopt(argc, argv, "d")) != -1) { 346 switch(ch) { 347 case 'd': 348 debugopt = 1; 349 break; 350 default: 351 usage(); 352 /* NOT REACHED */ 353 } 354 } 355 argc -= optind; 356 argv += optind; 357 358 TAILQ_INIT(&pdev_array_list); 359 TAILQ_INIT(&udev_monitor_list); 360 361 r = ignore_signal(SIGPIPE); 362 if (r != 0) 363 err(1, "could not ignore_signal SIGPIPE"); 364 365 r = pthread_mutex_init(&(monitor_lock), NULL); 366 if (r != 0) 367 err(1, "could not allocate a pthread_mutex"); 368 369 if ((udevfd = open(UDEV_DEVICE_PATH, O_RDWR | O_NONBLOCK)) == -1) 370 err(1, "%s", UDEV_DEVICE_PATH); 371 unblock_descriptor(udevfd); 372 373 s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0); 374 if (s < 0) 375 err(1, "init_local_server"); 376 377 pidf = fopen("/var/run/udevd.pid", "w"); 378 if (pidf == NULL) 379 err(1, "pidfile"); 380 381 set_killed_signal(); 382 383 if (debugopt == 0) 384 if (daemon(0, 0) == -1) 385 err(1, "daemon"); 386 387 fprintf(pidf, "%ld\n", (long)getpid()); 388 fclose(pidf); 389 390 syslog(LOG_ERR, "udevd started"); 391 392 pdev_array_entry_insert(udev_getdevs(udevfd)); 393 394 memset(fds, 0 , sizeof(fds)); 395 fds[UDEV_DEVICE_FD_IDX].fd = udevfd; 396 fds[UDEV_DEVICE_FD_IDX].events = POLLIN; 397 fds[UDEV_SOCKET_FD_IDX].fd = s; 398 fds[UDEV_SOCKET_FD_IDX].events = POLLIN | POLLPRI; 399 400 for (;;) { 401 r = poll(fds, NFDS, -1); 402 if (r < 0) 403 err(1, "polling..."); 404 405 for (i = 0; (i < NFDS) && (r > 0); i++) { 406 if (fds[i].revents == 0) 407 continue; 408 409 --r; 410 switch (i) { 411 case UDEV_DEVICE_FD_IDX: 412 udev_read_event(udevfd); 413 break; 414 case UDEV_SOCKET_FD_IDX: 415 handle_new_connection(s); 416 break; 417 default: 418 break; 419 } 420 } 421 } 422 423 return 0; 424 } 425