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