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
usage_gen()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
serve_syseventdotconf(int argc,char ** argv,char * cmd)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
main(int argc,char ** argv)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
enter_lock(char * root_dir)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
exit_lock()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
set_root_dir(char * dir)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
usage()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
add_cmd(void)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
list_remove_cmd(int cmd)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
list_file(char * fname)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
remove_file(char * fname)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
check_for_removes(FILE * fp)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
matches_serecord(serecord_t * sep)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
print_serecord(FILE * fp,serecord_t * sep)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
restart_cmd(void)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 *
read_next_line(FILE * fp)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 *
parse_line(str_t * line)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
free_serecord(serecord_t * sep)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 *
skip_spaces(char ** cpp)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 *
next_field(char ** cpp)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 *
sc_malloc(size_t n)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 *
sc_realloc(void * p,size_t current,size_t n)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
sc_free(void * p,size_t n)962 sc_free(void *p, size_t n)
963 {
964 free(p);
965 }
966
967
968 static char *
sc_strdup(char * cp)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
sc_strfree(char * s)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 *
initstr(int hint)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
freestr(str_t * str)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
resetstr(str_t * str)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
strcats(str_t * str,char * s)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
strcatc(str_t * str,int c)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 *
fstrgets(str_t * line,FILE * fp)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 **
build_strlist(char ** argvlist,int * size,int * alloc,char * str)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
no_mem_err()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