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