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 static int hangup_ongoing = 0; 66 static struct pollfd fds[NFDS]; 67 68 extern pthread_mutex_t monitor_lock; 69 extern TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list; 70 extern TAILQ_HEAD(pdev_array_list_head, pdev_array_entry) pdev_array_list; 71 72 static void usage(void); 73 74 int match_dev_dict(prop_dictionary_t, prop_dictionary_t); 75 prop_dictionary_t find_dev_dict(int64_t, prop_dictionary_t, int *); 76 77 void udev_read_event(int); 78 prop_array_t udev_getdevs(int); 79 80 static 81 void 82 usage(void) 83 { 84 fprintf(stderr, "usage: udevd [-d]\n"); 85 exit(1); 86 } 87 88 int 89 match_dev_dict(prop_dictionary_t dict, prop_dictionary_t match_dict) 90 { 91 prop_number_t pn, pn2; 92 prop_string_t ps, ps2; 93 94 if (dict == NULL) 95 return 0; 96 97 if ((ps = prop_dictionary_get(dict, "name")) == NULL) 98 return 0; 99 if ((ps2 = prop_dictionary_get(match_dict, "name")) == NULL) 100 return 0; 101 if (!prop_string_equals(ps, ps2)) 102 return 0; 103 104 if ((pn = prop_dictionary_get(dict, "devnum")) == NULL) 105 return 0; 106 if ((pn2 = prop_dictionary_get(match_dict, "devnum")) == NULL) 107 return 0; 108 if (!prop_number_equals(pn, pn2)) 109 return 0; 110 111 if ((pn = prop_dictionary_get(dict, "kptr")) == NULL) 112 return 0; 113 if ((pn2 = prop_dictionary_get(match_dict, "kptr")) == NULL) 114 return 0; 115 if (!prop_number_equals(pn, pn2)) 116 return 0; 117 118 return 1; 119 } 120 121 prop_dictionary_t 122 find_dev_dict(int64_t generation, prop_dictionary_t match_dict, int *idx) 123 { 124 struct pdev_array_entry *pae; 125 prop_array_t pa; 126 prop_object_iterator_t iter; 127 prop_dictionary_t dict; 128 int i = 0; 129 130 if (generation == -1) 131 pae = pdev_array_entry_get_last(); 132 else 133 pae = pdev_array_entry_get(generation); 134 135 if (pae == NULL) 136 return NULL; 137 138 pa = pae->pdev_array; 139 140 iter = prop_array_iterator(pa); 141 if (iter == NULL) { 142 pdev_array_entry_unref(pae); 143 return NULL; 144 } 145 146 while ((dict = prop_object_iterator_next(iter)) != NULL) { 147 if (match_dev_dict(dict, match_dict)) 148 break; 149 ++i; 150 } 151 152 prop_object_iterator_release(iter); 153 154 if (idx != NULL) 155 *idx = i; 156 157 pdev_array_entry_unref(pae); 158 return dict; 159 } 160 161 void 162 udev_read_event(int fd) 163 { 164 struct pdev_array_entry *pae; 165 prop_dictionary_t dict, evdict, devdict; 166 prop_number_t pn; 167 prop_string_t ps; 168 prop_object_t po; 169 prop_array_t pa; 170 char *xml; 171 int n, idx, evtype; 172 size_t sz; 173 174 sz = 4096 * 1024; 175 176 xml = malloc(sz); /* 4 MB */ 177 again: 178 if ((n = read(fd, xml, sz)) <= 0) { 179 if (errno == ENOMEM) { 180 sz <<= 2; 181 if ((xml = realloc(xml, sz)) == NULL) { 182 syslog(LOG_ERR, "could not realloc xml memory"); 183 return; 184 } 185 goto again; 186 } 187 free(xml); 188 return; 189 } 190 191 dict = prop_dictionary_internalize(xml); 192 free(xml); 193 if (dict == NULL) { 194 syslog(LOG_ERR, "internalization of xml failed"); 195 return; 196 } 197 198 pn = prop_dictionary_get(dict, "evtype"); 199 if (pn == NULL) { 200 syslog(LOG_ERR, "read_event: no key evtype"); 201 goto out; 202 } 203 204 evtype = prop_number_integer_value(pn); 205 206 evdict = prop_dictionary_get(dict, "evdict"); 207 if (evdict == NULL) { 208 syslog(LOG_ERR, "read_event: no key evdict"); 209 goto out; 210 } 211 212 switch (evtype) { 213 case UDEV_EVENT_ATTACH: 214 monitor_queue_event(dict); 215 pae = pdev_array_entry_get_last(); 216 pa = prop_array_copy(pae->pdev_array); 217 pdev_array_entry_unref(pae); 218 if (pa == NULL) 219 goto out; 220 prop_array_add(pa, evdict); 221 pdev_array_entry_insert(pa); 222 break; 223 224 case UDEV_EVENT_DETACH: 225 monitor_queue_event(dict); 226 if ((devdict = find_dev_dict(-1, evdict, &idx)) == NULL) 227 goto out; 228 pae = pdev_array_entry_get_last(); 229 pa = prop_array_copy(pae->pdev_array); 230 pdev_array_entry_unref(pae); 231 if (pa == NULL) 232 goto out; 233 prop_array_remove(pa, idx); 234 pdev_array_entry_insert(pa); 235 break; 236 237 case UDEV_EV_KEY_UPDATE: 238 if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL) 239 goto out; 240 if ((ps = prop_dictionary_get(evdict, "key")) == NULL) 241 goto out; 242 if ((po = prop_dictionary_get(evdict, "value")) == NULL) 243 goto out; 244 /* prop_object_retain(po); */ /* not necessary afaik */ 245 prop_dictionary_set(devdict, prop_string_cstring_nocopy(ps), po); 246 break; 247 248 case UDEV_EV_KEY_REMOVE: 249 if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL) 250 goto out; 251 if ((ps = prop_dictionary_get(evdict, "key")) == NULL) 252 goto out; 253 prop_dictionary_remove(devdict, prop_string_cstring_nocopy(ps)); 254 break; 255 256 default: 257 syslog(LOG_ERR, "read_event: unknown evtype %d", evtype); 258 } 259 260 out: 261 prop_object_release(dict); 262 return; 263 } 264 265 prop_array_t 266 udev_getdevs(int devfd) 267 { 268 prop_dictionary_t pd, rpd; 269 prop_string_t ps; 270 prop_array_t pa; 271 272 pd = prop_dictionary_create(); 273 if (pd == NULL) { 274 err(1, "prop_dictionary_create()"); 275 } 276 277 ps = prop_string_create_cstring("getdevs"); 278 if (ps == NULL) { 279 prop_object_release(pd); 280 err(1, "prop_string_create_cstring()"); 281 } 282 283 if (prop_dictionary_set(pd, "command", ps) == false) { 284 prop_object_release(ps); 285 prop_object_release(pd); 286 err(1, "prop_dictionary_set()"); 287 } 288 289 prop_object_release(ps); 290 291 /* Send dictionary to kernel space */ 292 if (prop_dictionary_sendrecv_ioctl(pd, devfd, UDEVPROP, &rpd) != 0) 293 err(1, "prop_array_recv_ioctl()"); 294 295 prop_object_release(pd); 296 297 pa = prop_dictionary_get(rpd, "array"); 298 if (pa == NULL) 299 goto out; 300 prop_object_retain(pa); 301 302 out: 303 prop_object_release(rpd); 304 return pa; 305 } 306 307 static void 308 killed(int sig __unused) 309 { 310 syslog(LOG_ERR, "udevd stopped"); 311 unlink("/var/run/udevd.pid"); 312 pdev_array_clean(); 313 exit(0); 314 } 315 316 static void 317 hangup(int sig __unused) 318 { 319 FILE *pidf; 320 int s; 321 322 syslog(LOG_ERR, "udevd hangup+resume"); 323 324 pidf = fopen("/var/run/udevd.pid", "w"); 325 if (pidf != NULL) { 326 fprintf(pidf, "%ld\n", (long)getpid()); 327 fclose(pidf); 328 } 329 330 hangup_ongoing = 1; 331 close(fds[UDEV_SOCKET_FD_IDX].fd); 332 pdev_array_clean(); 333 s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0); 334 if (s < 0) 335 err(1, "init_local_server"); 336 337 fds[UDEV_SOCKET_FD_IDX].fd = s; 338 pdev_array_entry_insert(udev_getdevs(udevfd)); 339 hangup_ongoing = 0; 340 } 341 342 int 343 ignore_signal(int signum) 344 { 345 struct sigaction act; 346 int ret; 347 348 act.sa_handler = SIG_IGN; 349 sigemptyset(&act.sa_mask); 350 act.sa_flags = 0; 351 352 ret = sigaction(signum, &act, NULL); 353 return ret; 354 } 355 356 static int 357 set_signal(int signum, sig_t sig_func) 358 { 359 struct sigaction act; 360 int ret; 361 362 act.sa_handler = sig_func; 363 sigemptyset(&act.sa_mask); 364 act.sa_flags = 0; 365 366 ret = sigaction(signum, &act, NULL); 367 return ret; 368 } 369 370 int main(int argc, char *argv[]) 371 { 372 int error __unused, i, r, s; 373 FILE *pidf; 374 int ch = 0; 375 376 while ((ch = getopt(argc, argv, "d")) != -1) { 377 switch(ch) { 378 case 'd': 379 debugopt = 1; 380 break; 381 default: 382 usage(); 383 /* NOT REACHED */ 384 } 385 } 386 argc -= optind; 387 argv += optind; 388 389 TAILQ_INIT(&pdev_array_list); 390 TAILQ_INIT(&udev_monitor_list); 391 392 r = ignore_signal(SIGPIPE); 393 if (r != 0) 394 err(1, "could not ignore_signal SIGPIPE"); 395 396 r = pthread_mutex_init(&(monitor_lock), NULL); 397 if (r != 0) 398 err(1, "could not allocate a pthread_mutex"); 399 400 if ((udevfd = open(UDEV_DEVICE_PATH, O_RDWR | O_NONBLOCK)) == -1) 401 err(1, "%s", UDEV_DEVICE_PATH); 402 unblock_descriptor(udevfd); 403 404 s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0); 405 if (s < 0) 406 err(1, "init_local_server"); 407 408 pidf = fopen("/var/run/udevd.pid", "w"); 409 #if 0 410 if (pidf == NULL) 411 err(1, "pidfile"); 412 #endif 413 414 set_signal(SIGTERM, killed); 415 set_signal(SIGHUP, hangup); 416 417 if (debugopt == 0) 418 if (daemon(0, 0) == -1) 419 err(1, "daemon"); 420 421 if (pidf != NULL) { 422 fprintf(pidf, "%ld\n", (long)getpid()); 423 fclose(pidf); 424 } 425 426 syslog(LOG_ERR, "udevd started"); 427 428 pdev_array_entry_insert(udev_getdevs(udevfd)); 429 430 memset(fds, 0 , sizeof(fds)); 431 fds[UDEV_DEVICE_FD_IDX].fd = udevfd; 432 fds[UDEV_DEVICE_FD_IDX].events = POLLIN; 433 fds[UDEV_SOCKET_FD_IDX].fd = s; 434 fds[UDEV_SOCKET_FD_IDX].events = POLLIN | POLLPRI; 435 436 for (;;) { 437 r = poll(fds, NFDS, -1); 438 if (r < 0) { 439 if (hangup_ongoing == 0) { 440 if (errno == EINTR) { 441 usleep(5000); 442 continue; 443 } else { 444 err(1, "polling..."); 445 } 446 } else { 447 usleep(20000); /* 20 ms */ 448 continue; 449 } 450 } 451 452 for (i = 0; (i < NFDS) && (r > 0); i++) { 453 if (fds[i].revents == 0) 454 continue; 455 456 --r; 457 switch (i) { 458 case UDEV_DEVICE_FD_IDX: 459 udev_read_event(udevfd); 460 break; 461 case UDEV_SOCKET_FD_IDX: 462 handle_new_connection(s); 463 break; 464 default: 465 break; 466 } 467 } 468 } 469 470 syslog(LOG_ERR, "udevd is exiting normally"); 471 return 0; 472 } 473