xref: /titanic_52/usr/src/cmd/saf/sacadm.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
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 (c) 1999 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"       /* SVr4.0 1.15*/
32 
33 
34 # include <stdio.h>
35 # include <fcntl.h>
36 # include <errno.h>
37 # include <sys/types.h>
38 # include <sys/stat.h>
39 # include <signal.h>
40 # include <unistd.h>
41 # include <sac.h>
42 # include "misc.h"
43 # include "structs.h"
44 # include "adm.h"
45 # include "extern.h"
46 
47 
48 /*
49  * functions
50  */
51 
52 char	*pflags();
53 char	*getfield();
54 void	add_pm();
55 void	cleandirs();
56 void	rem_pm();
57 void	start_pm();
58 void	kill_pm();
59 void	enable_pm();
60 void	disable_pm();
61 void	list_pms();
62 void	read_db();
63 void	sendcmd();
64 void	checkresp();
65 void	single_print();
66 void	catch();
67 void	usage();
68 
69 # define START		0x1	/* -s seen */
70 # define KILL		0x2	/* -k seen */
71 # define ENABLE		0x4	/* -e seen */
72 # define DISABLE	0x8	/* -d seen */
73 # define PLIST		0x10	/* -l seen */
74 # define LIST		0x20	/* -L seen */
75 # define DBREAD		0x40	/* -x seen */
76 # define CONFIG		0x80	/* -G seen */
77 # define PCONFIG	0x100	/* -g seen */
78 # define ADD		0x200	/* -a or other required options seen */
79 # define REMOVE		0x400	/* -r seen */
80 
81 /*
82  * common error messages
83  */
84 
85 # define NOTPRIV	"User not privileged for operation"
86 # define SACERR		"Can not contact SAC"
87 # define BADINP		"Embedded newlines not allowed"
88 
89 
90 int	Saferrno;	/* internal `errno' for exit */
91 
92 
93 /*
94  * main - scan args for sacadm and call appropriate handling code
95  */
96 
97 main(argc, argv)
98 int argc;
99 char *argv[];
100 {
101 	int c;			/* option letter */
102 	uid_t uid;		/* invoker's real uid */
103 	int ret;		/* return code from check_version */
104 	int flag = 0;		/* flag to record requested operations */
105 	int errflg = 0;		/* error indicator */
106 	int version = -1;	/* argument to -v */
107 	int count = 0;		/* argument to -n */
108 	int badcnt = 0;		/* count of bad args to -f */
109 	int sawaflag = 0;	/* true if actually saw -a */
110 	int conflag = 0;	/* true if output should be in condensed form */
111 	long flags = 0;		/* arguments to -f */
112 	FILE *fp;		/* scratch file pointer */
113 	char *pmtag = NULL;	/* argument to -p */
114 	char *type = NULL;	/* argument to -t */
115 	char *script = NULL;	/* argument to -z */
116 	char *command = NULL;	/* argument to -c */
117 	char *comment = " ";	/* argument to -y */
118 	char badargs[BADFARGSIZE];	/* place to hold bad args to -f */
119 	char buf[SIZE];		/* scratch buffer */
120 	register char *p;	/* scratch pointer */
121 
122 	if (argc == 1)
123 		usage(argv[0]);
124 	while ((c = getopt(argc, argv, "ac:def:GgkLln:p:rst:v:xy:z:")) != -1) {
125 		switch (c) {
126 		case 'a':
127 			flag |= ADD;
128 			sawaflag = 1;
129 			break;
130 		case 'c':
131 			flag |= ADD;
132 			if (strchr(optarg, '\n')) {
133 				Saferrno = E_BADARGS;
134 				error(BADINP);
135 			}
136 			command = optarg;
137 			if (*command != '/') {
138 				Saferrno = E_BADARGS;
139 				error("command must be a full pathname");
140 			}
141 			break;
142 		case 'd':
143 			flag |= DISABLE;
144 			break;
145 		case 'e':
146 			flag |= ENABLE;
147 			break;
148 		case 'f':
149 			flag |= ADD;
150 			while (*optarg) {
151 				switch (*optarg++) {
152 				case 'd':
153 					flags |= D_FLAG;
154 					break;
155 				case 'x':
156 					flags |= X_FLAG;
157 					break;
158 				default:
159 					if (badcnt < (BADFARGSIZE -1))
160 					    badargs[badcnt++] = *(optarg - 1);
161 					break;
162 				}
163 			}
164 			/* null terminate just in case anything is there */
165 			badargs[badcnt] = '\0';
166 			break;
167 		case 'G':
168 			flag |= CONFIG;
169 			break;
170 		case 'g':
171 			flag |= PCONFIG;
172 			break;
173 		case 'k':
174 			flag |= KILL;
175 			break;
176 		case 'L':
177 			flag |= LIST;
178 			break;
179 		case 'l':
180 			flag |= PLIST;
181 			break;
182 		case 'n':
183 			flag |= ADD;
184 			count = atoi(optarg);
185 			if (count < 0) {
186 				Saferrno = E_BADARGS;
187 				error("restart count can not be negative");
188 			}
189 			break;
190 		case 'p':
191 			pmtag = optarg;
192 			if (strchr(optarg, '\n')) {
193 				Saferrno = E_BADARGS;
194 				error(BADINP);
195 			}
196 			if (strlen(pmtag) > PMTAGSIZE) {
197 				pmtag[PMTAGSIZE] = '\0';
198 				(void) fprintf(stderr, "tag too long, truncated to <%s>\n", pmtag);
199 			}
200 			for (p = pmtag; *p; p++) {
201 				if (!isalnum(*p)) {
202 					Saferrno = E_BADARGS;
203 					error("port monitor tag must be alphanumeric");
204 				}
205 			}
206 			break;
207 		case 'r':
208 			flag |= REMOVE;
209 			break;
210 		case 's':
211 			flag |= START;
212 			break;
213 		case 't':
214 			type = optarg;
215 			if (strchr(optarg, '\n')) {
216 				Saferrno = E_BADARGS;
217 				error(BADINP);
218 			}
219 			if (strlen(type) > PMTYPESIZE) {
220 				type[PMTYPESIZE] = '\0';
221 				(void) fprintf(stderr, "type too long, truncated to <%s>\n", type);
222 			}
223 			for (p = type; *p; p++) {
224 				if (!isalnum(*p)) {
225 					Saferrno = E_BADARGS;
226 					error("port monitor type must be alphanumeric");
227 				}
228 			}
229 			break;
230 		case 'v':
231 			flag |= ADD;
232 			version = atoi(optarg);
233 			if (version < 0) {
234 				Saferrno = E_BADARGS;
235 				error("version number can not be negative");
236 			}
237 			break;
238 		case 'x':
239 			flag |= DBREAD;
240 			break;
241 		case 'y':
242 			flag |= ADD;
243 			if (strchr(optarg, '\n')) {
244 				Saferrno = E_BADARGS;
245 				error(BADINP);
246 			}
247 			comment = optarg;
248 			break;
249 		case 'z':
250 			if (strchr(optarg, '\n')) {
251 				Saferrno = E_BADARGS;
252 				error(BADINP);
253 			}
254 			script = optarg;
255 			break;
256 		case '?':
257 			errflg++;
258 		}
259 	}
260 	if (errflg || (optind < argc))
261 		usage(argv[0]);
262 
263 	if (badcnt) {
264 		/* bad flags were given to -f */
265 		Saferrno = E_BADARGS;
266 		(void) sprintf(buf,
267 		    "Invalid request, %s are not valid arguments for \"-f\"",
268 			badargs);
269 		error(buf);
270 	}
271 
272 	if ((ret = check_version(VERSION, SACTAB)) == 1) {
273 		Saferrno = E_SAFERR;
274 		error("_sactab version number is incorrect");
275 	}
276 	else if (ret == 2) {
277 		(void) sprintf(buf, "could not open %s", SACTAB);
278 		Saferrno = E_SYSERR;
279 		error(buf);
280 	}
281 	else if (ret == 3) {
282 		(void) sprintf(buf, "%s file is corrupt", SACTAB);
283 		Saferrno = E_SAFERR;
284 		error(buf);
285 	}
286 	uid = getuid();
287 	switch (flag) {
288 	case ADD:
289 		if (uid) {
290 			Saferrno = E_NOPRIV;
291 			error(NOTPRIV);
292 		}
293 		if (!sawaflag || !pmtag || !type || !command || (version < 0))
294 			usage(argv[0]);
295 		add_pm(pmtag, type, command, version, flags, count, script, comment);
296 		break;
297 	case REMOVE:
298 		if (uid) {
299 			Saferrno = E_NOPRIV;
300 			error(NOTPRIV);
301 		}
302 		if (!pmtag || type || script)
303 			usage(argv[0]);
304 		rem_pm(pmtag);
305 		break;
306 	case START:
307 		if (uid) {
308 			Saferrno = E_NOPRIV;
309 			error(NOTPRIV);
310 		}
311 		if (!pmtag || type || script)
312 			usage(argv[0]);
313 		start_pm(pmtag);
314 		break;
315 	case KILL:
316 		if (uid) {
317 			Saferrno = E_NOPRIV;
318 			error(NOTPRIV);
319 		}
320 		if (!pmtag || type || script)
321 			usage(argv[0]);
322 		kill_pm(pmtag);
323 		break;
324 	case ENABLE:
325 		if (uid) {
326 			Saferrno = E_NOPRIV;
327 			error(NOTPRIV);
328 		}
329 		if (!pmtag || type || script)
330 			usage(argv[0]);
331 		enable_pm(pmtag);
332 		break;
333 	case DISABLE:
334 		if (uid) {
335 			Saferrno = E_NOPRIV;
336 			error(NOTPRIV);
337 		}
338 		if (!pmtag || type || script)
339 			usage(argv[0]);
340 		disable_pm(pmtag);
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_pms(pmtag, type, conflag);
349 		break;
350 	case DBREAD:
351 		if (uid) {
352 			Saferrno = E_NOPRIV;
353 			error(NOTPRIV);
354 		}
355 		if (type || script)
356 			usage(argv[0]);
357 		read_db(pmtag);
358 		break;
359 	case CONFIG:
360 		if (script && uid) {
361 			Saferrno = E_NOPRIV;
362 			error(NOTPRIV);
363 		}
364 		if (type || pmtag)
365 			usage(argv[0]);
366 		(void) do_config(script, "_sysconfig");
367 		break;
368 	case PCONFIG:
369 		if (script && uid) {
370 			Saferrno = E_NOPRIV;
371 			error(NOTPRIV);
372 		}
373 		if (!pmtag || type)
374 			usage(argv[0]);
375 		fp = fopen(SACTAB, "r");
376 		if (fp == NULL) {
377 			Saferrno = E_SYSERR;
378 			error("Could not open _sactab");
379 		}
380 		if (!find_pm(fp, pmtag)) {
381 			Saferrno = E_NOEXIST;
382 			(void) sprintf(buf, "Invalid request, %s does not exist", pmtag);
383 			error(buf);
384 		}
385 		(void) fclose(fp);
386 		(void) sprintf(buf, "%s/_config", pmtag);
387 		(void) do_config(script, buf);
388 		break;
389 	default:
390 		/* we only get here if more than one flag bit was set */
391 		usage(argv[0]);
392 		/* NOTREACHED */
393 	}
394 	quit();
395 	/* NOTREACHED */
396 }
397 
398 
399 /*
400  * usage - print out a usage message
401  *
402  *	args:	cmdname - the name command was invoked with
403  */
404 
405 void
406 usage(cmdname)
407 char *cmdname;
408 {
409 	(void) fprintf(stderr, "Usage:\t%s -a -p pmtag -t type -c cmd -v ver [ -f dx ] [ -n count ]\n", cmdname);
410 	(void) fprintf(stderr, "\t\t[ -y comment ] [ -z script]\n");
411 	(void) fprintf(stderr, "\t%s -r -p pmtag\n", cmdname);
412 	(void) fprintf(stderr, "\t%s -s -p pmtag\n", cmdname);
413 	(void) fprintf(stderr, "\t%s -k -p pmtag\n", cmdname);
414 	(void) fprintf(stderr, "\t%s -e -p pmtag\n", cmdname);
415 	(void) fprintf(stderr, "\t%s -d -p pmtag\n", cmdname);
416 	(void) fprintf(stderr, "\t%s -l [ -p pmtag | -t type ]\n", cmdname);
417 	(void) fprintf(stderr, "\t%s -L [ -p pmtag | -t type ]\n", cmdname);
418 	(void) fprintf(stderr, "\t%s -g -p pmtag [ -z script ]\n", cmdname);
419 	(void) fprintf(stderr, "\t%s -G [ -z script ]\n", cmdname);
420 	(void) fprintf(stderr, "\t%s -x [ -p pmtag ]\n", cmdname);
421 	Saferrno = E_BADARGS;
422 	quit();
423 }
424 
425 
426 /*
427  * add_pm - add a port monitor entry
428  *
429  *	args:	tag - port monitor's tag
430  *		type - port monitor's type
431  *		command - command string to invoke port monitor
432  *		version - version number of port monitor's pmtab
433  *		flags - port monitor flags
434  *		count - restart count
435  *		script - port monitor's configuration script
436  *		comment - comment describing port monitor
437  */
438 
439 void
440 add_pm(tag, type, command, version, flags, count, script, comment)
441 char *tag;
442 char *type;
443 char *command;
444 int version;
445 long flags;
446 int count;
447 char *script;
448 char *comment;
449 {
450 	FILE *fp;		/* file pointer for _sactab */
451 	int fd;			/* scratch file descriptor */
452 	struct stat statbuf;	/* file status info */
453 	char buf[SIZE];		/* scratch buffer */
454 	char fname[SIZE];	/* scratch buffer for building names */
455 	register int i;		/* scratch variable */
456 
457 	fp = fopen(SACTAB, "r");
458 	if (fp == NULL) {
459 		Saferrno = E_SYSERR;
460 		error("Could not open _sactab");
461 	}
462 	if (find_pm(fp, tag)) {
463 		Saferrno = E_DUP;
464 		(void) sprintf(buf, "Invalid request, %s already exists", tag);
465 		error(buf);
466 	}
467 	(void) fclose(fp);
468 
469 /*
470  * create the directories for it if needed and put in initial files
471  * (/etc/saf and /var/saf)
472  */
473 
474 	for (i = 0; i < 2; i++) {
475 		/* i == 0 do /etc/saf i == 1 do /var/saf */
476 		(void) sprintf(fname, "%s/%s", (i == 0 ) ? HOME : ALTHOME, tag);
477 		if (access(fname, 0) == 0) {
478 			/* something is there, find out what it is */
479 			if (stat(fname, &statbuf) < 0) {
480 				Saferrno = E_SYSERR;
481 				(void) sprintf(buf, "could not stat <%s>", fname);
482 				error(buf);
483 			}
484 			if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
485 				Saferrno = E_SYSERR;
486 				(void) sprintf(buf, "<%s> exists and is not a directory", fname);
487 				error(buf);
488 			}
489 			/* note: this removes the directory too */
490 			(void) sprintf(buf, "rm -rf %s", fname);
491 			if (system(buf) < 0) {
492 				Saferrno = E_SYSERR;
493 				(void) sprintf(buf, "could not remove files under <%s>", fname);
494 				error(buf);
495 			}
496 		}
497 
498 /*
499  * create the directory
500  */
501 
502 		if (mkdir(fname, 0755) < 0) {
503 			Saferrno = E_SYSERR;
504 			(void) sprintf(buf, "could not create directory <%s>", fname);
505 			cleandirs(tag);
506 			error(buf);
507 		}
508 	}
509 
510 /*
511  * put in the config script, if specified
512  */
513 
514 	if (script) {
515 		(void) sprintf(fname, "%s/_config", tag);
516 		if (do_config(script, fname)) {
517 			cleandirs(tag);
518 			/* do_config put out any messages */
519 			quit();
520 		}
521 	}
522 
523 /*
524  * create the communications pipe, but first make sure that the
525  * permissions we specify are what we get
526  */
527 
528 	(void) umask(0);
529 	(void) sprintf(fname, "%s/%s/_pmpipe", HOME, tag);
530 	if (mknod(fname, S_IFIFO | 0600, 0) < 0) {
531 		Saferrno = E_SYSERR;
532 		cleandirs(tag);
533 		error("could not create communications pipe");
534 	}
535 
536 /*
537  * create the _pid file
538  */
539 
540 	(void) sprintf(fname, "%s/%s/_pid", HOME, tag);
541 	if ((fd = creat(fname, 0644)) < 0) {
542 		Saferrno = E_SYSERR;
543 		cleandirs(tag);
544 		error("could not create _pid file");
545 	}
546 	(void) close(fd);
547 
548 /*
549  * create the _pmtab file
550  */
551 
552 	(void) sprintf(fname, "%s/%s/_pmtab", HOME, tag);
553 	if ((fd = creat(fname, 0644)) < 0) {
554 		Saferrno = E_SYSERR;
555 		cleandirs(tag);
556 		error("could not create _pmtab file");
557 	}
558 	(void) sprintf(buf, "%s%d\n", VSTR, version);
559 	if (write(fd, buf, (unsigned) strlen(buf)) != strlen(buf)) {
560 		(void) close(fd);
561 		(void) unlink(fname);
562 		Saferrno = E_SYSERR;
563 		cleandirs(tag);
564 		error("error initializing _pmtab");
565 	}
566 	(void) close(fd);
567 
568 /*
569  * isolate the command name, but remember it since strtok() trashes it
570  */
571 
572 	(void) strcpy(buf, command);
573 	(void) strtok(command, " \t");
574 
575 /*
576  * check out the command - let addition succeed if it doesn't exist (assume
577  * it will be added later); fail anything else
578  */
579 
580 	if (access(command, 0) == 0) {
581 		if (stat(command, &statbuf) < 0) {
582 			Saferrno = E_SYSERR;
583 			(void) fprintf(stderr, "Could not stat <%s>\n", command);
584 			cleandirs(tag);
585 			quit();
586 		}
587 		if (!(statbuf.st_mode & 0111)) {
588 			Saferrno = E_BADARGS;
589 			(void) fprintf(stderr, "%s not executable\n", command);
590 			cleandirs(tag);
591 			quit();
592 		}
593 		if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
594 			Saferrno = E_BADARGS;
595 			(void) fprintf(stderr, "%s not a regular file\n", command);
596 			cleandirs(tag);
597 			quit();
598 		}
599 	}
600 	else {
601 		(void) fprintf(stderr, "warning - %s does not exist\n", command);
602 	}
603 
604 /*
605  * add the line
606  */
607 
608 	fp = fopen(SACTAB, "a");
609 	if (fp == NULL) {
610 		Saferrno = E_SYSERR;
611 		cleandirs(tag);
612 		error("Could not open _sactab");
613 	}
614 	(void) fprintf(fp, "%s:%s:%s:%d:%s\t#%s\n", tag, type,
615 		(flags ? pflags(flags, FALSE) : ""), count, buf,
616 		(comment ? comment : ""));
617 	(void) fclose(fp);
618 
619 
620 /*
621  * tell the SAC to read _sactab if its there (i.e. single user)
622  */
623 
624 	if (sac_home())
625 		read_db(NULL);
626 	return;
627 }
628 
629 
630 /*
631  * cleandirs - remove anything that might have been created (i.e. failed
632  *	addition.  Saferrno is set elsewhere; this is strictly an attempt
633  *	to clean up what mess we've left, so don't check to see if the
634  *	cleanup worked.
635  *
636  *	args:	tag - tag of port monitor whose trees should be removed
637  */
638 
639 void
640 cleandirs(tag)
641 char *tag;
642 {
643 	char buf[SIZE];		/* scratch buffer */
644 
645 	/* note: this removes the directory too, first zap /etc/saf/<tag> */
646 	(void) sprintf(buf, "rm -rf %s/%s", HOME, tag);
647 	(void) system(buf);
648 
649 	/* now remove /var/saf/<tag> */
650 	(void) sprintf(buf, "%s/%s", ALTHOME, tag);
651 	(void) rmdir(buf);
652 }
653 
654 
655 /*
656  * rem_pm - remove a port monitor
657  *
658  *	args:	tag - tag of port monitor to be removed
659  */
660 
661 void
662 rem_pm(tag)
663 char *tag;
664 {
665 	FILE *fp;		/* file pointer for _sactab */
666 	FILE *tfp;		/* file pointer for temp file */
667 	int line;		/* line number entry is on */
668 	char *tname;		/* temp file name */
669 	char buf[SIZE];		/* scratch buffer */
670 
671 	fp = fopen(SACTAB, "r");
672 	if (fp == NULL) {
673 		Saferrno = E_SYSERR;
674 		error("Could not open _sactab");
675 	}
676 	if ((line = find_pm(fp, tag)) == 0) {
677 		Saferrno = E_NOEXIST;
678 		(void) sprintf(buf, "Invalid request, %s does not exist", tag);
679 		error(buf);
680 	}
681 	tname = make_tempname("_sactab");
682 	tfp = open_temp(tname);
683 	if (line != 1) {
684 		if (copy_file(fp, tfp, 1, line - 1)) {
685 			(void) unlink(tname);
686 			Saferrno = E_SYSERR;
687 			error("error accessing temp file");
688 		}
689 	}
690 	if (copy_file(fp, tfp, line + 1, -1)) {
691 		(void) unlink(tname);
692 		Saferrno = E_SYSERR;
693 		error("error accessing temp file");
694 	}
695 	(void) fclose(fp);
696 	if (fclose(tfp) == EOF) {
697 		(void) unlink(tname);
698 		Saferrno = E_SYSERR;
699 		error("error closing tempfile");
700 	}
701 	/* note - replace only returns if successful */
702 	replace("_sactab", tname);
703 
704 /*
705  * tell the SAC to read _sactab if its there (i.e. single user)
706  */
707 
708 	if (sac_home())
709 		read_db(NULL);
710 	return;
711 }
712 
713 
714 /*
715  * start_pm - start a particular port monitor
716  *
717  *	args:	tag - tag of port monitor to be started
718  */
719 
720 void
721 start_pm(tag)
722 char *tag;
723 {
724 	struct admcmd cmd;			/* command structure */
725 	register struct admcmd *ap = &cmd;	/* and a pointer to it */
726 
727 	ap->ac_mtype = AC_START;
728 	(void) strcpy(ap->ac_tag, tag);
729 	ap->ac_pid = getpid();
730 	sendcmd(ap, NULL, tag);
731 	return;
732 }
733 
734 
735 /*
736  * kill_pm - stop a particular port monitor
737  *
738  *	args:	tag - tag of port monitor to be stopped
739  */
740 
741 void
742 kill_pm(tag)
743 char *tag;
744 {
745 	struct admcmd cmd;			/* command structure */
746 	register struct admcmd *ap = &cmd;	/* and a pointer to it */
747 
748 	ap->ac_mtype = AC_KILL;
749 	(void) strcpy(ap->ac_tag, tag);
750 	ap->ac_pid = getpid();
751 	sendcmd(ap, NULL, tag);
752 	return;
753 }
754 
755 
756 /*
757  * enable_pm - enable a particular port monitor
758  *
759  *	args:	tag - tag of port monitor to be enabled
760  */
761 
762 void
763 enable_pm(tag)
764 char *tag;
765 {
766 	struct admcmd cmd;			/* command structure */
767 	register struct admcmd *ap = &cmd;	/* and a pointer to it */
768 
769 	ap->ac_mtype = AC_ENABLE;
770 	(void) strcpy(ap->ac_tag, tag);
771 	ap->ac_pid = getpid();
772 	sendcmd(ap, NULL, tag);
773 	return;
774 }
775 
776 
777 /*
778  * disable_pm - disable a particular port monitor
779  *
780  *	args:	tag - tag of port monitor to be disabled
781  */
782 
783 void
784 disable_pm(tag)
785 char *tag;
786 {
787 	struct admcmd cmd;			/* command structure */
788 	register struct admcmd *ap = &cmd;	/* and a pointer to it */
789 
790 	ap->ac_mtype = AC_DISABLE;
791 	(void) strcpy(ap->ac_tag, tag);
792 	ap->ac_pid = getpid();
793 	sendcmd(ap, NULL, tag);
794 	return;
795 }
796 
797 
798 /*
799  * read_db - tell SAC or a port monitor to read its administrative file.
800  *
801  *	args:	tag - tag of port monitor that should read its administrative
802  *		      file.  If NULL, it means SAC should.
803  */
804 
805 void
806 read_db(tag)
807 char *tag;
808 {
809 	struct admcmd cmd;			/* command structure */
810 	register struct admcmd *ap = &cmd;	/* and a pointer to it */
811 
812 	ap->ac_mtype = (tag) ? AC_PMREAD : AC_SACREAD;
813 	if (tag)
814 		(void) strcpy(ap->ac_tag, tag);
815 	ap->ac_pid = getpid();
816 	sendcmd(ap, NULL, tag);
817 	return;
818 }
819 
820 
821 /*
822  * list_pms - request information about port monitors from SAC and output
823  *		requested info
824  *
825  *	args:	pmtag - tag of port monitor to be listed (may be null)
826  *		pmtype - type of port monitors to be listed (may be null)
827  *		oflag - true if output should be easily parseable
828  */
829 
830 void
831 list_pms(pmtag, pmtype, oflag)
832 char *pmtag;
833 char *pmtype;
834 int oflag;
835 {
836 	struct admcmd acmd;			/* command structure */
837 	register struct admcmd *ap = &acmd;	/* and a pointer to it */
838 	int nprint = 0;				/* count # of PMs printed */
839 	char *p;				/* scratch pointer */
840 	char *tag;				/* returned tag */
841 	char *type;				/* returned type */
842 	char *flags;				/* returned flags */
843 	char *rsmax;				/* returned restart count */
844 	char *state;				/* returned state */
845 	char *cmd;				/* returned command string */
846 	char *comment;				/* returned comment string */
847 
848 /*
849  * if sac isn't there (single user), provide info direct from _sactab
850  * note: when this routine returns, the process exits, so there is no
851  * need to free up any memory
852  */
853 
854 	p = NULL;
855 	if (sac_home()) {
856 		ap->ac_mtype = AC_STATUS;
857 		ap->ac_tag[0] = '\0';
858 		ap->ac_pid = getpid();
859 		sendcmd(ap, &p, NULL);
860 	}
861 	else {
862 		single_print(&p);
863 	}
864 
865 /*
866  * SAC sends back info in condensed form, we have to separate it out
867  * fields come in ':' separated, records are separated by newlines
868  */
869 
870 	while (p && *p) {
871 		tag = getfield(&p, ':');	/* PM tag */
872 		type = getfield(&p, ':');	/* PM type */
873 		flags = getfield(&p, ':');	/* flags */
874 		rsmax = getfield(&p, ':');	/* restart count */
875 		state = pstate((unchar) atoi(getfield(&p, ':')));	/* state in nice output format */
876 		cmd = getfield(&p, ':');	/* command */
877 		comment = getfield(&p, '\n');	/* comment */
878 
879 
880 /*
881  * print out if no selectors specified, else check to see if
882  * a selector matched
883  */
884 
885 		if ((!pmtag && !pmtype) || (pmtag && !strcmp(pmtag, tag)) || (pmtype && !strcmp(pmtype, type))) {
886 			if (oflag) {
887 				(void) printf("%s:%s:%s:%s:%s:%s#%s\n", tag, type, pflags(atol(flags), FALSE),
888 						rsmax, state, cmd, comment);
889 			}
890 			else {
891 				if (nprint == 0) {
892 					(void) printf("PMTAG          PMTYPE         FLGS RCNT STATUS     COMMAND\n");
893 				}
894 				(void) printf("%-14s %-14s %-4s %-4s %-10s %s #%s\n", tag, type, pflags(atol(flags), TRUE),
895 						rsmax, state, cmd, comment);
896 			}
897 			nprint++;
898 		}
899 	}
900 	/*
901 	 * if we didn't find any valid ones, indicate an error (note: 1 and
902 	 * only 1 of the if statements should be true)
903 	 */
904 	if (nprint == 0) {
905 		if (pmtype)
906 			(void) fprintf(stderr, "Invalid request, %s does not exist\n", pmtype);
907 		else if (pmtag)
908 			(void) fprintf(stderr, "Invalid request, %s does not exist\n", pmtag);
909 		else if (!pmtag && !pmtype)
910 			(void) fprintf(stderr, "No port monitors defined\n");
911 		Saferrno = E_NOEXIST;
912 	}
913 	return;
914 }
915 
916 
917 /*
918  * getfield - retrieve and return a field from the sac "status" string (input
919  *	argument is modified to point to next field as a side-effect)
920  *
921  *	args:	p - address of remaining portion of string
922  *		sepchar - field terminator character
923  */
924 
925 char *
926 getfield(p, sepchar)
927 char **p;
928 char sepchar;
929 {
930 	char *savep;	/* for saving argument */
931 
932 	savep = *p;
933 	*p = strchr(*p, sepchar);
934 	if (*p == NULL) {
935 		Saferrno = E_SAFERR;
936 		(void) fprintf(stderr, "Improper message from SAC\n");
937 		return(NULL);
938 	}
939 	**p = '\0';
940 	(*p)++;
941 	return(savep);
942 }
943 
944 
945 /*
946  * single_print - print out _sactab if sac not at home (should only happen
947  *	in single user mode
948  *
949  *	args:	p - address of pointer where formatted data should be
950  *		    placed (space allocated here)
951  */
952 
953 void
954 single_print(p)
955 char **p;
956 {
957 	FILE *fp;				/* file pointer for _sactab */
958 	struct stat statbuf;			/* file status info */
959 	register char *tp1;			/* scratch pointer */
960 	register char *tp2;			/* scratch pointer */
961 	struct sactab stab;			/* place to hold parsed info */
962 	register struct sactab *sp = &stab;	/* and a pointer to it */
963 	char buf[SIZE];				/* scratch buffer */
964 
965 	fp = fopen(SACTAB, "r");
966 	if (fp == NULL) {
967 		Saferrno = E_SYSERR;
968 		error("Could not open _sactab");
969 	}
970 	if (fstat(fileno(fp), &statbuf) < 0) {
971 		Saferrno = E_SYSERR;
972 		error("could not stat _sactab");
973 	}
974 
975 /*
976  * allocate space to build return string, twice file size should be more
977  * than enough (and make sure it's zero'ed out)
978  */
979 
980 	tp1 = calloc(2 * statbuf.st_size, sizeof(char));
981 	if (tp1 == NULL) {
982 		Saferrno = E_SYSERR;
983 		error("could not allocate storage");
984 	}
985 
986 /*
987  * read the file and build the string
988  */
989 
990 	while (fgets(buf, SIZE, fp)) {
991 		tp2 = trim(buf);
992 		if (*tp2 == '\0')
993 			continue;
994 		parse(tp2, &stab);
995 		(void) sprintf(buf, "%s:%s:%d:%d:%d:%s:%s\n", sp->sc_tag, sp->sc_type,
996 			sp->sc_flags, sp->sc_rsmax, SSTATE, sp->sc_cmd, sp->sc_comment);
997 		(void) strcat(tp1, buf);
998 		free(sp->sc_cmd);
999 		free(sp->sc_comment);
1000 	}
1001 	if (!feof(fp)) {
1002 		Saferrno = E_SYSERR;
1003 		error("error reading _sactab");
1004 	}
1005 	(void) fclose(fp);
1006 
1007 /*
1008  * point at the just-built string
1009  */
1010 
1011 	*p = tp1;
1012 	return;
1013 }
1014 
1015 
1016 /*
1017  * openpipe - open up command pipe to SAC
1018  */
1019 
1020 openpipe()
1021 {
1022 	int fd;		/* file descriptor associated with command pipe */
1023 
1024 	fd = open(CMDPIPE, O_RDWR);
1025 	if (fd < 0) {
1026 		Saferrno = E_SYSERR;
1027 		error(SACERR);
1028 	}
1029 
1030 /*
1031  * lock pipe to insure serial access, lock will disappear if process dies
1032  */
1033 
1034 	if (lockf(fd, F_LOCK, 0) < 0) {
1035 		Saferrno = E_SYSERR;
1036 		error("unable to lock command pipe");
1037 	}
1038 	return(fd);
1039 }
1040 
1041 
1042 /*
1043  * sendcmd - send a command to the SAC
1044  *
1045  *	args:	ap - pointer to command to send
1046  *		info - pointer to return information from the SAC
1047  *		tag - tag of port monitor to which the command applies (may
1048  *		      be NULL)
1049  */
1050 
1051 void
1052 sendcmd(ap, info, tag)
1053 struct admcmd *ap;
1054 char **info;
1055 char *tag;
1056 {
1057 	int fd;		/* file descriptor of command pipe */
1058 
1059 	fd = openpipe();
1060 	if (write(fd, ap, sizeof(struct admcmd)) < 0) {
1061 		Saferrno = E_SYSERR;
1062 		error(SACERR);
1063 	}
1064 	checkresp(fd, info, tag);
1065 
1066 /*
1067  * unlock the command pipe - not really necessary since we're about to close
1068  */
1069 
1070 	(void) lockf(fd, F_ULOCK, 0);
1071 	(void) close(fd);
1072 	return;
1073 }
1074 
1075 
1076 /*
1077  * checkresp - check the SAC's response to our command
1078  *
1079  *	args:	fd - file descriptor of command pipe
1080  *		info - pointer to return and info send along by SAC
1081  *		tag - tag of port monitor that the command had been
1082  *		      for, only used for error reporting
1083  */
1084 
1085 void
1086 checkresp(fd, info, tag)
1087 int fd;
1088 char **info;
1089 char *tag;
1090 {
1091 	struct admack ack;			/* acknowledgment struct */
1092 	register struct admack *ak = &ack;	/* and a pointer to it */
1093 	pid_t pid;				/* my pid */
1094 	struct sigaction sigact;		/* signal handler setup */
1095 
1096 /*
1097  * make sure this ack is meant for me, put an alarm around the read
1098  * so we don't hang out forever.
1099  */
1100 
1101 	pid = getpid();
1102 	sigact.sa_flags = 0;
1103 	sigact.sa_handler = catch;
1104 	(void) sigemptyset(&sigact.sa_mask);
1105 	(void) sigaddset(&sigact.sa_mask, SIGALRM);
1106 	(void) sigaction(SIGALRM, &sigact, NULL);
1107 	(void) alarm(10);
1108 	do {
1109 		if (read(fd, ak, sizeof(ack)) != sizeof(ack)) {
1110 			Saferrno = E_SACNOTRUN;
1111 			error(SACERR);
1112 		}
1113 	} while (pid != ak->ak_pid);
1114 	(void) alarm(0);
1115 
1116 /*
1117  * check out what happened
1118  */
1119 
1120 	switch (ak->ak_resp) {
1121 	case AK_ACK:
1122 		/* everything was A-OK */
1123 		if (info && ak->ak_size) {
1124 			/* there is return info and a place to put it */
1125 			if ((*info = malloc((unsigned) (ak->ak_size + 1))) == NULL) {
1126 				Saferrno = E_SYSERR;
1127 				error("could not allocate storage");
1128 			}
1129 			if (read(fd, *info, (unsigned) ak->ak_size) != ak->ak_size) {
1130 				Saferrno = E_SYSERR;
1131 				error(SACERR);
1132 			}
1133 			/* make sure "string" is null-terminated */
1134 			(*info)[ak->ak_size] = '\0';
1135 		}
1136 		return;
1137 	/* something went wrong - see what */
1138 	case AK_PMRUN:
1139 		Saferrno = E_PMRUN;
1140 		(void) fprintf(stderr, "Port monitor, %s, is already running\n", tag);
1141 		break;
1142 	case AK_PMNOTRUN:
1143 		Saferrno = E_PMNOTRUN;
1144 		(void) fprintf(stderr, "Port monitor, %s, is not running\n", tag);
1145 		break;
1146 	case AK_NOPM:
1147 		Saferrno = E_NOEXIST;
1148 		(void) fprintf(stderr, "Invalid request, %s does not exist\n", tag);
1149 		break;
1150 	case AK_UNKNOWN:
1151 		Saferrno = E_SAFERR;
1152 		(void) fprintf(stderr, "Internal error - sent invalid command\n");
1153 		break;
1154 	case AK_NOCONTACT:
1155 		Saferrno = E_SAFERR;
1156 		(void) fprintf(stderr, "Could not contact %s\n", tag);
1157 		break;
1158 	case AK_PMLOCK:
1159 		Saferrno = E_SAFERR;
1160 		(void) fprintf(stderr, "Could not start %s - _pid file locked\n", tag);
1161 		break;
1162 	case AK_RECOVER:
1163 		Saferrno = E_RECOVER;
1164 		(void) fprintf(stderr, "Port monitor, %s, is in recovery\n", tag);
1165 		break;
1166 	case AK_REQFAIL:
1167 		Saferrno = E_SAFERR;
1168 		(void) fprintf(stderr, "This request could not be completed - see sac log file for details\n");
1169 		break;
1170 	default:
1171 		Saferrno = E_SAFERR;
1172 		(void) fprintf(stderr, "unknown response\n");
1173 		break;
1174 	}
1175 }
1176 
1177 
1178 /*
1179  * catch - catcher for SIGALRM, don't need to do anything
1180  */
1181 
1182 void
1183 catch()
1184 {
1185 }
1186 
1187 
1188 /*
1189  * pflags - put port monitor flags into intelligible form for output
1190  *
1191  *	args:	flags - binary representation of flags
1192  *		dflag - true if a "-" should be returned if no flags
1193  */
1194 
1195 char *
1196 pflags(flags, dflag)
1197 long flags;
1198 int dflag;
1199 {
1200 	register int i;			/* scratch counter */
1201 	static char buf[SIZE];		/* formatted flags */
1202 
1203 	if (flags == 0) {
1204 		if (dflag)
1205 			return("-");
1206 		else
1207 			return("");
1208 	}
1209 	i = 0;
1210 	if (flags & D_FLAG) {
1211 		buf[i++] = 'd';
1212 		flags &= ~D_FLAG;
1213 	}
1214 	if (flags & X_FLAG) {
1215 		buf[i++] = 'x';
1216 		flags &= ~X_FLAG;
1217 	}
1218 	if (flags) {
1219 		(void) fprintf(stderr, "Bad information from SAC\n");
1220 		exit(1);
1221 	}
1222 	buf[i] = '\0';
1223 	return(buf);
1224 }
1225 
1226 
1227 /*
1228  * sac_home - returns true is sac has a lock on its logfile, false
1229  *	otherwise (useful to avoid errors for administrative actions in
1230  *	single user mode)
1231  */
1232 
1233 sac_home()
1234 {
1235 	int fd;		/* fd to sac logfile */
1236 
1237 	fd = open(LOGFILE, O_RDONLY);
1238 	if (fd < 0) {
1239 		fprintf(stderr, "warning - could not ascertain sac status\n");
1240 		return(FALSE);
1241 	}
1242 	if (lockf(fd, F_TEST, 0) < 0) {
1243 		/* everything is ok */
1244 		(void) close(fd);
1245 		return(TRUE);
1246 	}
1247 	else {
1248 		/* no one home */
1249 		(void) close(fd);
1250 		return(FALSE);
1251 	}
1252 }
1253