xref: /illumos-gate/usr/src/cmd/saf/pmadm.c (revision de81e71e031139a0a7f13b7bf64152c3faa76698)
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 
58 char	*pflags();
59 char	*pspec();
60 struct	taglist	*find_type();
61 void	usage();
62 void	parseline();
63 void	add_svc();
64 void	rem_svc();
65 void	ed_svc();
66 void	list_svcs();
67 void	doconf();
68 
69 /*
70  * format of a _pmtab entry - used to hold parsed info
71  */
72 
73 struct	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 
88 struct	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 
101 int	Saferrno;	/* internal `errno' for exit */
102 
103 
104 /*
105  * main - scan args for pmadm and call appropriate handling code
106  */
107 
108 int
109 main(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 
376 void
377 usage(cmdname)
378 char *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 
408 void
409 add_svc(tag, type, svctag, id, pmspec, flags, version, comment, script)
410 char *tag;
411 char *type;
412 char *svctag;
413 char *id;
414 char *pmspec;
415 long flags;
416 int version;
417 char *comment;
418 char *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 
549 void
550 rem_svc(pmtag, svctag)
551 char *pmtag;
552 char *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 
636 void
637 ed_svc(pmtag, svctag, flag)
638 char *pmtag;
639 char *svctag;
640 int 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 
777 void
778 doconf(script, tag, type, svctag)
779 char *script;
780 char *tag;
781 char *type;
782 char *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 
872 int
873 tell_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 
938 void
939 list_svcs(pmtag, type, svctag, oflag)
940 char *pmtag;
941 char *type;
942 char *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 
1042 int
1043 find_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 
1087 void
1088 parseline(p, pp, tag)
1089 register char *p;
1090 register struct pmtab *pp;
1091 char *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 
1208 char *
1209 pspec(spec)
1210 char *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 
1257 char *
1258 pflags(flags, dflag)
1259 long flags;
1260 int 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 
1297 struct taglist *
1298 find_type(fp, type)
1299 FILE *fp;
1300 char *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