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