xref: /illumos-gate/usr/src/cmd/syseventadm/syseventadm.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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