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