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
75static char	*whoami		= NULL;
76static char	*root_dir	= "";
77
78static char	*arg_vendor	= NULL;
79static char	*arg_publisher	= NULL;
80static char	*arg_class	= NULL;
81static char	*arg_subclass	= NULL;
82static char	*arg_username	= NULL;
83static char	*arg_path	= NULL;
84static int	arg_nargs	= 0;
85static char	**arg_args	= NULL;
86
87static	int	lock_fd;
88static	char 	lock_file[PATH_MAX + 1];
89
90extern char	*optarg;
91extern int	optind;
92
93static int
94usage_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
108static int
109serve_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
170int
171main(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
219static void
220enter_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
241retry:
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
253static void
254exit_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
275static void
276set_root_dir(char *dir)
277{
278	root_dir = sc_strdup(dir);
279}
280
281
282static 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
294static int
295usage()
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
309static int
310add_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
428static int
429list_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
516static int
517list_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
550static int
551remove_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
654static int
655check_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
680static int
681matches_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
734static void
735print_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
780static int
781restart_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
792static str_t *
793read_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
811static serecord_t *
812parse_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
868static void
869free_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 */
887static char *
888skip_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 */
907static char *
908next_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
936static void *
937sc_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*/
949static void *
950sc_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*/
961static void
962sc_free(void *p, size_t n)
963{
964	free(p);
965}
966
967
968static char *
969sc_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
982static void
983sc_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 */
1005static str_t *
1006initstr(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 */
1022static void
1023freestr(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 */
1036static void
1037resetstr(str_t *str)
1038{
1039	str->s_len = 0;
1040}
1041
1042
1043/*
1044 * Concatenate a (simple) string onto a dynamically-allocated string
1045 */
1046static void
1047strcats(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 */
1066static void
1067strcatc(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 */
1086static char *
1087fstrgets(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
1107static char **
1108build_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
1139static void
1140no_mem_err()
1141{
1142	(void) fprintf(stderr, MSG_NO_MEM, whoami);
1143	exit_lock();
1144	exit(EXIT_NO_MEM);
1145	/*NOTREACHED*/
1146}
1147