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