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 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <strings.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
38#include "extern.h"
39#include "misc.h"
40#include <sac.h>
41#include "structs.h"
42
43#define	ADD		0x1	/* -a or other required options seen */
44#define	REMOVE		0x2	/* -r seen */
45#define	ENABLE		0x4	/* -e seen */
46#define	DISABLE		0x8	/* -d seen */
47#define	PLIST		0x10	/* -l seen */
48#define	LIST		0x20	/* -L seen */
49#define	CONFIG		0x40	/* -g seen */
50
51# define U_FLAG		0x1	/* -fu seen */
52# define X_FLAG		0x2	/* -fx seen */
53
54/*
55 * functions
56 */
57
58char	*pflags();
59char	*pspec();
60struct	taglist	*find_type();
61void	usage();
62void	parseline();
63void	add_svc();
64void	rem_svc();
65void	ed_svc();
66void	list_svcs();
67void	doconf();
68
69/*
70 * format of a _pmtab entry - used to hold parsed info
71 */
72
73struct	pmtab {
74	char	*p_tag;		/* service tag */
75	long	p_flags;	/* flags */
76	char	*p_id;		/* logname to start service as */
77	char	*p_res1;	/* reserved field */
78	char	*p_res2;	/* reserved field */
79	char	*p_res3;	/* reserved field */
80	char	*p_pmspec;	/* port monitor specific info */
81};
82
83/*
84 * format of a tag list, which is a list of port monitor tags of
85 * a designated type
86 */
87
88struct	taglist {
89	struct	taglist	*t_next;	/* next in list */
90	char	t_tag[PMTAGSIZE + 1];	/* PM tag */
91	char	t_type[PMTYPESIZE + 1];	/* PM type */
92};
93
94/*
95 * common error messages
96 */
97
98# define NOTPRIV	"User not privileged for operation"
99# define BADINP		"Embedded newlines not allowed"
100
101int	Saferrno;	/* internal `errno' for exit */
102
103
104/*
105 * main - scan args for pmadm and call appropriate handling code
106 */
107
108int
109main(int argc, char *argv[])
110{
111	int c;			/* option letter */
112	int ret;		/* return code from check_version */
113	uid_t uid;		/* invoker's real uid */
114	int flag = 0;		/* flag to record requested operations */
115	int errflg = 0;		/* error indicator */
116	int badcnt = 0;		/* count of bad args to -f */
117	int version = -1;	/* argument to -v */
118	int sawaflag = 0;	/* true if actually saw -a */
119	int conflag = 0;	/* true if output should be in condensed form */
120	long flags = 0;		/* arguments to -f */
121	char *pmtag = NULL;	/* argument to -p */
122	char *type = NULL;	/* argument to -t */
123	char *script = NULL;	/* argument to -z */
124	char *comment = " ";	/* argument to -y */
125	char *id = NULL;	/* argument to -i */
126	char *svctag = NULL;	/* argument to -s */
127	char *pmspec = NULL;	/* argument to -m */
128	char badargs[SIZE];	/* place to hold bad args to -f */
129	char buf[SIZE];		/* scratch buffer */
130	register char *p;	/* scratch pointer */
131
132	if (argc == 1)
133		usage(argv[0]);
134	while ((c = getopt(argc, argv, "adef:gi:Llm:p:rs:t:v:y:z:")) != -1) {
135		switch (c) {
136		case 'a':
137			flag |= ADD;
138			sawaflag = 1;
139			break;
140		case 'd':
141			flag |= DISABLE;
142			break;
143		case 'e':
144			flag |= ENABLE;
145			break;
146		case 'f':
147			flag |= ADD;
148			while (*optarg) {
149				switch (*optarg++) {
150				case 'u':
151					flags |= U_FLAG;
152					break;
153				case 'x':
154					flags |= X_FLAG;
155					break;
156				default:
157					badargs[badcnt++] = *(optarg - 1);
158					break;
159				}
160			}
161			/* null terminate just in case anything is there */
162			badargs[badcnt] = '\0';
163			break;
164		case 'g':
165			flag |= CONFIG;
166			break;
167		case 'i':
168			if (strchr(optarg, '\n')) {
169				Saferrno = E_BADARGS;
170				error(BADINP);
171			}
172			flag |= ADD;
173			id = optarg;
174			break;
175		case 'L':
176			flag |= LIST;
177			break;
178		case 'l':
179			flag |= PLIST;
180			break;
181		case 'm':
182			if (strchr(optarg, '\n')) {
183				Saferrno = E_BADARGS;
184				error(BADINP);
185			}
186			if (*optarg == '\0') {
187				/* this will generate a usage message below */
188				errflg++;
189				break;
190			}
191			flag |= ADD;
192			pmspec = optarg;
193			break;
194		case 'p':
195			if (strchr(optarg, '\n')) {
196				Saferrno = E_BADARGS;
197				error(BADINP);
198			}
199			pmtag = optarg;
200			if (strlen(pmtag) > PMTAGSIZE) {
201				pmtag[PMTAGSIZE] = '\0';
202				(void) fprintf(stderr, "tag too long, truncated to <%s>\n", pmtag);
203			}
204			for (p = pmtag; *p; p++) {
205				if (!isalnum(*p)) {
206					Saferrno = E_BADARGS;
207					error("port monitor tag must be alphanumeric");
208				}
209			}
210			break;
211		case 'r':
212			flag |= REMOVE;
213			break;
214		case 's':
215			if (strchr(optarg, '\n')) {
216				Saferrno = E_BADARGS;
217				error(BADINP);
218			}
219			svctag = optarg;
220			if (strlen(svctag) > SVCTAGSIZE) {
221				svctag[SVCTAGSIZE] = '\0';
222				(void) fprintf(stderr, "svctag too long, truncated to <%s>\n", svctag);
223			}
224			for (p = svctag; *p; p++) {
225				if (!isalnum(*p)) {
226					Saferrno = E_BADARGS;
227					error("service tag must be alphanumeric");
228				}
229			}
230			break;
231		case 't':
232			if (strchr(optarg, '\n')) {
233				Saferrno = E_BADARGS;
234				error(BADINP);
235			}
236			type = optarg;
237			if (strlen(type) > PMTYPESIZE) {
238				type[PMTYPESIZE] = '\0';
239				(void) fprintf(stderr, "type too long, truncated to <%s>\n", type);
240			}
241			for (p = type; *p; p++) {
242				if (!isalnum(*p)) {
243					Saferrno = E_BADARGS;
244					error("port monitor type must be alphanumeric");
245				}
246			}
247			break;
248		case 'v':
249			flag |= ADD;
250			version = atoi(optarg);
251			if (version < 0) {
252				Saferrno = E_BADARGS;
253				error("version number can not be negative");
254			}
255			break;
256		case 'y':
257			if (strchr(optarg, '\n')) {
258				Saferrno = E_BADARGS;
259				error(BADINP);
260			}
261			flag |= ADD;
262			comment = optarg;
263			break;
264		case 'z':
265			if (strchr(optarg, '\n')) {
266				Saferrno = E_BADARGS;
267				error(BADINP);
268			}
269			script = optarg;
270			break;
271		case '?':
272			errflg++;
273		}
274	}
275	if (errflg || (optind < argc))
276		usage(argv[0]);
277
278	if (badcnt) {
279		/* bad flags were given to -f */
280		(void) sprintf(buf, "Invalid request, %s are not valid arguments for \"-f\"", badargs);
281		Saferrno = E_BADARGS;
282		error(buf);
283	}
284
285	uid = getuid();
286
287/*
288 * don't do anything if _sactab isn't the version we understand
289 */
290
291	if ((ret = check_version(VERSION, SACTAB)) == 1) {
292		Saferrno = E_SAFERR;
293		error("_sactab version number is incorrect");
294	}
295	else if (ret == 2) {
296		(void) sprintf(buf, "could not open %s", SACTAB);
297		Saferrno = E_SYSERR;
298		error(buf);
299	}
300	else if (ret == 3) {
301		(void) sprintf(buf, "%s file is corrupt", SACTAB);
302		Saferrno = E_SAFERR;
303		error(buf);
304	}
305
306	switch (flag) {
307	case ADD:
308		if (uid) {
309			Saferrno = E_NOPRIV;
310			error(NOTPRIV);
311		}
312		if (!sawaflag || (pmtag && type) || (!pmtag && !type) || !svctag || !id || !pmspec || (version < 0))
313			usage(argv[0]);
314		add_svc(pmtag, type, svctag, id, pmspec, flags, version, comment, script);
315		break;
316	case REMOVE:
317		if (uid) {
318			Saferrno = E_NOPRIV;
319			error(NOTPRIV);
320		}
321		if (!pmtag || !svctag || type || script)
322			usage(argv[0]);
323		rem_svc(pmtag, svctag);
324		break;
325	case ENABLE:
326		if (uid) {
327			Saferrno = E_NOPRIV;
328			error(NOTPRIV);
329		}
330		if (!pmtag || !svctag || type || script)
331			usage(argv[0]);
332		ed_svc(pmtag, svctag, ENABLE);
333		break;
334	case DISABLE:
335		if (uid) {
336			Saferrno = E_NOPRIV;
337			error(NOTPRIV);
338		}
339		if (!pmtag || !svctag || type || script)
340			usage(argv[0]);
341		ed_svc(pmtag, svctag, DISABLE);
342		break;
343	case LIST:
344		conflag = 1;
345		/* fall through */
346	case PLIST:
347		if ((pmtag && type) || script)
348			usage(argv[0]);
349		list_svcs(pmtag, type, svctag, conflag);
350		break;
351	case CONFIG:
352		if (script && uid) {
353			Saferrno = E_NOPRIV;
354			error(NOTPRIV);
355		}
356		if ((pmtag && type) || (!pmtag && !type) || !svctag || (type && !script))
357			usage(argv[0]);
358		doconf(script, pmtag, type, svctag);
359		break;
360	default:
361		/* we only get here if more than one flag bit was set */
362		usage(argv[0]);
363		/* NOTREACHED */
364	}
365	quit();
366	/* NOTREACHED */
367}
368
369
370/*
371 * usage - print out a usage message
372 *
373 *	args:	cmdname - the name command was invoked with
374 */
375
376void
377usage(cmdname)
378char *cmdname;
379{
380	(void) fprintf(stderr, "Usage:\t%s -a [ -p pmtag | -t type ] -s svctag -i id -m \"pmspecific\"\n", cmdname);
381	(void) fprintf(stderr, "\t\t-v version [ -f xu ] [ -y comment ] [ -z script]\n");
382	(void) fprintf(stderr, "\t%s -r -p pmtag -s svctag\n", cmdname);
383	(void) fprintf(stderr, "\t%s -e -p pmtag -s svctag\n", cmdname);
384	(void) fprintf(stderr, "\t%s -d -p pmtag -s svctag\n", cmdname);
385	(void) fprintf(stderr, "\t%s -l [ -p pmtag | -t type ] [ -s svctag ]\n", cmdname);
386	(void) fprintf(stderr, "\t%s -L [ -p pmtag | -t type ] [ -s svctag ]\n", cmdname);
387	(void) fprintf(stderr, "\t%s -g -p pmtag -s svctag [ -z script ]\n", cmdname);
388	(void) fprintf(stderr, "\t%s -g -s svctag -t type -z script\n", cmdname);
389	Saferrno = E_BADARGS;
390	quit();
391}
392
393
394/*
395 * add_svc - add a service entry
396 *
397 *	args:	tag - port monitor's tag (may be null)
398 *		type - port monitor's type (may be null)
399 *		svctag - service's tag
400 *		id - identity under which service should run
401 *		pmspec - uninterpreted port monitor-specific info
402 *		flags - service flags
403 *		version - version number of port monitor's pmtab
404 *		comment - comment describing service
405 *		script - service's configuration script
406 */
407
408void
409add_svc(tag, type, svctag, id, pmspec, flags, version, comment, script)
410char *tag;
411char *type;
412char *svctag;
413char *id;
414char *pmspec;
415long flags;
416int version;
417char *comment;
418char *script;
419{
420	FILE *fp;			/* scratch file pointer */
421	struct taglist tl;		/* 'list' for degenerate case (1 PM) */
422	register struct taglist *tp = NULL;	/* working pointer */
423	int ret;			/* return code from check_version */
424	char buf[SIZE];			/* scratch buffer */
425	char fname[SIZE];		/* scratch buffer for building names */
426	int added;			/* count number added */
427
428	fp = fopen(SACTAB, "r");
429	if (fp == NULL) {
430		Saferrno = E_SYSERR;
431		error("Could not open _sactab");
432	}
433	if (tag && !find_pm(fp, tag)) {
434		(void) sprintf(buf, "Invalid request, %s does not exist", tag);
435		Saferrno = E_NOEXIST;
436		error(buf);
437	}
438	if (type && !(tp = find_type(fp, type))) {
439		(void) sprintf(buf, "Invalid request, %s does not exist", type);
440		Saferrno = E_NOEXIST;
441		error(buf);
442	}
443	(void) fclose(fp);
444
445	if (tag) {
446
447/*
448 * treat the case of 1 PM as a degenerate case of a list of PMs from a
449 * type specification.  Build the 'list' here.
450 */
451
452		tp = &tl;
453		tp->t_next = NULL;
454		(void) strcpy(tp->t_tag, tag);
455	}
456
457	added = 0;
458	while (tp) {
459		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
460		if ((ret = check_version(version, fname)) == 1) {
461			(void) sprintf(buf, "%s version number is incorrect", fname);
462			Saferrno = E_SAFERR;
463			error(buf);
464		}
465		else if (ret == 2) {
466			(void) sprintf(buf, "could not open %s", fname);
467			Saferrno = E_SYSERR;
468			error(buf);
469		}
470		else if (ret == 3) {
471			(void) sprintf(buf, "%s file is corrupt", fname);
472			Saferrno = E_SAFERR;
473			error(buf);
474		}
475		fp = fopen(fname, "r");
476		if (fp == NULL) {
477			(void) sprintf(buf, "Could not open %s", fname);
478			Saferrno = E_SYSERR;
479			error(buf);
480		}
481		if (find_svc(fp, tp->t_tag, svctag)) {
482			if (tag) {
483				/* special case of tag only */
484				(void) sprintf(buf, "Invalid request, %s already exists under %s", svctag, tag);
485				Saferrno = E_DUP;
486				error(buf);
487			}
488			else {
489				(void) fprintf(stderr, "warning - %s already exists under %s - ignoring\n", svctag, tp->t_tag);
490				tp = tp->t_next;
491				(void) fclose(fp);
492				continue;
493			}
494		}
495		(void) fclose(fp);
496
497/*
498 * put in the config script, if specified
499*/
500
501		if (script) {
502			(void) sprintf(fname, "%s/%s", tp->t_tag, svctag);
503			if (do_config(script, fname)) {
504				/* do_config put out any messages */
505				tp = tp->t_next;
506				continue;
507			}
508		}
509
510/*
511 * add the line
512 */
513
514		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
515		fp = fopen(fname, "a");
516		if (fp == NULL) {
517			(void) sprintf(buf, "Could not open %s", fname);
518			Saferrno = E_SYSERR;
519			error(buf);
520		}
521		(void) fprintf(fp, "%s:%s:%s:reserved:reserved:reserved:%s#%s\n",
522			svctag, (flags ? pflags(flags, FALSE) : ""), id, pmspec,
523			(comment ? comment : ""));
524		(void) fclose(fp);
525		added++;
526
527/*
528 * tell the SAC to to tell PM to read _pmtab
529 */
530
531		(void) tell_sac(tp->t_tag);
532		tp = tp->t_next;
533	}
534	if (added == 0) {
535		Saferrno = E_SAFERR;
536		error("No services added");
537	}
538	return;
539}
540
541
542/*
543 * rem_svc - remove a service
544 *
545 *	args:	pmtag - tag of port monitor responsible for the service
546 *		svctag - tag of the service to be removed
547 */
548
549void
550rem_svc(pmtag, svctag)
551char *pmtag;
552char *svctag;
553{
554	FILE *fp;		/* scratch file pointer */
555	FILE *tfp;		/* file pointer for temp file */
556	int line;		/* line number entry is on */
557	char *tname;		/* temp file name */
558	char buf[SIZE];		/* scratch buffer */
559	char fname[SIZE];	/* path to correct _pmtab */
560
561	fp = fopen(SACTAB, "r");
562	if (fp == NULL) {
563		Saferrno = E_SYSERR;
564		error("Could not open _sactab");
565	}
566	if (!find_pm(fp, pmtag)) {
567		(void) sprintf(buf, "Invalid request, %s does not exist", pmtag);
568		Saferrno = E_NOEXIST;
569		error(buf);
570	}
571	(void) fclose(fp);
572
573	(void) sprintf(fname, "%s/_pmtab", pmtag);
574	(void) sprintf(buf, "%s/%s", HOME, fname);
575	fp = fopen(buf, "r");
576	if (fp == NULL) {
577		(void) sprintf(buf, "Could not open %s/%s", HOME, fname);
578		Saferrno = E_SYSERR;
579		error(buf);
580	}
581	if ((line = find_svc(fp, pmtag, svctag)) == 0) {
582		(void) sprintf(buf, "Invalid request, %s does not exist under %s", svctag, pmtag);
583		Saferrno = E_NOEXIST;
584		error(buf);
585	}
586	tname = make_tempname(fname);
587	tfp = open_temp(tname);
588	if (line != 1) {
589		if (copy_file(fp, tfp, 1, line - 1)) {
590			(void) unlink(tname);
591			Saferrno = E_SYSERR;
592			error("error accessing temp file");
593		}
594	}
595	if (copy_file(fp, tfp, line + 1, -1)) {
596		(void) unlink(tname);
597		Saferrno = E_SYSERR;
598		error("error accessing temp file");
599	}
600	(void) fclose(fp);
601	if (fclose(tfp) == EOF) {
602		(void) unlink(tname);
603		Saferrno = E_SYSERR;
604		error("error closing tempfile");
605	}
606	/* note - replace only returns if successful */
607	replace(fname, tname);
608
609/*
610 * tell the SAC to to tell PM to read _pmtab
611 */
612
613	if (tell_sac(pmtag)) {
614
615/*
616 * if we got rid of the service, try to remove the config script too.
617 * Don't check return status since it may not have existed anyhow.
618 */
619
620		(void) sprintf(buf, "%s/%s/%s", HOME, pmtag, svctag);
621		(void) unlink(buf);
622		return;
623	}
624}
625
626
627
628/*
629 * ed_svc - enable or disable a particular service
630 *
631 *	args:	pmtag - tag of port monitor responsible for the service
632 *		svctag - tag of service to be enabled or disabled
633 *		flag - operation to perform (ENABLE or DISABLE)
634 */
635
636void
637ed_svc(pmtag, svctag, flag)
638char *pmtag;
639char *svctag;
640int flag;
641{
642	FILE *fp;		/* scratch file pointer */
643	FILE *tfp;		/* file pointer for temp file */
644	int line;		/* line number entry is on */
645	register char *from;	/* working pointer */
646	register char *to;	/* working pointer */
647	char *tname;		/* temp file name */
648	char *p;		/* scratch pointer */
649	char buf[SIZE];		/* scratch buffer */
650	char tbuf[SIZE];	/* scratch buffer */
651	char fname[SIZE];	/* path to correct _pmtab */
652
653	fp = fopen(SACTAB, "r");
654	if (fp == NULL) {
655		Saferrno = E_SYSERR;
656		error("Could not open _sactab");
657	}
658	if (!find_pm(fp, pmtag)) {
659		(void) sprintf(buf, "Invalid request, %s does not exist", pmtag);
660		Saferrno = E_NOEXIST;
661		error(buf);
662	}
663	(void) fclose(fp);
664
665	(void) sprintf(fname, "%s/_pmtab", pmtag);
666	(void) sprintf(buf, "%s/%s", HOME, fname);
667	fp = fopen(buf, "r");
668	if (fp == NULL) {
669		(void) sprintf(buf, "Could not open %s/%s", HOME, fname);
670		Saferrno = E_SYSERR;
671		error(buf);
672	}
673	if ((line = find_svc(fp, pmtag, svctag)) == 0) {
674		(void) sprintf(buf, "Invalid request, %s does not exist under %s", svctag, pmtag);
675		Saferrno = E_NOEXIST;
676		error(buf);
677	}
678	tname = make_tempname(fname);
679	tfp = open_temp(tname);
680	if (line != 1) {
681		if (copy_file(fp, tfp, 1, line - 1)) {
682			(void) unlink(tname);
683			Saferrno = E_SYSERR;
684			error("error accessing temp file");
685		}
686	}
687
688/*
689 * Note: find_svc above has already read and parsed this entry, thus
690 * we know it to be well-formed, so just change the flags as appropriate
691 */
692
693	if (fgets(buf, SIZE, fp) == NULL) {
694		(void) unlink(tname);
695		Saferrno = E_SYSERR;
696		error("error accessing temp file");
697	}
698	from = buf;
699	to = tbuf;
700
701/*
702 * copy initial portion of entry
703 */
704
705	p = strchr(from, DELIMC);
706	for ( ; from <= p; )
707		*to++ = *from++;
708
709/*
710 * isolate and fix the flags
711 */
712
713	p = strchr(from, DELIMC);
714	for ( ; from < p; ) {
715		if (*from == 'x') {
716			from++;
717			continue;
718		}
719		*to++ = *from++;
720	}
721
722/*
723 * above we removed x flag, if this was a disable operation, stick it in
724 * and also copy the field delimiter
725 */
726
727	if (flag == DISABLE)
728		*to++ = 'x';
729	*to++ = *from++;
730
731/*
732 * copy the rest of the line
733 */
734
735	for ( ; from < &buf[SIZE - 1] ;)
736		*to++ = *from++;
737/***	*to = '\0';  BUG: Don't uncomment it ****/
738
739	(void) fprintf(tfp, "%s", tbuf);
740
741	if (copy_file(fp, tfp, line + 1, -1)) {
742		(void) unlink(tname);
743		Saferrno = E_SYSERR;
744		error("error accessing temp file");
745	}
746	(void) fclose(fp);
747	if (fclose(tfp) == EOF) {
748		(void) unlink(tname);
749		Saferrno = E_SYSERR;
750		error("error closing tempfile");
751	}
752	/* note - replace only returns if successful */
753	replace(fname, tname);
754
755
756/*
757 * tell the SAC to to tell PM to read _pmtab
758 */
759
760	(void) tell_sac(pmtag);
761}
762
763
764/*
765 * doconf - take a config script and have it put where it belongs or
766 *	    output an existing one
767 *
768 *	args:	script - name of file containing script (if NULL, means
769 *			 output existing one instead)
770 *		tag - tag of port monitor that is responsible for the
771 *		      designated service (may be null)
772 *		type - type of port monitor that is responsible for the
773 *		       designated service (may be null)
774 *		svctag - tag of service whose config script we're operating on
775 */
776
777void
778doconf(script, tag, type, svctag)
779char *script;
780char *tag;
781char *type;
782char *svctag;
783{
784	FILE *fp;			/* scratch file pointer */
785	int added;			/* count of config scripts added */
786	struct taglist tl;		/* 'list' for degenerate case (1 PM) */
787	register struct taglist *tp = NULL;	/* working pointer */
788	char buf[SIZE];			/* scratch buffer */
789	char fname[SIZE];		/* scratch buffer for names */
790
791	fp = fopen(SACTAB, "r");
792	if (fp == NULL) {
793		Saferrno = E_SYSERR;
794		error("Could not open _sactab");
795	}
796	if (tag && !find_pm(fp, tag)) {
797		(void) sprintf(buf, "Invalid request, %s does not exist", tag);
798		Saferrno = E_NOEXIST;
799		error(buf);
800	}
801	if (type && !(tp = find_type(fp, type))) {
802		(void) sprintf(buf, "Invalid request, %s does not exist", type);
803		Saferrno = E_NOEXIST;
804		error(buf);
805	}
806	(void) fclose(fp);
807
808	if (tag) {
809
810/*
811 * treat the case of 1 PM as a degenerate case of a list of PMs from a
812 * type specification.  Build the 'list' here.
813 */
814
815		tp = &tl;
816		tp->t_next = NULL;
817		(void) strcpy(tp->t_tag, tag);
818	}
819
820	added = 0;
821	while (tp) {
822		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
823		fp = fopen(fname, "r");
824		if (fp == NULL) {
825			(void) sprintf(buf, "Could not open %s", fname);
826			Saferrno = E_SYSERR;
827			error(buf);
828		}
829		if (!find_svc(fp, tp->t_tag, svctag)) {
830			if (tag) {
831				/* special case of tag only */
832				(void) sprintf(buf, "Invalid request, %s does not exist under %s", svctag, tag);
833				Saferrno = E_NOEXIST;
834				error(buf);
835			}
836			else {
837				(void) fprintf(stderr, "warning - %s does not exist under %s - ignoring\n", svctag, tp->t_tag);
838				Saferrno = E_NOEXIST;
839				tp = tp->t_next;
840				(void) fclose(fp);
841				continue;
842			}
843		}
844		(void) fclose(fp);
845
846		(void) sprintf(fname, "%s/%s", tp->t_tag, svctag);
847
848/*
849 * do_config does all the real work (keep track if any errors occurred)
850 */
851
852		if (do_config(script, fname) == 0)
853			added++;
854		tp = tp->t_next;
855	}
856	if (added == 0) {
857		Saferrno = E_SAFERR;
858		error("No configuration scripts installed");
859	}
860	return;
861}
862
863
864/*
865 * tell_sac - use sacadm to tell the sac to tell a port monitor to read
866 *	its _pmtab.  Return TRUE on success, FALSE on failure.
867 *
868 *	args:	tag - tag of port monitor to be notified
869 */
870
871
872int
873tell_sac(char *tag)
874{
875	pid_t pid;	/* returned pid from fork */
876	int status;	/* return status from sacadm child */
877
878	if ((pid = fork()) < 0) {
879		(void) fprintf(stderr, "warning - fork failed - could not notify <%s> about modified table\n", tag);
880		(void) fprintf(stderr, "try executing the command \"sacadm -x -p %s\"\n", tag);
881		Saferrno = E_SYSERR;
882		return(FALSE);
883	}
884	else if (pid) {
885		/* parent */
886		(void) wait(&status);
887		if (status) {
888			if (((status >> 8) & 0xff) == E_PMNOTRUN) {
889				(void) fprintf(stderr, "warning - port monitor, %s is not running\n", tag);
890				return (FALSE);
891			}
892			if (((status >> 8) & 0xff) == E_SACNOTRUN) {
893				Saferrno = E_SACNOTRUN;
894			} else {
895				Saferrno = E_SYSERR;
896			}
897			(void) fprintf(stderr,
898			    "warning - could not notify <%s> about modified"
899			    " table\n", tag);
900			(void) fprintf(stderr, "try executing the command"
901			    " \"sacadm -x -p %s\"\n", tag);
902			return(FALSE);
903		}
904		else {
905			return(TRUE);
906		}
907	}
908	else {
909		/* set IFS for security */
910		(void) putenv("IFS=\" \"");
911		/* muffle sacadm warning messages */
912		(void) fclose(stderr);
913		(void) fopen("/dev/null", "w");
914		(void) execl("/usr/sbin/sacadm", "sacadm", "-x", "-p", tag, 0);
915
916/*
917 * if we got here, it didn't work, exit status will clue in parent to
918 * put out the warning
919 */
920
921		exit(1);
922	}
923	/* NOTREACHED */
924}
925
926
927/*
928 * list_svcs - list information about services
929 *
930 *	args:	pmtag - tag of port monitor responsible for the service
931 *			(may be null)
932 *		type - type of port monitor responsible for the service
933 *		       (may be null)
934 *		svctag - tag of service to be listed (may be null)
935 *		oflag - true if output should be easily parseable
936 */
937
938void
939list_svcs(pmtag, type, svctag, oflag)
940char *pmtag;
941char *type;
942char *svctag;
943{
944	FILE *fp;				/* scratch file pointer */
945	register struct taglist *tp;		/* pointer to PM list */
946	int nprint = 0;				/* count # of svcs printed */
947	struct pmtab pmtab;			/* place to hold parsed info */
948	register struct pmtab *pp = &pmtab;	/* and a pointer to it */
949	register char *p;			/* working pointer */
950	char buf[SIZE];				/* scratch buffer */
951	char fname[SIZE];			/* scratch buffer for building names */
952
953	fp = fopen(SACTAB, "r");
954	if (fp == NULL) {
955		Saferrno = E_SYSERR;
956		error("Could not open _sactab");
957	}
958	if (pmtag && !find_pm(fp, pmtag)) {
959		(void) sprintf(buf, "Invalid request, %s does not exist", pmtag);
960		Saferrno = E_NOEXIST;
961		error(buf);
962	}
963	rewind(fp);
964	if (type) {
965		tp = find_type(fp, type);
966		if (tp == NULL) {
967			(void) sprintf(buf, "Invalid request, %s does not exist", type);
968			Saferrno = E_NOEXIST;
969			error(buf);
970		}
971	}
972	else
973		tp = find_type(fp, NULL);
974	(void) fclose(fp);
975
976	while (tp) {
977		if (pmtag && strcmp(tp->t_tag, pmtag)) {
978			/* not interested in this port monitor */
979			tp = tp->t_next;
980			continue;
981		}
982		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
983		fp = fopen(fname, "r");
984		if (fp == NULL) {
985			(void) sprintf(buf, "Could not open %s", fname);
986			Saferrno = E_SYSERR;
987			error(buf);
988		}
989		while (fgets(buf, SIZE, fp)) {
990			p = trim(buf);
991			if (*p == '\0')
992				continue;
993			parseline(p, pp, tp->t_tag);
994			if (!svctag || !strcmp(pp->p_tag, svctag)) {
995				if (oflag) {
996					(void) printf("%s:%s:%s:%s:%s:%s:%s:%s:%s#%s\n",
997						tp->t_tag, tp->t_type, pp->p_tag,
998						pflags(pp->p_flags, FALSE),
999						pp->p_id, pp->p_res1, pp->p_res2,
1000						pp->p_res3,pp->p_pmspec, Comment);
1001				}
1002				else {
1003					if (nprint == 0) {
1004						(void) printf("PMTAG          PMTYPE         SVCTAG         FLGS ID       <PMSPECIFIC>\n");
1005					}
1006					(void) printf("%-14s %-14s %-14s %-4s %-8s %s #%s\n", tp->t_tag, tp->t_type, pp->p_tag,
1007						pflags(pp->p_flags, TRUE), pp->p_id, pspec(pp->p_pmspec), Comment);
1008				}
1009				nprint++;
1010			}
1011		}
1012		if (!feof(fp)) {
1013			(void) sprintf(buf, "error reading %s", fname);
1014			Saferrno = E_SYSERR;
1015			error(buf);
1016		}
1017		else {
1018			(void) fclose(fp);
1019			tp = tp->t_next;
1020		}
1021	}
1022	/* if we didn't find any valid ones, indicate an error */
1023	if (nprint == 0) {
1024		if (svctag)
1025			(void) fprintf(stderr, "Service <%s> does not exist\n", svctag);
1026		else
1027			(void) fprintf(stderr, "No services defined\n");
1028		Saferrno = E_NOEXIST;
1029	}
1030	return;
1031}
1032
1033
1034/*
1035 * find_svc - find an entry in _pmtab for a particular service tag
1036 *
1037 *	args:	fp - file pointer for _pmtab
1038 *		tag - port monitor tag (for error reporting)
1039 *		svctag - tag of service we're looking for
1040 */
1041
1042int
1043find_svc(FILE *fp, char *tag, char *svctag)
1044{
1045	register char *p;	/* working pointer */
1046	int line = 0;		/* line number we found entry on */
1047	struct pmtab pmtab;	/* place to hold parsed info */
1048	static char buf[SIZE];	/* scratch buffer */
1049
1050	while (fgets(buf, SIZE, fp)) {
1051		line++;
1052		p = trim(buf);
1053		if (*p == '\0')
1054			continue;
1055		parseline(p, &pmtab, tag);
1056		if (!(strcmp(pmtab.p_tag, svctag)))
1057			return(line);
1058	}
1059	if (!feof(fp)) {
1060		(void) sprintf(buf, "error reading %s/%s/_pmtab", HOME, tag);
1061		Saferrno = E_SYSERR;
1062		error(buf);
1063		/* NOTREACHED */
1064		return (0);
1065	} else
1066		return (0);
1067}
1068
1069
1070/*
1071 * parseline - parse a line from _pmtab.  This routine will return if the
1072 *		parse wa successful, otherwise it will output an error and
1073 *		exit.
1074 *
1075 *	args:	p - pointer to the data read from the file (note - this is
1076 *		    a static data region, so we can point into it)
1077 *		pp - pointer to a structure in which the separated fields
1078 *		     are placed
1079 *		tag - port monitor tag (for error reporting)
1080 *
1081 *	A line in the file has the following format:
1082 *
1083 *	tag:flags:identity:reserved:reserved:reserved:PM_spec_info # comment
1084 */
1085
1086
1087void
1088parseline(p, pp, tag)
1089register char *p;
1090register struct pmtab *pp;
1091char *tag;
1092{
1093	char buf[SIZE];	/* scratch buffer */
1094
1095/*
1096 * get the service tag
1097 */
1098
1099	p = nexttok(p, DELIM, FALSE);
1100	if (p == NULL) {
1101		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
1102		Saferrno = E_SAFERR;
1103		error(buf);
1104	}
1105	if (strlen(p) > PMTAGSIZE) {
1106		p[PMTAGSIZE] = '\0';
1107		(void) fprintf(stderr, "tag too long, truncated to <%s>", p);
1108	}
1109	pp->p_tag = p;
1110
1111/*
1112 * get the flags
1113 */
1114
1115	p = nexttok(NULL, DELIM, FALSE);
1116	if (p == NULL) {
1117		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
1118		Saferrno = E_SAFERR;
1119		error(buf);
1120	}
1121	pp->p_flags = 0;
1122	while (*p) {
1123		switch (*p++) {
1124		case 'u':
1125			pp->p_flags |= U_FLAG;
1126			break;
1127		case 'x':
1128			pp->p_flags |= X_FLAG;
1129			break;
1130		default:
1131			(void) sprintf(buf, "Unrecognized flag <%c>", *(p - 1));
1132			Saferrno = E_SAFERR;
1133			error(buf);
1134			break;
1135		}
1136	}
1137
1138/*
1139 * get the identity
1140 */
1141
1142	p = nexttok(NULL, DELIM, FALSE);
1143	if (p == NULL) {
1144		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
1145		Saferrno = E_SAFERR;
1146		error(buf);
1147	}
1148	pp->p_id = p;
1149
1150/*
1151 * get the first reserved field
1152 */
1153
1154	p = nexttok(NULL, DELIM, FALSE);
1155	if (p == NULL) {
1156		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
1157		Saferrno = E_SAFERR;
1158		error(buf);
1159	}
1160	pp->p_res1 = p;
1161
1162/*
1163 * get the second reserved field
1164 */
1165
1166	p = nexttok(NULL, DELIM, FALSE);
1167	if (p == NULL) {
1168		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
1169		Saferrno = E_SAFERR;
1170		error(buf);
1171	}
1172	pp->p_res2 = p;
1173
1174/*
1175 * get the third reserved field
1176 */
1177
1178	p = nexttok(NULL, DELIM, FALSE);
1179	if (p == NULL) {
1180		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
1181		Saferrno = E_SAFERR;
1182		error(buf);
1183	}
1184	pp->p_res3 = p;
1185
1186/*
1187 * the rest is the port monitor specific info
1188 */
1189
1190	p = nexttok(NULL, DELIM, TRUE);
1191	if (p == NULL) {
1192		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
1193		Saferrno = E_SAFERR;
1194		error(buf);
1195	}
1196	pp->p_pmspec = p;
1197	return;
1198}
1199
1200
1201/*
1202 * pspec - format port monitor specific information
1203 *
1204 *	args:	spec - port monitor specific info, separated by
1205 *		       field separater character (may be escaped by \)
1206 */
1207
1208char *
1209pspec(spec)
1210char *spec;
1211{
1212	static char buf[SIZE];		/* returned string */
1213	register char *from;		/* working pointer */
1214	register char *to;		/* working pointer */
1215	int newflag;			/* flag indicating new field */
1216
1217	to = buf;
1218	from = spec;
1219	newflag = 1;
1220	while (*from) {
1221		switch (*from) {
1222		case ':':
1223			if (newflag) {
1224				*to++ = '-';
1225			}
1226			*to++ = ' ';
1227			from++;
1228			newflag = 1;
1229			break;
1230		case '\\':
1231			if (*(from + 1) == ':') {
1232				*to++ = ':';
1233				/* skip over \: */
1234				from += 2;
1235			}
1236			else
1237				*to++ = *from++;
1238			newflag = 0;
1239			break;
1240		default:
1241			newflag = 0;
1242			*to++ = *from++;
1243		}
1244	}
1245	*to = '\0';
1246	return(buf);
1247}
1248
1249
1250/*
1251 * pflags - put service flags into intelligible form for output
1252 *
1253 *	args:	flags - binary representation of flags
1254 *		dflag - true if a "-" should be returned if no flags
1255 */
1256
1257char *
1258pflags(flags, dflag)
1259long flags;
1260int dflag;
1261{
1262	register int i;			/* scratch counter */
1263	static char buf[SIZE];		/* formatted flags */
1264
1265	if (flags == 0) {
1266		if (dflag)
1267			return("-");
1268		else
1269			return("");
1270	}
1271	i = 0;
1272	if (flags & U_FLAG) {
1273		buf[i++] = 'u';
1274		flags &= ~U_FLAG;
1275	}
1276	if (flags & X_FLAG) {
1277		buf[i++] = 'x';
1278		flags &= ~X_FLAG;
1279	}
1280	if (flags) {
1281		Saferrno = E_SAFERR;
1282		error("Internal error in pflags");
1283	}
1284	buf[i] = '\0';
1285	return(buf);
1286}
1287
1288
1289/*
1290 * find_type - find entries in _sactab for a particular port monitor type
1291 *
1292 *	args:	fp - file pointer for _sactab
1293 *		type - type of port monitor we're looking for (if type is
1294 *		       null, it means find all PMs)
1295 */
1296
1297struct taglist *
1298find_type(fp, type)
1299FILE *fp;
1300char *type;
1301{
1302	register char *p;			/* working pointer */
1303	struct sactab stab;			/* place to hold parsed info */
1304	register struct sactab *sp = &stab;	/* and a pointer to it */
1305	char buf[SIZE];				/* scratch buffer */
1306	struct taglist *thead;			/* linked list of tags */
1307	register struct taglist *temp;		/* scratch pointer */
1308
1309	thead = NULL;
1310	while (fgets(buf, SIZE, fp)) {
1311		p = trim(buf);
1312		if (*p == '\0')
1313			continue;
1314		parse(p, sp);
1315		if ((type == NULL) || !(strcmp(sp->sc_type, type))) {
1316			temp = (struct taglist *) malloc(sizeof(struct taglist));
1317			if (temp == NULL) {
1318				Saferrno = E_SYSERR;
1319				error("malloc failed");
1320			}
1321			temp->t_next = thead;
1322			(void) strcpy(temp->t_tag, sp->sc_tag);
1323			(void) strcpy(temp->t_type, sp->sc_type);
1324			thead = temp;
1325		}
1326	}
1327	if (!feof(fp)) {
1328		Saferrno = E_SYSERR;
1329		error("error reading _sactab");
1330		/* NOTREACHED */
1331		return (0);
1332	} else
1333		return (thead ? thead : NULL);
1334}
1335