1/* 2 * Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com> 3 * Copyright (C) 2003-2006 Kay Sievers <kay.sievers@vrfy.org> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation version 2 of the License. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 * 18 */ 19 20#include <stddef.h> 21#include <stdlib.h> 22#include <string.h> 23#include <stdio.h> 24#include <ctype.h> 25#include <unistd.h> 26#include <sys/stat.h> 27#include <errno.h> 28 29#include "udev.h" 30#include "udev_rules.h" 31 32 33void udev_rules_iter_init(struct udev_rules *rules) 34{ 35 dbg("bufsize=%zi", rules->bufsize); 36 rules->current = 0; 37} 38 39struct udev_rule *udev_rules_iter_next(struct udev_rules *rules) 40{ 41 static struct udev_rule *rule; 42 43 if (!rules) 44 return NULL; 45 46 dbg("current=%zi", rules->current); 47 if (rules->current >= rules->bufsize) { 48 dbg("no more rules"); 49 return NULL; 50 } 51 52 /* get next rule */ 53 rule = (struct udev_rule *) (rules->buf + rules->current); 54 rules->current += sizeof(struct udev_rule) + rule->bufsize; 55 56 return rule; 57} 58 59struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label) 60{ 61 static struct udev_rule *rule; 62 63next: 64 dbg("current=%zi", rules->current); 65 if (rules->current >= rules->bufsize) { 66 dbg("no more rules"); 67 return NULL; 68 } 69 rule = (struct udev_rule *) (rules->buf + rules->current); 70 71 if (strcmp(&rule->buf[rule->label.val_off], label) != 0) { 72 dbg("moving forward, looking for label '%s'", label); 73 rules->current += sizeof(struct udev_rule) + rule->bufsize; 74 goto next; 75 } 76 77 dbg("found label '%s'", label); 78 return rule; 79} 80 81static int get_key(char **line, char **key, enum key_operation *operation, char **value) 82{ 83 char *linepos; 84 char *temp; 85 86 linepos = *line; 87 if (linepos == NULL && linepos[0] == '\0') 88 return -1; 89 90 /* skip whitespace */ 91 while (isspace(linepos[0]) || linepos[0] == ',') 92 linepos++; 93 94 /* get the key */ 95 if (linepos[0] == '\0') 96 return -1; 97 *key = linepos; 98 99 while (1) { 100 linepos++; 101 if (linepos[0] == '\0') 102 return -1; 103 if (isspace(linepos[0])) 104 break; 105 if (linepos[0] == '=') 106 break; 107 if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) 108 if (linepos[1] == '=') 109 break; 110 } 111 112 /* remember end of key */ 113 temp = linepos; 114 115 /* skip whitespace after key */ 116 while (isspace(linepos[0])) 117 linepos++; 118 if (linepos[0] == '\0') 119 return -1; 120 121 /* get operation type */ 122 if (linepos[0] == '=' && linepos[1] == '=') { 123 *operation = KEY_OP_MATCH; 124 linepos += 2; 125 dbg("operator=match"); 126 } else if (linepos[0] == '!' && linepos[1] == '=') { 127 *operation = KEY_OP_NOMATCH; 128 linepos += 2; 129 dbg("operator=nomatch"); 130 } else if (linepos[0] == '+' && linepos[1] == '=') { 131 *operation = KEY_OP_ADD; 132 linepos += 2; 133 dbg("operator=add"); 134 } else if (linepos[0] == '=') { 135 *operation = KEY_OP_ASSIGN; 136 linepos++; 137 dbg("operator=assign"); 138 } else if (linepos[0] == ':' && linepos[1] == '=') { 139 *operation = KEY_OP_ASSIGN_FINAL; 140 linepos += 2; 141 dbg("operator=assign_final"); 142 } else 143 return -1; 144 145 /* terminate key */ 146 temp[0] = '\0'; 147 dbg("key='%s'", *key); 148 149 /* skip whitespace after operator */ 150 while (isspace(linepos[0])) 151 linepos++; 152 if (linepos[0] == '\0') 153 return -1; 154 155 /* get the value*/ 156 if (linepos[0] == '"') 157 linepos++; 158 else 159 return -1; 160 *value = linepos; 161 162 temp = strchr(linepos, '"'); 163 if (!temp) 164 return -1; 165 temp[0] = '\0'; 166 temp++; 167 dbg("value='%s'", *value); 168 169 /* move line to next key */ 170 *line = temp; 171 172 return 0; 173} 174 175/* extract possible KEY{attr} */ 176static char *get_key_attribute(char *str) 177{ 178 char *pos; 179 char *attr; 180 181 attr = strchr(str, '{'); 182 if (attr != NULL) { 183 attr++; 184 pos = strchr(attr, '}'); 185 if (pos == NULL) { 186 err("missing closing brace for format"); 187 return NULL; 188 } 189 pos[0] = '\0'; 190 dbg("attribute='%s'", attr); 191 return attr; 192 } 193 194 return NULL; 195} 196 197static int add_rule_key(struct udev_rule *rule, struct key *key, 198 enum key_operation operation, const char *value) 199{ 200 size_t val_len = strnlen(value, PATH_SIZE); 201 202 key->operation = operation; 203 204 key->val_off = rule->bufsize; 205 strlcpy(rule->buf + rule->bufsize, value, val_len+1); 206 rule->bufsize += val_len+1; 207 208 return 0; 209} 210 211static int add_rule_key_pair(struct udev_rule *rule, struct key_pairs *pairs, 212 enum key_operation operation, const char *key, const char *value) 213{ 214 size_t key_len = strnlen(key, PATH_SIZE); 215 216 if (pairs->count >= PAIRS_MAX) { 217 err("skip, too many keys of the same type in a single rule"); 218 return -1; 219 } 220 221 add_rule_key(rule, &pairs->keys[pairs->count].key, operation, value); 222 223 /* add the key-name of the pair */ 224 pairs->keys[pairs->count].key_name_off = rule->bufsize; 225 strlcpy(rule->buf + rule->bufsize, key, key_len+1); 226 rule->bufsize += key_len+1; 227 228 pairs->count++; 229 230 return 0; 231} 232 233static int add_to_rules(struct udev_rules *rules, char *line, const char *filename, unsigned int lineno) 234{ 235 char buf[sizeof(struct udev_rule) + LINE_SIZE]; 236 struct udev_rule *rule; 237 size_t rule_size; 238 int valid; 239 char *linepos; 240 char *attr; 241 size_t padding; 242 int physdev = 0; 243 int retval; 244 245 memset(buf, 0x00, sizeof(buf)); 246 rule = (struct udev_rule *) buf; 247 linepos = line; 248 valid = 0; 249 250 /* get all the keys */ 251 while (1) { 252 char *key; 253 char *value; 254 enum key_operation operation = KEY_OP_UNSET; 255 256 retval = get_key(&linepos, &key, &operation, &value); 257 if (retval) 258 break; 259 260 if (strcasecmp(key, "ACTION") == 0) { 261 if (operation != KEY_OP_MATCH && 262 operation != KEY_OP_NOMATCH) { 263 err("invalid ACTION operation"); 264 goto invalid; 265 } 266 add_rule_key(rule, &rule->action, operation, value); 267 valid = 1; 268 continue; 269 } 270 271 if (strcasecmp(key, "DEVPATH") == 0) { 272 if (operation != KEY_OP_MATCH && 273 operation != KEY_OP_NOMATCH) { 274 err("invalid DEVPATH operation"); 275 goto invalid; 276 } 277 add_rule_key(rule, &rule->devpath, operation, value); 278 valid = 1; 279 continue; 280 } 281 282 if (strcasecmp(key, "KERNEL") == 0) { 283 if (operation != KEY_OP_MATCH && 284 operation != KEY_OP_NOMATCH) { 285 err("invalid KERNEL operation"); 286 goto invalid; 287 } 288 add_rule_key(rule, &rule->kernel, operation, value); 289 valid = 1; 290 continue; 291 } 292 293 if (strcasecmp(key, "SUBSYSTEM") == 0) { 294 if (operation != KEY_OP_MATCH && 295 operation != KEY_OP_NOMATCH) { 296 err("invalid SUBSYSTEM operation"); 297 goto invalid; 298 } 299 /* bus, class, subsystem events should all be the same */ 300 if (strcmp(value, "subsystem") == 0 || 301 strcmp(value, "bus") == 0 || 302 strcmp(value, "class") == 0) { 303 if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) 304 err("'%s' must be specified as 'subsystem' " 305 "please fix it in %s:%u", value, filename, lineno); 306 add_rule_key(rule, &rule->subsystem, operation, "subsystem|class|bus"); 307 } else 308 add_rule_key(rule, &rule->subsystem, operation, value); 309 valid = 1; 310 continue; 311 } 312 313 if (strcasecmp(key, "DRIVER") == 0) { 314 if (operation != KEY_OP_MATCH && 315 operation != KEY_OP_NOMATCH) { 316 err("invalid DRIVER operation"); 317 goto invalid; 318 } 319 add_rule_key(rule, &rule->driver, operation, value); 320 valid = 1; 321 continue; 322 } 323 324 if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { 325 attr = get_key_attribute(key + sizeof("ATTR")-1); 326 if (attr == NULL) { 327 err("error parsing ATTR attribute"); 328 goto invalid; 329 } 330 if (add_rule_key_pair(rule, &rule->attr, operation, attr, value) != 0) 331 goto invalid; 332 valid = 1; 333 continue; 334 } 335 336 if (strcasecmp(key, "KERNELS") == 0 || 337 strcasecmp(key, "ID") == 0) { 338 if (operation != KEY_OP_MATCH && 339 operation != KEY_OP_NOMATCH) { 340 err("invalid KERNELS operation"); 341 goto invalid; 342 } 343 add_rule_key(rule, &rule->kernels, operation, value); 344 valid = 1; 345 continue; 346 } 347 348 if (strcasecmp(key, "SUBSYSTEMS") == 0 || 349 strcasecmp(key, "BUS") == 0) { 350 if (operation != KEY_OP_MATCH && 351 operation != KEY_OP_NOMATCH) { 352 err("invalid SUBSYSTEMS operation"); 353 goto invalid; 354 } 355 add_rule_key(rule, &rule->subsystems, operation, value); 356 valid = 1; 357 continue; 358 } 359 360 if (strcasecmp(key, "DRIVERS") == 0) { 361 if (operation != KEY_OP_MATCH && 362 operation != KEY_OP_NOMATCH) { 363 err("invalid DRIVERS operation"); 364 goto invalid; 365 } 366 add_rule_key(rule, &rule->drivers, operation, value); 367 valid = 1; 368 continue; 369 } 370 371 if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 || 372 strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) { 373 if (operation != KEY_OP_MATCH && 374 operation != KEY_OP_NOMATCH) { 375 err("invalid ATTRS operation"); 376 goto invalid; 377 } 378 attr = get_key_attribute(key + sizeof("ATTRS")-1); 379 if (attr == NULL) { 380 err("error parsing ATTRS attribute"); 381 goto invalid; 382 } 383 if (strncmp(attr, "device/", 7) == 0) 384 err("the 'device' link is deprecated and will be removed from a future kernel, " 385 "please fix it in %s:%u", filename, lineno); 386 else if (strstr(attr, "../") != NULL) 387 err("do not reference parent sysfs directories directly, that may break with a future kernel, " 388 "please fix it in %s:%u", filename, lineno); 389 if (add_rule_key_pair(rule, &rule->attrs, operation, attr, value) != 0) 390 goto invalid; 391 valid = 1; 392 continue; 393 } 394 395 if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) { 396 attr = get_key_attribute(key + sizeof("ENV")-1); 397 if (attr == NULL) { 398 err("error parsing ENV attribute"); 399 goto invalid; 400 } 401 if (strncmp(attr, "PHYSDEV", 7) == 0) 402 physdev = 1; 403 if (add_rule_key_pair(rule, &rule->env, operation, attr, value) != 0) 404 goto invalid; 405 valid = 1; 406 continue; 407 } 408 409 if (strcasecmp(key, "PROGRAM") == 0) { 410 add_rule_key(rule, &rule->program, operation, value); 411 valid = 1; 412 continue; 413 } 414 415 if (strcasecmp(key, "RESULT") == 0) { 416 if (operation != KEY_OP_MATCH && 417 operation != KEY_OP_NOMATCH) { 418 err("invalid RESULT operation"); 419 goto invalid; 420 } 421 add_rule_key(rule, &rule->result, operation, value); 422 valid = 1; 423 continue; 424 } 425 426 if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { 427 attr = get_key_attribute(key + sizeof("IMPORT")-1); 428 if (attr && strstr(attr, "program")) { 429 dbg("IMPORT will be executed"); 430 rule->import_type = IMPORT_PROGRAM; 431 } else if (attr && strstr(attr, "file")) { 432 dbg("IMPORT will be included as file"); 433 rule->import_type = IMPORT_FILE; 434 } else if (attr && strstr(attr, "parent")) { 435 dbg("IMPORT will include the parent values"); 436 rule->import_type = IMPORT_PARENT; 437 } else { 438 /* figure it out if it is executable */ 439 char file[PATH_SIZE]; 440 char *pos; 441 struct stat stats; 442 443 strlcpy(file, value, sizeof(file)); 444 pos = strchr(file, ' '); 445 if (pos) 446 pos[0] = '\0'; 447 448 /* allow programs in /lib/udev called without the path */ 449 if (strchr(file, '/') == NULL) { 450 strlcpy(file, "/lib/udev/", sizeof(file)); 451 strlcat(file, value, sizeof(file)); 452 pos = strchr(file, ' '); 453 if (pos) 454 pos[0] = '\0'; 455 } 456 457 dbg("IMPORT auto mode for '%s'", file); 458 if (!lstat(file, &stats) && (stats.st_mode & S_IXUSR)) { 459 dbg("IMPORT is executable, will be executed (autotype)"); 460 rule->import_type = IMPORT_PROGRAM; 461 } else { 462 dbg("IMPORT is not executable, will be included as file (autotype)"); 463 rule->import_type = IMPORT_FILE; 464 } 465 } 466 add_rule_key(rule, &rule->import, operation, value); 467 valid = 1; 468 continue; 469 } 470 471 if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) { 472 attr = get_key_attribute(key + sizeof("TEST")-1); 473 if (attr != NULL) 474 rule->test_mode_mask = strtol(attr, NULL, 8); 475 add_rule_key(rule, &rule->test, operation, value); 476 valid = 1; 477 continue; 478 } 479 480 if (strcasecmp(key, "RUN") == 0) { 481 add_rule_key(rule, &rule->run, operation, value); 482 valid = 1; 483 continue; 484 } 485 486 if (strcasecmp(key, "WAIT_FOR_SYSFS") == 0) { 487 add_rule_key(rule, &rule->wait_for_sysfs, operation, value); 488 valid = 1; 489 continue; 490 } 491 492 if (strcasecmp(key, "LABEL") == 0) { 493 add_rule_key(rule, &rule->label, operation, value); 494 valid = 1; 495 continue; 496 } 497 498 if (strcasecmp(key, "GOTO") == 0) { 499 add_rule_key(rule, &rule->goto_label, operation, value); 500 valid = 1; 501 continue; 502 } 503 504 if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) { 505 attr = get_key_attribute(key + sizeof("NAME")-1); 506 if (attr != NULL) { 507 if (strstr(attr, "all_partitions") != NULL) { 508 dbg("creation of partition nodes requested"); 509 rule->partitions = DEFAULT_PARTITIONS_COUNT; 510 } 511 if (strstr(attr, "ignore_remove") != NULL) { 512 dbg("remove event should be ignored"); 513 rule->ignore_remove = 1; 514 } 515 } 516 if (value[0] == '\0') 517 dbg("name empty, node creation supressed"); 518 add_rule_key(rule, &rule->name, operation, value); 519 continue; 520 } 521 522 if (strcasecmp(key, "SYMLINK") == 0) { 523 add_rule_key(rule, &rule->symlink, operation, value); 524 valid = 1; 525 continue; 526 } 527 528 if (strcasecmp(key, "OWNER") == 0) { 529 valid = 1; 530 if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { 531 char *endptr; 532 strtoul(value, &endptr, 10); 533 if (endptr[0] != '\0') { 534 char owner[32]; 535 uid_t uid = lookup_user(value); 536 dbg("replacing username='%s' by id=%i", value, uid); 537 sprintf(owner, "%u", (unsigned int) uid); 538 add_rule_key(rule, &rule->owner, operation, owner); 539 continue; 540 } 541 } 542 543 add_rule_key(rule, &rule->owner, operation, value); 544 continue; 545 } 546 547 if (strcasecmp(key, "GROUP") == 0) { 548 valid = 1; 549 if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { 550 char *endptr; 551 strtoul(value, &endptr, 10); 552 if (endptr[0] != '\0') { 553 char group[32]; 554 gid_t gid = lookup_group(value); 555 dbg("replacing groupname='%s' by id=%i", value, gid); 556 sprintf(group, "%u", (unsigned int) gid); 557 add_rule_key(rule, &rule->group, operation, group); 558 continue; 559 } 560 } 561 562 add_rule_key(rule, &rule->group, operation, value); 563 continue; 564 } 565 566 if (strcasecmp(key, "MODE") == 0) { 567 rule->mode = strtol(value, NULL, 8); 568 rule->mode_operation = operation; 569 valid = 1; 570 continue; 571 } 572 573 if (strcasecmp(key, "OPTIONS") == 0) { 574 const char *pos; 575 576 if (strstr(value, "last_rule") != NULL) { 577 dbg("last rule to be applied"); 578 rule->last_rule = 1; 579 } 580 if (strstr(value, "ignore_device") != NULL) { 581 dbg("device should be ignored"); 582 rule->ignore_device = 1; 583 } 584 if (strstr(value, "ignore_remove") != NULL) { 585 dbg("remove event should be ignored"); 586 rule->ignore_remove = 1; 587 } 588 pos = strstr(value, "link_priority="); 589 if (pos != NULL) { 590 rule->link_priority = atoi(&pos[strlen("link_priority=")]); 591 info("link priority=%i", rule->link_priority); 592 } 593 pos = strstr(value, "string_escape="); 594 if (pos != NULL) { 595 pos = &pos[strlen("string_escape=")]; 596 if (strncmp(pos, "none", strlen("none")) == 0) 597 rule->string_escape = ESCAPE_NONE; 598 else if (strncmp(pos, "replace", strlen("replace")) == 0) 599 rule->string_escape = ESCAPE_REPLACE; 600 } 601 if (strstr(value, "all_partitions") != NULL) { 602 dbg("creation of partition nodes requested"); 603 rule->partitions = DEFAULT_PARTITIONS_COUNT; 604 } 605 valid = 1; 606 continue; 607 } 608 609 err("unknown key '%s' in %s:%u", key, filename, lineno); 610 } 611 612 if (physdev && rule->wait_for_sysfs.operation == KEY_OP_UNSET) 613 err("PHYSDEV* values are deprecated and will be removed from a future kernel, " 614 "please fix it in %s:%u", filename, lineno); 615 616 /* skip line if not any valid key was found */ 617 if (!valid) 618 goto invalid; 619 620 /* grow buffer and add rule */ 621 rule_size = sizeof(struct udev_rule) + rule->bufsize; 622 padding = (sizeof(size_t) - rule_size % sizeof(size_t)) % sizeof(size_t); 623 dbg("add %zi padding bytes", padding); 624 rule_size += padding; 625 rule->bufsize += padding; 626 627 rules->buf = realloc(rules->buf, rules->bufsize + rule_size); 628 if (!rules->buf) { 629 err("realloc failed"); 630 goto exit; 631 } 632 dbg("adding rule to offset %zi", rules->bufsize); 633 memcpy(rules->buf + rules->bufsize, rule, rule_size); 634 rules->bufsize += rule_size; 635exit: 636 return 0; 637 638invalid: 639 err("invalid rule '%s:%u'", filename, lineno); 640 return -1; 641} 642 643static int parse_file(struct udev_rules *rules, const char *filename) 644{ 645 char line[LINE_SIZE]; 646 char *bufline; 647 unsigned int lineno; 648 char *buf; 649 size_t bufsize; 650 size_t cur; 651 size_t count; 652 int retval = 0; 653 654 if (file_map(filename, &buf, &bufsize) != 0) { 655 err("can't open '%s' as rules file: %s", filename, strerror(errno)); 656 return -1; 657 } 658 info("reading '%s' as rules file", filename); 659 660 /* loop through the whole file */ 661 cur = 0; 662 lineno = 0; 663 while (cur < bufsize) { 664 unsigned int i, j; 665 666 count = buf_get_line(buf, bufsize, cur); 667 bufline = &buf[cur]; 668 cur += count+1; 669 lineno++; 670 671 /* eat the whitespace */ 672 while ((count > 0) && isspace(bufline[0])) { 673 bufline++; 674 count--; 675 } 676 if (count == 0) 677 continue; 678 679 /* see if this is a comment */ 680 if (bufline[0] == COMMENT_CHARACTER) 681 continue; 682 683 if (count >= sizeof(line)) { 684 err("line too long, rule skipped '%s:%u'", filename, lineno); 685 continue; 686 } 687 688 /* skip backslash and newline from multiline rules */ 689 for (i = j = 0; i < count; i++) { 690 if (bufline[i] == '\\' && bufline[i+1] == '\n') 691 continue; 692 693 line[j++] = bufline[i]; 694 } 695 line[j] = '\0'; 696 697 dbg("read '%s'", line); 698 add_to_rules(rules, line, filename, lineno); 699 } 700 701 file_unmap(buf, bufsize); 702 return retval; 703} 704 705int udev_rules_init(struct udev_rules *rules, int resolve_names) 706{ 707 struct stat stats; 708 int retval; 709 710 memset(rules, 0x00, sizeof(struct udev_rules)); 711 rules->resolve_names = resolve_names; 712 713 /* parse rules file or all matching files in directory */ 714 if (stat(udev_rules_dir, &stats) != 0) 715 return -1; 716 717 if ((stats.st_mode & S_IFMT) != S_IFDIR) { 718 dbg("parse single rules file '%s'", udev_rules_dir); 719 retval = parse_file(rules, udev_rules_dir); 720 } else { 721 struct name_entry *name_loop, *name_tmp; 722 LIST_HEAD(name_list); 723 724 dbg("parse rules directory '%s'", udev_rules_dir); 725 retval = add_matching_files(&name_list, udev_rules_dir, RULESFILE_SUFFIX); 726 727 list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { 728 if (stat(name_loop->name, &stats) == 0) { 729 if (stats.st_size) 730 parse_file(rules, name_loop->name); 731 else 732 dbg("empty rules file '%s'", name_loop->name); 733 } else 734 err("could not read '%s': %s", name_loop->name, strerror(errno)); 735 list_del(&name_loop->node); 736 free(name_loop); 737 } 738 } 739 740 return retval; 741} 742 743void udev_rules_cleanup(struct udev_rules *rules) 744{ 745 if (rules->buf) { 746 free(rules->buf); 747 rules->buf = NULL; 748 } 749} 750