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