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