1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * syseventadm - command to administer the sysevent.conf registry 28 * - administers the general purpose event framework 29 * 30 * The current implementation of the registry using files in 31 * /etc/sysevent/config, files are named as event specifications 32 * are added with the combination of the vendor, publisher, event 33 * class and subclass strings: 34 * 35 * [<vendor>,][<publisher>,][<class>,]sysevent.conf 36 * 37 */ 38 #include <stdio.h> 39 #include <ctype.h> 40 #include <sys/types.h> 41 #include <dirent.h> 42 #include <stdarg.h> 43 #include <stddef.h> 44 #include <stdlib.h> 45 #include <dlfcn.h> 46 #include <door.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <signal.h> 50 #include <strings.h> 51 #include <unistd.h> 52 #include <synch.h> 53 #include <syslog.h> 54 #include <thread.h> 55 #include <limits.h> 56 #include <locale.h> 57 #include <assert.h> 58 #include <libsysevent.h> 59 #include <zone.h> 60 #include <sys/sysevent_impl.h> 61 #include <sys/modctl.h> 62 #include <sys/param.h> 63 #include <sys/stat.h> 64 #include <sys/systeminfo.h> 65 #include <sys/wait.h> 66 67 #include "syseventadm.h" 68 #include "syseventadm_msg.h" 69 70 #ifndef DEBUG 71 #undef assert 72 #define assert(EX) ((void)0) 73 #endif 74 75 static char *whoami = NULL; 76 static char *root_dir = ""; 77 78 static char *arg_vendor = NULL; 79 static char *arg_publisher = NULL; 80 static char *arg_class = NULL; 81 static char *arg_subclass = NULL; 82 static char *arg_username = NULL; 83 static char *arg_path = NULL; 84 static int arg_nargs = 0; 85 static char **arg_args = NULL; 86 87 static int lock_fd; 88 static char lock_file[PATH_MAX + 1]; 89 90 extern char *optarg; 91 extern int optind; 92 93 static int 94 usage_gen() 95 { 96 (void) fprintf(stderr, MSG_USAGE_INTRO); 97 (void) fprintf(stderr, MSG_USAGE_OPTIONS); 98 (void) fprintf(stderr, "\n" 99 "\tsyseventadm add ...\n" 100 "\tsyseventadm remove ...\n" 101 "\tsyseventadm list ...\n" 102 "\tsyseventadm restart\n" 103 "\tsyseventadm help\n"); 104 105 return (EXIT_USAGE); 106 } 107 108 static int 109 serve_syseventdotconf(int argc, char **argv, char *cmd) 110 { 111 int c; 112 int rval; 113 114 while ((c = getopt(argc, argv, "R:v:p:c:s:u:")) != EOF) { 115 switch (c) { 116 case 'R': 117 /* 118 * Alternate root path for install, etc. 119 */ 120 set_root_dir(optarg); 121 break; 122 case 'v': 123 arg_vendor = optarg; 124 break; 125 case 'p': 126 arg_publisher = optarg; 127 break; 128 case 'c': 129 arg_class = optarg; 130 break; 131 case 's': 132 arg_subclass = optarg; 133 break; 134 case 'u': 135 arg_username = optarg; 136 break; 137 default: 138 return (usage()); 139 } 140 } 141 142 if (optind < argc) { 143 arg_path = argv[optind++]; 144 if (optind < argc) { 145 arg_nargs = argc - optind; 146 arg_args = argv + optind; 147 } 148 } 149 150 enter_lock(root_dir); 151 152 if (strcmp(cmd, "add") == 0) { 153 rval = add_cmd(); 154 } else if (strcmp(cmd, "list") == 0) { 155 rval = list_remove_cmd(CMD_LIST); 156 } else if (strcmp(cmd, "remove") == 0) { 157 rval = list_remove_cmd(CMD_REMOVE); 158 } else if (strcmp(cmd, "restart") == 0) { 159 rval = restart_cmd(); 160 } else { 161 rval = usage(); 162 } 163 164 exit_lock(); 165 166 return (rval); 167 } 168 169 170 int 171 main(int argc, char **argv) 172 { 173 char *cmd; 174 int rval; 175 176 177 (void) setlocale(LC_ALL, ""); 178 (void) textdomain(TEXT_DOMAIN); 179 180 if ((whoami = strrchr(argv[0], '/')) == NULL) { 181 whoami = argv[0]; 182 } else { 183 whoami++; 184 } 185 186 if (argc == 1) { 187 return (usage_gen()); 188 } 189 190 cmd = argv[optind++]; 191 192 /* Allow non-privileged users to get the help messages */ 193 if (strcmp(cmd, "help") == 0) { 194 rval = usage_gen(); 195 return (rval); 196 } 197 198 if (getuid() != 0) { 199 (void) fprintf(stderr, MSG_NOT_ROOT, whoami); 200 exit(EXIT_PERM); 201 } 202 203 if (strcmp(cmd, "evc") != 0 && getzoneid() != GLOBAL_ZONEID) { 204 (void) fprintf(stderr, MSG_NOT_GLOBAL, whoami); 205 exit(EXIT_PERM); 206 } 207 208 if (strcmp(cmd, "add") == 0 || 209 strcmp(cmd, "remove") == 0 || strcmp(cmd, "list") == 0 || 210 strcmp(cmd, "restart") == 0) { 211 rval = serve_syseventdotconf(argc, argv, cmd); 212 } else { 213 rval = usage_gen(); 214 } 215 return (rval); 216 } 217 218 219 static void 220 enter_lock(char *root_dir) 221 { 222 struct flock lock; 223 224 if (snprintf(lock_file, sizeof (lock_file), "%s%s", root_dir, 225 LOCK_FILENAME) >= sizeof (lock_file)) { 226 (void) fprintf(stderr, MSG_LOCK_PATH_ERR, whoami, lock_file); 227 exit(EXIT_CMD_FAILED); 228 } 229 lock_fd = open(lock_file, O_CREAT|O_RDWR, 0644); 230 if (lock_fd < 0) { 231 (void) fprintf(stderr, MSG_LOCK_CREATE_ERR, 232 whoami, lock_file, strerror(errno)); 233 exit(EXIT_CMD_FAILED); 234 } 235 236 lock.l_type = F_WRLCK; 237 lock.l_whence = SEEK_SET; 238 lock.l_start = 0; 239 lock.l_len = 0; 240 241 retry: 242 if (fcntl(lock_fd, F_SETLKW, &lock) == -1) { 243 if (errno == EAGAIN || errno == EINTR) 244 goto retry; 245 (void) close(lock_fd); 246 (void) fprintf(stderr, MSG_LOCK_SET_ERR, 247 whoami, lock_file, strerror(errno)); 248 exit(EXIT_CMD_FAILED); 249 } 250 } 251 252 253 static void 254 exit_lock() 255 { 256 struct flock lock; 257 258 lock.l_type = F_UNLCK; 259 lock.l_whence = SEEK_SET; 260 lock.l_start = 0; 261 lock.l_len = 0; 262 263 if (fcntl(lock_fd, F_SETLK, &lock) == -1) { 264 (void) fprintf(stderr, MSG_LOCK_CLR_ERR, 265 whoami, lock_file, strerror(errno)); 266 } 267 268 if (close(lock_fd) == -1) { 269 (void) fprintf(stderr, MSG_LOCK_CLOSE_ERR, 270 whoami, lock_file, strerror(errno)); 271 } 272 } 273 274 275 static void 276 set_root_dir(char *dir) 277 { 278 root_dir = sc_strdup(dir); 279 } 280 281 282 static char *usage_msg[] = { 283 "\n" 284 "\tsyseventadm add [-R <rootdir>] [-v vendor] [-p publisher]\n" 285 "\t[-c class] [-s subclass] [-u username] path [args]\n" 286 "\n" 287 "\tsyseventadm remove [-R <rootdir>] [-v vendor] [-p publisher]\n" 288 "\t[-c class] [-s subclass] [-u username] [path [args]]\n" 289 "\n" 290 "\tsyseventadm list [-R <rootdir>] [-v vendor] [-p publisher]\n" 291 "\t[-c class] [-s subclass] [-u username] [path [args]]\n" 292 }; 293 294 static int 295 usage() 296 { 297 char **msgs; 298 int i; 299 300 msgs = usage_msg; 301 for (i = 0; i < sizeof (usage_msg)/sizeof (char *); i++) { 302 (void) fputs(*msgs++, stderr); 303 } 304 305 return (EXIT_USAGE); 306 } 307 308 309 static int 310 add_cmd(void) 311 { 312 char fname[MAXPATHLEN+1]; 313 int need_comma = 0; 314 int noptions = 0; 315 struct stat st; 316 FILE *fp; 317 str_t *line; 318 int i; 319 320 /* 321 * At least one of vendor/publisher/class must be specified. 322 * Subclass is only defined within the context of class. 323 * For add, path must also be specified. 324 */ 325 if (arg_vendor) 326 noptions++; 327 if (arg_publisher) 328 noptions++; 329 if (arg_class) 330 noptions++; 331 332 if (noptions == 0 || (arg_subclass && arg_class == NULL)) { 333 return (usage()); 334 } 335 336 if (arg_path == NULL) 337 return (usage()); 338 339 /* 340 * Generate the sysevent.conf file name 341 */ 342 (void) strcpy(fname, root_dir); 343 (void) strcat(fname, SYSEVENT_CONFIG_DIR); 344 (void) strcat(fname, "/"); 345 346 if (arg_vendor) { 347 (void) strcat(fname, arg_vendor); 348 need_comma = 1; 349 } 350 if (arg_publisher) { 351 if (need_comma) 352 (void) strcat(fname, ","); 353 (void) strcat(fname, arg_publisher); 354 need_comma = 1; 355 } 356 if (arg_class) { 357 if (need_comma) 358 (void) strcat(fname, ","); 359 (void) strcat(fname, arg_class); 360 } 361 (void) strcat(fname, SYSEVENT_CONF_SUFFIX); 362 363 /* 364 * Prepare the line to be written to the sysevent.conf file 365 */ 366 line = initstr(128); 367 368 strcats(line, arg_class == NULL ? "-" : arg_class); 369 strcatc(line, ' '); 370 371 strcats(line, arg_subclass == NULL ? "-" : arg_subclass); 372 strcatc(line, ' '); 373 374 strcats(line, arg_vendor == NULL ? "-" : arg_vendor); 375 strcatc(line, ' '); 376 377 strcats(line, arg_publisher == NULL ? "-" : arg_publisher); 378 strcatc(line, ' '); 379 380 strcats(line, arg_username == NULL ? "-" : arg_username); 381 strcatc(line, ' '); 382 383 strcats(line, "- - "); 384 strcats(line, arg_path); 385 386 if (arg_nargs) { 387 for (i = 0; i < arg_nargs; i++) { 388 strcatc(line, ' '); 389 strcats(line, arg_args[i]); 390 } 391 } 392 393 if (stat(fname, &st) == -1) { 394 if (creat(fname, 0644) == -1) { 395 (void) fprintf(stderr, MSG_CANNOT_CREATE, 396 whoami, fname, strerror(errno)); 397 freestr(line); 398 return (EXIT_CMD_FAILED); 399 } 400 } 401 402 fp = fopen(fname, "a"); 403 if (fp == NULL) { 404 (void) fprintf(stderr, MSG_CANNOT_OPEN, 405 whoami, fname, strerror(errno)); 406 freestr(line); 407 return (EXIT_CMD_FAILED); 408 } 409 410 (void) fprintf(fp, "%s\n", line->s_str); 411 freestr(line); 412 413 if (fclose(fp) == -1) { 414 (void) fprintf(stderr, MSG_CLOSE_ERROR, 415 whoami, fname, strerror(errno)); 416 return (EXIT_CMD_FAILED); 417 } 418 419 if (chmod(fname, 0444) == -1) { 420 (void) fprintf(stderr, MSG_CHMOD_ERROR, 421 whoami, fname, strerror(errno)); 422 return (EXIT_CMD_FAILED); 423 } 424 return (EXIT_OK); 425 } 426 427 428 static int 429 list_remove_cmd(int cmd) 430 { 431 struct dirent *dp; 432 DIR *dir; 433 char path[MAXPATHLEN+1]; 434 char fname[MAXPATHLEN+1]; 435 char *suffix; 436 char **dirlist = NULL; 437 int list_size = 0; 438 int list_alloc = 0; 439 char **p; 440 int rval; 441 int result; 442 443 /* 444 * For the remove cmd, at least one of vendor/publisher/class/username 445 * path must be specified. Subclass is only defined within the 446 * context of a class. 447 */ 448 if (cmd == CMD_REMOVE) { 449 int noptions = 0; 450 if (arg_vendor) 451 noptions++; 452 if (arg_publisher) 453 noptions++; 454 if (arg_class) 455 noptions++; 456 if (arg_username) 457 noptions++; 458 if (arg_path) 459 noptions++; 460 if (noptions == 0 || (arg_subclass && arg_class == NULL)) { 461 return (usage()); 462 } 463 } 464 465 (void) strcpy(path, root_dir); 466 (void) strcat(path, SYSEVENT_CONFIG_DIR); 467 468 if ((dir = opendir(path)) == NULL) { 469 (void) fprintf(stderr, MSG_CANNOT_OPEN_DIR, 470 whoami, path, strerror(errno)); 471 return (EXIT_CMD_FAILED); 472 } 473 474 while ((dp = readdir(dir)) != NULL) { 475 if (dp->d_name[0] == '.') 476 continue; 477 if ((strlen(dp->d_name) == 0) || 478 (strcmp(dp->d_name, "lost+found") == 0)) 479 continue; 480 suffix = strrchr(dp->d_name, ','); 481 if (suffix && strcmp(suffix, SYSEVENT_CONF_SUFFIX) == 0) { 482 (void) strcpy(fname, path); 483 (void) strcat(fname, "/"); 484 (void) strcat(fname, dp->d_name); 485 dirlist = build_strlist(dirlist, 486 &list_size, &list_alloc, fname); 487 } 488 } 489 490 if (closedir(dir) == -1) { 491 (void) fprintf(stderr, MSG_CLOSE_DIR_ERROR, 492 whoami, path, strerror(errno)); 493 return (EXIT_CMD_FAILED); 494 } 495 496 rval = EXIT_NO_MATCH; 497 if (dirlist) { 498 for (p = dirlist; *p != NULL; p++) { 499 switch (cmd) { 500 case CMD_LIST: 501 result = list_file(*p); 502 break; 503 case CMD_REMOVE: 504 result = remove_file(*p); 505 break; 506 } 507 if (rval == EXIT_NO_MATCH && 508 result != EXIT_NO_MATCH) 509 rval = result; 510 } 511 } 512 return (rval); 513 } 514 515 516 static int 517 list_file(char *fname) 518 { 519 FILE *fp; 520 str_t *line; 521 serecord_t *sep; 522 int rval = EXIT_NO_MATCH; 523 524 fp = fopen(fname, "r"); 525 if (fp == NULL) { 526 (void) fprintf(stderr, MSG_CANNOT_OPEN, 527 whoami, fname, strerror(errno)); 528 return (EXIT_CMD_FAILED); 529 } 530 for (;;) { 531 line = read_next_line(fp); 532 if (line == NULL) 533 break; 534 sep = parse_line(line); 535 if (sep != NULL) { 536 if (matches_serecord(sep)) { 537 print_serecord(stdout, sep); 538 rval = EXIT_OK; 539 } 540 free_serecord(sep); 541 } 542 freestr(line); 543 } 544 (void) fclose(fp); 545 546 return (rval); 547 } 548 549 550 static int 551 remove_file(char *fname) 552 { 553 FILE *fp; 554 FILE *tmp_fp; 555 str_t *line; 556 char *raw_line; 557 serecord_t *sep; 558 char tmp_name[MAXPATHLEN+1]; 559 int is_empty = 1; 560 561 fp = fopen(fname, "r"); 562 if (fp == NULL) { 563 (void) fprintf(stderr, MSG_CANNOT_OPEN, 564 whoami, fname, strerror(errno)); 565 return (EXIT_CMD_FAILED); 566 } 567 568 if (check_for_removes(fp) == 0) { 569 (void) fclose(fp); 570 return (EXIT_NO_MATCH); 571 } 572 573 rewind(fp); 574 575 (void) strcpy(tmp_name, root_dir); 576 (void) strcat(tmp_name, SYSEVENT_CONFIG_DIR); 577 (void) strcat(tmp_name, "/tmp.XXXXXX"); 578 if (mktemp(tmp_name) == NULL) { 579 (void) fprintf(stderr, "unable to make tmp file name\n"); 580 return (EXIT_CMD_FAILED); 581 } 582 583 if (creat(tmp_name, 0644) == -1) { 584 (void) fprintf(stderr, MSG_CANNOT_CREATE, 585 whoami, tmp_name, strerror(errno)); 586 return (EXIT_CMD_FAILED); 587 } 588 589 tmp_fp = fopen(tmp_name, "a"); 590 if (tmp_fp == NULL) { 591 (void) fprintf(stderr, MSG_CANNOT_OPEN, 592 whoami, tmp_name, strerror(errno)); 593 (void) unlink(tmp_name); 594 (void) fclose(fp); 595 return (EXIT_CMD_FAILED); 596 } 597 598 for (;;) { 599 line = read_next_line(fp); 600 if (line == NULL) 601 break; 602 raw_line = sc_strdup(line->s_str); 603 sep = parse_line(line); 604 if (sep == NULL) { 605 (void) fputs(line->s_str, tmp_fp); 606 } else { 607 if (!matches_serecord(sep)) { 608 is_empty = 0; 609 (void) fprintf(tmp_fp, "%s\n", raw_line); 610 } 611 free_serecord(sep); 612 } 613 freestr(line); 614 sc_strfree(raw_line); 615 } 616 (void) fclose(fp); 617 if (fclose(tmp_fp) == -1) { 618 (void) fprintf(stderr, MSG_CLOSE_ERROR, 619 whoami, tmp_name, strerror(errno)); 620 } 621 622 if (is_empty) { 623 if (unlink(tmp_name) == -1) { 624 (void) fprintf(stderr, MSG_CANNOT_UNLINK, 625 whoami, tmp_name, strerror(errno)); 626 return (EXIT_CMD_FAILED); 627 } 628 if (unlink(fname) == -1) { 629 (void) fprintf(stderr, MSG_CANNOT_UNLINK, 630 whoami, fname, strerror(errno)); 631 return (EXIT_CMD_FAILED); 632 } 633 } else { 634 if (unlink(fname) == -1) { 635 (void) fprintf(stderr, MSG_CANNOT_UNLINK, 636 whoami, fname, strerror(errno)); 637 return (EXIT_CMD_FAILED); 638 } 639 if (rename(tmp_name, fname) == -1) { 640 (void) fprintf(stderr, MSG_CANNOT_RENAME, 641 whoami, tmp_name, fname, strerror(errno)); 642 return (EXIT_CMD_FAILED); 643 } 644 if (chmod(fname, 0444) == -1) { 645 (void) fprintf(stderr, MSG_CHMOD_ERROR, 646 whoami, fname, strerror(errno)); 647 return (EXIT_CMD_FAILED); 648 } 649 } 650 651 return (EXIT_OK); 652 } 653 654 static int 655 check_for_removes(FILE *fp) 656 { 657 str_t *line; 658 serecord_t *sep; 659 660 for (;;) { 661 line = read_next_line(fp); 662 if (line == NULL) 663 break; 664 sep = parse_line(line); 665 if (sep != NULL) { 666 if (matches_serecord(sep)) { 667 free_serecord(sep); 668 freestr(line); 669 return (1); 670 } 671 free_serecord(sep); 672 } 673 freestr(line); 674 } 675 676 return (0); 677 } 678 679 680 static int 681 matches_serecord(serecord_t *sep) 682 { 683 char *line; 684 char *lp; 685 char *token; 686 int i; 687 688 if (arg_vendor && 689 strcmp(arg_vendor, sep->se_vendor) != 0) { 690 return (0); 691 } 692 693 if (arg_publisher && 694 strcmp(arg_publisher, sep->se_publisher) != 0) { 695 return (0); 696 } 697 698 if (arg_class && 699 strcmp(arg_class, sep->se_class) != 0) { 700 return (0); 701 } 702 703 if (arg_subclass && 704 strcmp(arg_subclass, sep->se_subclass) != 0) { 705 return (0); 706 } 707 708 if (arg_username && 709 strcmp(arg_username, sep->se_user) != 0) { 710 return (0); 711 } 712 713 if (arg_path && 714 strcmp(arg_path, sep->se_path) != 0) { 715 return (0); 716 } 717 718 if (arg_nargs > 0) { 719 line = sc_strdup(sep->se_args); 720 lp = line; 721 for (i = 0; i < arg_nargs; i++) { 722 token = next_field(&lp); 723 if (strcmp(arg_args[i], token) != 0) { 724 sc_strfree(line); 725 return (0); 726 } 727 } 728 sc_strfree(line); 729 } 730 731 return (1); 732 } 733 734 static void 735 print_serecord(FILE *fp, serecord_t *sep) 736 { 737 str_t *line; 738 739 line = initstr(128); 740 741 if (strcmp(sep->se_vendor, "-") != 0) { 742 strcats(line, "vendor="); 743 strcats(line, sep->se_vendor); 744 strcats(line, " "); 745 } 746 if (strcmp(sep->se_publisher, "-") != 0) { 747 strcats(line, "publisher="); 748 strcats(line, sep->se_publisher); 749 strcats(line, " "); 750 } 751 if (strcmp(sep->se_class, "-") != 0) { 752 strcats(line, "class="); 753 strcats(line, sep->se_class); 754 strcats(line, " "); 755 if (strcmp(sep->se_subclass, "-") != 0) { 756 strcats(line, "subclass="); 757 strcats(line, sep->se_subclass); 758 strcats(line, " "); 759 } 760 } 761 if (strcmp(sep->se_user, "-") != 0) { 762 strcats(line, "username="); 763 strcats(line, sep->se_user); 764 strcats(line, " "); 765 } 766 strcats(line, sep->se_path); 767 if (sep->se_args) { 768 strcats(line, " "); 769 strcats(line, sep->se_args); 770 } 771 strcats(line, "\n"); 772 773 (void) fputs(line->s_str, fp); 774 freestr(line); 775 } 776 777 778 779 780 static int 781 restart_cmd(void) 782 { 783 if (system("pkill -HUP syseventd") == -1) { 784 (void) fprintf(stderr, MSG_RESTART_FAILED, 785 whoami, strerror(errno)); 786 return (EXIT_CMD_FAILED); 787 } 788 return (EXIT_OK); 789 } 790 791 792 static str_t * 793 read_next_line(FILE *fp) 794 { 795 char *lp; 796 str_t *line; 797 798 line = initstr(128); 799 800 lp = fstrgets(line, fp); 801 if (lp == NULL) { 802 freestr(line); 803 return (NULL); 804 } 805 806 *(lp + strlen(lp)-1) = 0; 807 return (line); 808 } 809 810 811 static serecord_t * 812 parse_line(str_t *line) 813 { 814 char *lp; 815 char *vendor, *publisher; 816 char *class, *subclass; 817 char *user; 818 char *reserved1, *reserved2; 819 char *path, *args; 820 serecord_t *sep; 821 822 lp = line->s_str; 823 if (*lp == 0 || *lp == '#') { 824 return (NULL); 825 } 826 827 if ((class = next_field(&lp)) != NULL) { 828 subclass = next_field(&lp); 829 if (lp == NULL) 830 return (NULL); 831 vendor = next_field(&lp); 832 if (lp == NULL) 833 return (NULL); 834 publisher = next_field(&lp); 835 if (lp == NULL) 836 return (NULL); 837 user = next_field(&lp); 838 if (lp == NULL) 839 return (NULL); 840 reserved1 = next_field(&lp); 841 if (lp == NULL) 842 return (NULL); 843 reserved2 = next_field(&lp); 844 if (lp == NULL) 845 return (NULL); 846 path = next_field(&lp); 847 if (lp == NULL) 848 return (NULL); 849 args = skip_spaces(&lp); 850 } 851 852 sep = sc_malloc(sizeof (serecord_t)); 853 854 sep->se_vendor = sc_strdup(vendor); 855 sep->se_publisher = sc_strdup(publisher); 856 sep->se_class = sc_strdup(class); 857 sep->se_subclass = sc_strdup(subclass); 858 sep->se_user = sc_strdup(user); 859 sep->se_reserved1 = sc_strdup(reserved1); 860 sep->se_reserved2 = sc_strdup(reserved2); 861 sep->se_path = sc_strdup(path); 862 sep->se_args = (args == NULL) ? NULL : sc_strdup(args); 863 864 return (sep); 865 } 866 867 868 static void 869 free_serecord(serecord_t *sep) 870 { 871 sc_strfree(sep->se_vendor); 872 sc_strfree(sep->se_publisher); 873 sc_strfree(sep->se_class); 874 sc_strfree(sep->se_subclass); 875 sc_strfree(sep->se_user); 876 sc_strfree(sep->se_reserved1); 877 sc_strfree(sep->se_reserved2); 878 sc_strfree(sep->se_path); 879 sc_strfree(sep->se_args); 880 sc_free(sep, sizeof (serecord_t)); 881 } 882 883 884 /* 885 * skip_spaces() - skip to next non-space character 886 */ 887 static char * 888 skip_spaces(char **cpp) 889 { 890 char *cp = *cpp; 891 892 while (*cp == ' ' || *cp == '\t') 893 cp++; 894 if (*cp == 0) { 895 *cpp = 0; 896 return (NULL); 897 } 898 return (cp); 899 } 900 901 902 /* 903 * Get next white-space separated field. 904 * next_field() will not check any characters on next line. 905 * Each entry is composed of a single line. 906 */ 907 static char * 908 next_field(char **cpp) 909 { 910 char *cp = *cpp; 911 char *start; 912 913 while (*cp == ' ' || *cp == '\t') 914 cp++; 915 if (*cp == 0) { 916 *cpp = 0; 917 return (NULL); 918 } 919 start = cp; 920 while (*cp && *cp != ' ' && *cp != '\t') 921 cp++; 922 if (*cp != 0) 923 *cp++ = 0; 924 *cpp = cp; 925 return (start); 926 } 927 928 929 930 /* 931 * The following functions are simple wrappers/equivalents 932 * for malloc, realloc, free, strdup and a special free 933 * for strdup. 934 */ 935 936 static void * 937 sc_malloc(size_t n) 938 { 939 void *p; 940 941 p = malloc(n); 942 if (p == NULL) { 943 no_mem_err(); 944 } 945 return (p); 946 } 947 948 /*ARGSUSED*/ 949 static void * 950 sc_realloc(void *p, size_t current, size_t n) 951 { 952 p = realloc(p, n); 953 if (p == NULL) { 954 no_mem_err(); 955 } 956 return (p); 957 } 958 959 960 /*ARGSUSED*/ 961 static void 962 sc_free(void *p, size_t n) 963 { 964 free(p); 965 } 966 967 968 static char * 969 sc_strdup(char *cp) 970 { 971 char *new; 972 973 new = malloc((unsigned)(strlen(cp) + 1)); 974 if (new == NULL) { 975 no_mem_err(); 976 } 977 (void) strcpy(new, cp); 978 return (new); 979 } 980 981 982 static void 983 sc_strfree(char *s) 984 { 985 if (s) 986 free(s); 987 } 988 989 990 /* 991 * The following functions provide some simple dynamic string 992 * capability. This module has no hard-coded maximum string 993 * lengths and should be able to parse and generate arbitrarily 994 * long strings, macro expansion and command lines. 995 * 996 * Each string must be explicitly allocated and freed. 997 */ 998 999 /* 1000 * Allocate a dynamic string, with a hint to indicate how 1001 * much memory to dynamically add to the string as it grows 1002 * beyond its existing bounds, so as to avoid excessive 1003 * reallocs as a string grows. 1004 */ 1005 static str_t * 1006 initstr(int hint) 1007 { 1008 str_t *str; 1009 1010 str = sc_malloc(sizeof (str_t)); 1011 str->s_str = NULL; 1012 str->s_len = 0; 1013 str->s_alloc = 0; 1014 str->s_hint = hint; 1015 return (str); 1016 } 1017 1018 1019 /* 1020 * Free a dynamically-allocated string 1021 */ 1022 static void 1023 freestr(str_t *str) 1024 { 1025 if (str->s_str) { 1026 sc_free(str->s_str, str->s_alloc); 1027 } 1028 sc_free(str, sizeof (str_t)); 1029 } 1030 1031 1032 /* 1033 * Reset a dynamically-allocated string, allows reuse 1034 * rather than freeing the old and allocating a new one. 1035 */ 1036 static void 1037 resetstr(str_t *str) 1038 { 1039 str->s_len = 0; 1040 } 1041 1042 1043 /* 1044 * Concatenate a (simple) string onto a dynamically-allocated string 1045 */ 1046 static void 1047 strcats(str_t *str, char *s) 1048 { 1049 char *new_str; 1050 int len = str->s_len + strlen(s) + 1; 1051 1052 if (str->s_alloc < len) { 1053 new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) : 1054 sc_realloc(str->s_str, str->s_alloc, len+str->s_hint); 1055 str->s_str = new_str; 1056 str->s_alloc = len + str->s_hint; 1057 } 1058 (void) strcpy(str->s_str + str->s_len, s); 1059 str->s_len = len - 1; 1060 } 1061 1062 1063 /* 1064 * Concatenate a character onto a dynamically-allocated string 1065 */ 1066 static void 1067 strcatc(str_t *str, int c) 1068 { 1069 char *new_str; 1070 int len = str->s_len + 2; 1071 1072 if (str->s_alloc < len) { 1073 new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) : 1074 sc_realloc(str->s_str, str->s_alloc, len+str->s_hint); 1075 str->s_str = new_str; 1076 str->s_alloc = len + str->s_hint; 1077 } 1078 *(str->s_str + str->s_len) = (char)c; 1079 *(str->s_str + str->s_len + 1) = 0; 1080 str->s_len++; 1081 } 1082 1083 /* 1084 * fgets() equivalent using a dynamically-allocated string 1085 */ 1086 static char * 1087 fstrgets(str_t *line, FILE *fp) 1088 { 1089 int c; 1090 1091 resetstr(line); 1092 while ((c = fgetc(fp)) != EOF) { 1093 strcatc(line, c); 1094 if (c == '\n') 1095 break; 1096 } 1097 if (line->s_len == 0) 1098 return (NULL); 1099 return (line->s_str); 1100 } 1101 1102 1103 1104 #define INITIAL_LISTSIZE 4 1105 #define INCR_LISTSIZE 4 1106 1107 static char ** 1108 build_strlist( 1109 char **argvlist, 1110 int *size, 1111 int *alloc, 1112 char *str) 1113 { 1114 int n; 1115 1116 if (*size + 1 > *alloc) { 1117 if (*alloc == 0) { 1118 *alloc = INITIAL_LISTSIZE; 1119 n = sizeof (char *) * (*alloc + 1); 1120 argvlist = (char **)malloc(n); 1121 if (argvlist == NULL) 1122 no_mem_err(); 1123 } else { 1124 *alloc += INCR_LISTSIZE; 1125 n = sizeof (char *) * (*alloc + 1); 1126 argvlist = (char **)realloc(argvlist, n); 1127 if (argvlist == NULL) 1128 no_mem_err(); 1129 } 1130 } 1131 1132 argvlist[*size] = strdup(str); 1133 *size += 1; 1134 argvlist[*size] = NULL; 1135 1136 return (argvlist); 1137 } 1138 1139 static void 1140 no_mem_err() 1141 { 1142 (void) fprintf(stderr, MSG_NO_MEM, whoami); 1143 exit_lock(); 1144 exit(EXIT_NO_MEM); 1145 /*NOTREACHED*/ 1146 } 1147