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