640 number_to_scaled_string is duplicated in several commands
[unleashed.git] / usr / src / cmd / saf / sac.c
blob6ecbe7fb4102677c6b94866693fcbdc4b262e0e7
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
26 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
30 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 #include <ctype.h>
36 #include <signal.h>
37 #include <strings.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/stropts.h>
42 #include <sys/wait.h>
43 #include <unistd.h>
44 #include <utmpx.h>
45 #include <memory.h>
46 #include "msgs.h"
47 #include "extern.h"
48 #include <sac.h>
49 #include "misc.h"
50 #include "structs.h"
52 #include <security/pam_appl.h>
54 #define RESP 1 /* pollfail via no response to sanity poll */
55 #define DEATH 2 /* pollfail via child death */
57 /* signal whose dispositions will be changed */
59 static struct sigaction Sigpoll; /* SIGPOLL */
60 static struct sigaction Sigcld; /* SIGCLD */
61 static struct sigaction Sigalrm; /* SIGALRM */
62 static sigset_t Origmask; /* original signal mask */
64 void usage(void);
65 void initialize(void);
66 void startpms(void);
67 void readutmpx(void);
68 int startpm(struct sactab *);
69 void cleanutx(struct sactab *);
70 void account(struct sactab *, pid_t);
71 void startit(struct sactab *);
72 char **mkargv(struct sactab *);
73 void pollpms(void);
74 void reap(int);
75 void pollfail(struct sactab *, int);
76 void readpipe(void);
77 int validstate(unchar);
78 int mk_cmd_pipe(void);
79 void startpoll(void);
84 * main - scan args for sac, initialize everything, and wait for commands
85 * from sacadm via the command pipe
88 int
89 main(int argc, char *argv[])
91 int c; /* place to hold options */
92 struct sigaction sigact; /* for signal handling */
94 (void) sigprocmask(SIG_SETMASK, NULL, &Origmask);
95 if (argc == 1)
96 usage();
97 (void) setpgrp();
98 while ((c = getopt(argc, argv, "t:")) != -1) {
99 switch (c) {
100 case 't':
101 if (Stime != 0)
102 usage();
103 Stime = atoi(optarg);
104 if (Stime <= 0)
105 usage();
106 break;
107 case '?':
108 usage();
111 if (optind < argc)
112 usage();
114 initialize();
115 sigact.sa_flags = 0;
116 sigact.sa_handler = pollpms;
117 (void) sigemptyset(&sigact.sa_mask);
118 (void) sigaddset(&sigact.sa_mask, SIGALRM);
119 (void) sigaction(SIGALRM, &sigact, &Sigalrm);
122 * minimize time spent in STARTING or UNKNOWN, pollpms() sets alarm
125 pollpms();
126 for (;;)
127 readpipe();
132 * usage - output a usage message on the console
135 void
136 usage()
138 FILE *fp; /* scratch file pointer */
140 fp = fopen("/dev/console", "w");
141 if (fp)
142 (void) fprintf(fp, "SAC: Usage: sac -t sanity_interval\n");
143 exit(1);
148 * initialize - initialization stuff
152 void
153 initialize()
155 int ret; /* return code from doconfig() */
156 struct sigaction sigact; /* for signal handling */
158 openlog();
159 log("*** SAC starting ***");
160 #ifdef DEBUG
161 opendebug();
162 log("Debugging turned on");
163 #endif
164 if (chdir(HOME) < 0)
165 error(E_CHDIR, EXIT);
168 * pass an invalid fd, shouldn't be doing pushes and pops in this per-system
169 * configuration script (_sysconfig)
172 if ((ret = doconfig(-1, SYSCONFIG, 0)) != 0) {
173 if (ret == -1)
174 error(E_SYSCONF, EXIT);
175 else {
176 (void) sprintf(Scratch,
177 "Error in _sysconfig: line %d", ret);
178 log(Scratch);
179 error(E_BADSYSCONF, EXIT);
183 sigact.sa_flags = 0;
184 sigact.sa_handler = reap;
185 (void) sigemptyset(&sigact.sa_mask);
186 (void) sigaddset(&sigact.sa_mask, SIGCLD);
187 (void) sigaction(SIGCLD, &sigact, &Sigcld);
190 * establish pipe for PMS to communicate with sac
193 if (access("_sacpipe", 0) != 0) {
194 /* not there, create one */
195 (void) umask(0);
196 if (mknod("_sacpipe", S_IFIFO | 0600, 0) < 0)
197 error(E_NOPIPE, EXIT);
199 Sfd = open("_sacpipe", O_RDWR);
200 if (Sfd < 0)
201 error(E_NOPIPE, EXIT);
204 * establish pipe for sacadm to communicate with sac
207 Cfd = mk_cmd_pipe();
210 * read in _sactab, but don't start port monitors as a by-product
211 * since we may be in recovery - start them explicitly instead
214 read_table(FALSE);
215 startpoll();
216 startpms();
221 * startpms - start initial set of port monitors
225 void
226 startpms()
228 struct sactab *sp; /* working pointer */
229 int rflag; /* recovery flag */
230 pid_t checklock();
233 * check to see if we're really a recovering SAC (if any port monitors hold
234 * locks, assume that we're in recovery), if so, start differently
237 rflag = 0;
238 for (sp = Sactab; sp; sp = sp->sc_next) {
239 if (checklock(sp)) {
240 rflag = 1;
241 sp->sc_sstate = sp->sc_pstate = UNKNOWN;
242 sp->sc_ok = 1;
243 sp->sc_exit = 0;
244 (void) sprintf(Scratch, "%s/_pmpipe", sp->sc_tag);
245 sp->sc_fd = open(Scratch, O_RDWR);
246 if (sp->sc_fd < 0) {
249 * if we get into here, we're in deep trouble. PM seems to be running
250 * and we're trying to recover, but we can't talk to it. Unfortunately,
251 * there's not much that can be done other than to try and restore a
252 * sane state. By setting sp->sc_ok to 0, this will look like a poll failure
253 * and if sp->rs_rsmax > 0, PM will be restarted.
256 (void) sprintf(Scratch,
257 "Could not open _pmpipe for port monitor <%s>",
258 sp->sc_tag);
259 log(Scratch);
260 (void) sendsig(sp, SIGTERM);
261 sp->sc_ok = 0;
265 if (rflag) {
266 readutmpx();
267 log("SAC in recovery");
268 return;
272 * normal startup
275 for (sp = Sactab; sp; sp = sp->sc_next) {
276 if (sp->sc_flags & X_FLAG) {
277 /* System Administator specified don't start */
278 continue;
280 (void) startpm(sp);
286 * readutmpx - read the utmpx file to find out the ids of running port
287 * monitors (only called during a recover start up). Note:
288 * after a sac failure, init will inherit all of the port
289 * monitors and should get the SIGCLD's if they die (and
290 * will clean up). This is mainly for stuck processes,
291 * although init would get the SIGCLD when the stuckie gets
292 * killed, it doesn't hurt to have the sac check. This is
293 * only done once.
298 void
299 readutmpx()
301 struct sactab *sp; /* working pointer */
302 struct sactab *savesp; /* rembered port monitor match */
303 struct utmpx *uxp; /* working pointer */
305 setutxent();
306 while (uxp = getutxent()) {
307 /* we're only interested in login processes */
308 if (uxp->ut_type != LOGIN_PROCESS)
309 continue;
310 if (uxp->ut_user[sizeof (uxp->ut_user) - 1] == '\0') {
313 * possible port monitor and name is short enough to do a normal compare
316 sp = findpm(uxp->ut_user);
317 if (sp && (sp->sc_sstate == UNKNOWN)) {
318 /* found one */
319 (void) memcpy(sp->sc_utid, uxp->ut_id, IDLEN);
320 sp->sc_pid = uxp->ut_pid;
322 } else {
325 * possible port monitor name, but it could have been truncated. If
326 * a match is found on a unique prefix, then it should be the correct
327 * entry. If an ambiguity is found, ignore the entry, init will clean
328 * up the entry if it dies.
331 savesp = NULL;
332 for (sp = Sactab; sp; sp = sp->sc_next) {
333 if (strncmp(uxp->ut_user, sp->sc_tag,
334 sizeof (uxp->ut_user)) == 0) {
335 if (savesp) {
336 /* already found a match */
337 savesp = NULL;
338 (void) sprintf(Scratch,
339 "ambiguous utmpx entry <%.8s>",
340 sp->sc_tag);
341 log(Scratch);
342 break;
343 } else {
344 savesp = sp;
348 if (savesp && (savesp->sc_sstate == UNKNOWN)) {
349 /* found it */
350 (void) memcpy(savesp->sc_utid, uxp->ut_id,
351 IDLEN);
352 savesp->sc_pid = uxp->ut_pid;
356 endutxent();
361 * startpm - start a particular PM, return code:
362 * -1: _pid file locked
363 * -2: any other reason
365 * args: sp - pointer to sac's port monitor information for
366 * designated port monitor
370 startpm(struct sactab *sp)
372 sigset_t cset; /* for signal handling */
373 sigset_t tset; /* for signal handling */
374 pid_t pid; /* pid of new port monitor */
375 pid_t checklock();
377 #ifdef DEBUG
378 debug("in startpm");
379 #endif
380 if (checklock(sp)) {
381 (void) sprintf(Scratch,
382 "could not start <%s> - _pid file locked", sp->sc_tag);
383 log(Scratch);
384 return (-1);
387 (void) sprintf(Scratch, "%s/_pmpipe", sp->sc_tag);
388 if (access(Scratch, 0) != 0) {
389 /* not there, create one */
390 (void) umask(0);
391 if (mknod(Scratch, S_IFIFO | 0600, 0) < 0) {
392 (void) sprintf(Scratch,
393 "Could not create _pmpipe for port monitor <%s>, errno is %d",
394 sp->sc_tag, errno);
395 log(Scratch);
396 return (-2);
399 sp->sc_fd = open(Scratch, O_RDWR);
400 if (sp->sc_fd < 0) {
401 (void) sprintf(Scratch,
402 "Could not open _pmpipe for port monitor <%s>, errno is %d",
403 sp->sc_tag, errno);
404 log(Scratch);
405 return (-2);
408 /* in case child dies too quickly */
409 (void) sigprocmask(SIG_SETMASK, NULL, &cset);
410 tset = cset;
411 (void) sigaddset(&tset, SIGCLD);
412 (void) sigprocmask(SIG_SETMASK, &tset, NULL);
413 if ((pid = fork()) < 0) {
414 (void) sprintf(Scratch,
415 "Could not fork port monitor <%s>", sp->sc_tag);
416 log(Scratch);
417 return (-2);
418 } else if (!pid) {
419 startit(sp);
420 /* no return */
424 * clean up old utmpx if its there
427 cleanutx(sp);
430 * create a utmpx entry and set initial states
433 account(sp, pid);
434 sp->sc_pstate = STARTING;
435 if (sp->sc_lstate == NOTRUNNING)
436 sp->sc_sstate = (sp->sc_flags & D_FLAG) ? DISABLED : ENABLED;
437 else
438 sp->sc_sstate = sp->sc_lstate;
439 sp->sc_ok = 1;
440 sp->sc_exit = 0;
441 sp->sc_pid = pid;
442 /* ok to take signals now that the table is up-to-table */
443 (void) sigprocmask(SIG_SETMASK, &cset, NULL);
444 return (0);
449 * cleanutx - clean out a utmpx record for a port monitor
451 * args: sp - pointer to sac's port monitor information for
452 * designated port monitor
456 void
457 cleanutx(struct sactab *sp)
459 int i; /* scratch variable */
460 int zerocheck; /* scratch variable */
461 char buf[SIZE]; /* scratch buffer */
462 pam_handle_t *pamh; /* PAM auth descriptor */
463 struct utmpx ut;
464 struct utmpx *up;
465 int pid;
466 char user[sizeof (up->ut_user) + 1];
467 char ttyn[sizeof (up->ut_line) + 1];
468 char rhost[sizeof (up->ut_host) + 1];
470 * check to see if there is a utmpx entry to clean up (indicated by a non
471 * zero utmpx id
473 zerocheck = 0;
474 for (i = 0; i < IDLEN; ++i) {
475 zerocheck += sp->sc_utid[i];
477 if (zerocheck == 0)
478 return;
480 pid = sp->sc_pid;
481 setutxent();
482 while (up = getutxent()) {
483 if (up->ut_pid == pid) {
484 if (up->ut_type == DEAD_PROCESS) {
486 * Cleaned up elsewhere.
488 break;
490 strncpy(user, up->ut_user, sizeof (up->ut_user));
491 user[sizeof (up->ut_user)] = '\0';
492 strncpy(ttyn, up->ut_line, sizeof (up->ut_line));
493 ttyn[sizeof (up->ut_line)] = '\0';
494 strncpy(rhost, up->ut_host, sizeof (up->ut_host));
495 rhost[sizeof (up->ut_host)] = '\0';
497 if ((pam_start("sac", user, NULL, &pamh)) ==
498 PAM_SUCCESS) {
499 (void) pam_set_item(pamh, PAM_TTY, ttyn);
500 (void) pam_set_item(pamh, PAM_RHOST, rhost);
501 (void) pam_close_session(pamh, 0);
502 pam_end(pamh, PAM_SUCCESS);
505 up->ut_type = DEAD_PROCESS;
506 up->ut_exit.e_termination = WTERMSIG(sp->sc_exit);
507 up->ut_exit.e_exit = WEXITSTATUS(sp->sc_exit);
508 if (sp->sc_utid != NULL)
509 (void) memcpy(up->ut_id, sp->sc_utid,
510 sizeof (up->ut_id));
511 (void) time(&up->ut_tv.tv_sec);
512 if (modutx(up) == NULL) {
514 * Since modutx failed we'll
515 * write out the new entry
516 * ourselves.
518 (void) pututxline(up);
519 updwtmpx("wtmpx", up);
521 break;
524 endutxent();
528 * account - create a utmp record for a port monitor
530 * args: pid - process id of port monitor
534 void
535 account(struct sactab *sp, pid_t pid)
537 struct utmpx utmpx; /* prototype utmpx entry */
538 struct utmpx *up = &utmpx; /* and a pointer to it */
540 (void) memset(up, '\0', sizeof (utmpx));
541 (void) strncpy(up->ut_user, sp->sc_tag, sizeof (up->ut_user));
542 up->ut_pid = pid;
543 up->ut_type = LOGIN_PROCESS;
544 up->ut_id[0] = 'P';
545 up->ut_id[1] = 'M';
546 up->ut_id[2] = SC_WILDC;
547 up->ut_id[3] = SC_WILDC;
548 (void) time(&up->ut_xtime);
549 if (makeutx(up) == NULL) {
550 log("Could not create utmpx entry");
551 (void) memset(sp->sc_utid, '\0', IDLEN);
552 } else {
553 (void) memcpy(sp->sc_utid, up->ut_id, IDLEN);
559 * startit - finish starting a particular port monitor, establish environment,
560 * etc. (Note: this is the child at this point)
562 * args: sp - pointer to sac's port monitor information for
563 * designated port monitor
567 void
568 startit(struct sactab *sp)
570 static char istate[SIZE]; /* place to put ISTATE env var. */
571 static char pmtag[SIZE]; /* place to put PMTAG env var. */
572 char **argvp; /* arglist for PM */
573 int i; /* loop control variable */
574 long ndesc; /* # of file descriptors configured */
575 int ret; /* return value from doconfig */
576 sigset_t cset; /* for signal handling */
577 sigset_t tset; /* for signal handling */
580 * establish the home directory
583 if (chdir(sp->sc_tag) < 0) {
584 (void) sprintf(Scratch,
585 "Cannot chdir to <%s/%s>, port monitor not started",
586 HOME, sp->sc_tag);
587 log(Scratch);
588 exit(1);
592 * interpret the configuration script, pass an invalid fd, shouldn't be
593 * doing pushes and pops in this script
596 (void) sigprocmask(SIG_SETMASK, NULL, &cset);
597 tset = cset;
598 (void) sigaddset(&tset, SIGCLD);
599 (void) sigprocmask(SIG_SETMASK, &tset, NULL);
600 if ((ret = doconfig(-1, "_config", 0)) != 0) {
601 if (ret == -1) {
602 (void) sprintf(Scratch,
603 "system error in _config script for <%s>",
604 sp->sc_tag);
605 log(Scratch);
606 exit(1);
607 } else {
608 (void) sprintf(Scratch,
609 "Error in _config script for <%s>: line %d",
610 sp->sc_tag, ret);
611 log(Scratch);
612 exit(1);
617 * add the promised environment variables
620 if (sp->sc_lstate == NOTRUNNING)
621 (void) sprintf(istate, "ISTATE=%s",
622 (sp->sc_flags & D_FLAG) ? "disabled" : "enabled");
623 else
624 (void) sprintf(istate, "ISTATE=%s",
625 (sp->sc_lstate == DISABLED) ? "disabled" : "enabled");
626 if (putenv(istate)) {
627 (void) sprintf(Scratch,
628 "can't expand port monitor <%s> environment",
629 sp->sc_tag);
630 log(Scratch);
631 exit(1);
633 (void) sprintf(pmtag, "PMTAG=%s", sp->sc_tag);
634 if (putenv(pmtag)) {
635 (void) sprintf(Scratch,
636 "can't expand port monitor <%s> environment",
637 sp->sc_tag);
638 log(Scratch);
639 exit(1);
643 * build an argv
646 argvp = mkargv(sp);
648 (void) sprintf(Scratch, "starting port monitor <%s>", sp->sc_tag);
649 log(Scratch);
650 ndesc = ulimit(4, 0L);
651 for (i = 0; i < ndesc; i++)
652 (void) fcntl(i, F_SETFD, 1);
653 /* restore orignal handlers and mask */
654 (void) sigaction(SIGPOLL, &Sigpoll, NULL);
655 (void) sigaction(SIGCLD, &Sigcld, NULL);
656 (void) sigaction(SIGALRM, &Sigalrm, NULL);
657 (void) sigprocmask(SIG_SETMASK, &Origmask, NULL);
658 (void) execve(argvp[0], argvp, environ);
659 (void) sprintf(Scratch, "exec of port monitor <%s> failed", sp->sc_tag);
660 log(Scratch);
661 exit(1);
666 * mkargv - Given a pointer to a struct sactab, construct argv
667 * for an exec system call.
669 * args: sp - pointer to sac's port monitor information for
670 * designated port montior
674 #define NARGS 50 /* max # of args */
676 static char *newargv[NARGS]; /* place for argv list */
677 static char *delim = " \t'\""; /* delimiter list */
679 char **
680 mkargv(struct sactab *sp)
682 char **argvp = newargv; /* scratch pointer */
683 char *p = sp->sc_cmd; /* working pointer */
684 char delch; /* delimiter seen */
685 char *savep; /* scratch pointer */
686 char *tp; /* scratch pointer */
688 *argvp = 0;
689 savep = p;
690 while (p && *p) {
691 if (p = strpbrk(p, delim)) {
692 switch (*p) {
693 case ' ':
694 case '\t':
695 /* "normal" cases */
696 *p++ = '\0';
697 *argvp++ = savep;
698 /* zap trailing white space */
699 while (isspace(*p))
700 p++;
701 savep = p;
702 break;
703 case '"':
704 case '\'':
705 /* found a string */
706 delch = *p; /* remember the delimiter */
707 savep = ++p;
710 * We work the string in place, embedded instances of the string delimiter,
711 * i.e. \" must have the '\' removed. Since we'd have to do a compare to
712 * decide if a copy were needed, it's less work to just do the copy, even
713 * though it is most likely unnecessary.
716 tp = p;
717 for (;;) {
718 if (*p == '\0') {
719 (void) sprintf(Scratch,
720 "invalid command line, non-terminated string for port monitor %s",
721 sp->sc_tag);
722 log(Scratch);
723 exit(1);
725 if (*p == delch) {
726 if (*(tp - 1) == '\\') {
727 /* \delim */
728 *(tp - 1) = *p;
729 p++;
730 } else { /* end of string */
731 *tp = 0;
732 *argvp++ = savep;
733 p++;
734 /* zap trailing white space */
735 while (isspace(*p))
736 p++;
737 savep = p;
738 break;
740 } else {
741 *tp++ = *p++;
744 break;
745 default:
746 log("Internal error in parse routine");
747 exit(1);
750 else
751 *argvp++ = savep;
753 *argvp = 0;
754 return (newargv);
759 * pollpms - send out sanity polls, if sc_sstate and sc_pstate are
760 * the same (everyone agrees on the state) or if SAC thinks PM
761 * should be stopping, send out a status message;
762 * otherwise, send out a message indicating the state the SAC
763 * thinks the PM should be entering
766 void
767 pollpms()
769 struct sactab *sp; /* working pointer */
770 struct sacmsg sacmsg; /* message to send to PM */
772 #ifdef DEBUG
773 debug("alarm went off");
774 #endif
775 for (sp = Sactab; sp; sp = sp->sc_next) {
776 if (sp->sc_pstate == NOTRUNNING || sp->sc_pstate == FAILED) {
777 /* don't bother if no one is home */
778 continue;
780 if (sp->sc_ok == 0) {
781 /* PM has stopped responding */
782 pollfail(sp, RESP);
783 continue;
787 * note - if we're in recovery, a SC_STATUS message is sent
788 * (sc_sstate = UNKNOWN and sc_pstate = UNKNOWN)
791 if (sp->sc_sstate == sp->sc_pstate) {
792 sacmsg.sc_type = SC_STATUS;
793 sacmsg.sc_size = 0;
794 } else {
795 switch (sp->sc_sstate) {
796 case ENABLED:
797 sacmsg.sc_type = SC_ENABLE;
798 sacmsg.sc_size = 0;
799 break;
800 case DISABLED:
801 sacmsg.sc_type = SC_DISABLE;
802 sacmsg.sc_size = 0;
803 break;
804 case STARTING:
805 case STOPPING:
806 case NOTRUNNING:
807 case FAILED:
808 case UNKNOWN:
810 * if NOTRUNNING or FAILED, PM will probably
811 * not respond to poll, that's how we detect
812 * that it's gone
814 sacmsg.sc_type = SC_STATUS;
815 sacmsg.sc_size = 0;
816 break;
817 default:
818 error(E_BADSTATE, EXIT);
822 /* send the message */
823 sendpmmsg(sp, &sacmsg);
824 sp->sc_ok = 0;
826 (void) alarm(Stime);
831 * reap - clean up dead children, equivalent to a "fast" poll failure
833 * args: signo - signal #
836 void
837 reap(int signo)
839 struct sactab *sp; /* working pointer */
840 pid_t pid; /* returned pid from wait */
841 int status; /* returned status from wait */
843 pid = wait(&status);
844 for (sp = Sactab; sp; sp = sp->sc_next) {
845 if (sp->sc_pid == pid)
846 break;
848 if (sp == NULL) {
849 /* not from a port monitor we know about */
850 return;
852 sp->sc_exit = status;
853 /* only call pollfail for "stuck" and stopping processes */
854 if (sp->sc_pstate != NOTRUNNING && sp->sc_pstate != FAILED)
855 pollfail(sp, DEATH);
860 * pollfail - handle the case where a PM stops responding to a sanity poll
862 * args: sp - pointer to sac's port monitor information for
863 * designated port monitor
864 * reason - RESP or DEATH (indicates why pollfail called)
868 void
869 pollfail(struct sactab *sp, int reason)
871 char buf[SIZE]; /* scratch buffer */
872 sigset_t cset; /* for signal handling */
873 sigset_t tset; /* for signal handling */
875 #ifdef DEBUG
876 debug("in pollfail");
877 #endif
879 /* first, remove the utmpx entry and clean up any links */
881 cleanutx(sp);
883 if (sp->sc_pstate == STOPPING) {
884 (void) sprintf(buf, "<%s> has stopped", sp->sc_tag);
885 log(buf);
886 sp->sc_pstate = NOTRUNNING;
887 sp->sc_lstate = NOTRUNNING;
888 (void) close(sp->sc_fd);
889 } else {
892 * PM in trouble - if it's still there, try to put it out of its misery
893 * We play with SIGCLD here to that after SIGKILL is sent, the catcher
894 * routine reap() is not called until we're ready (note: when a catcher
895 * is established for SIGCLD and any zombies are present, the signal is
896 * immediately received)
899 (void) sigprocmask(SIG_SETMASK, NULL, &cset);
900 tset = cset;
901 (void) sigaddset(&tset, SIGCLD);
902 (void) sigprocmask(SIG_SETMASK, &tset, NULL);
903 (void) sendsig(sp, SIGKILL);
904 if (sp->sc_rscnt < sp->sc_rsmax) {
905 /* try to restart it */
906 if (reason == RESP)
907 (void) sprintf(buf,
908 "<%s> stopped responding to sanity polls - trying to restart",
909 sp->sc_tag);
910 else
911 (void) sprintf(buf,
912 "<%s> has died - trying to restart",
913 sp->sc_tag);
914 log(buf);
915 sp->sc_rscnt++;
916 (void) close(sp->sc_fd);
917 (void) startpm(sp);
918 } else {
919 sp->sc_sstate = sp->sc_pstate = FAILED;
920 (void) close(sp->sc_fd);
921 (void) sprintf(buf, "<%s> has FAILED", sp->sc_tag);
922 log(buf);
925 (void) sigprocmask(SIG_SETMASK, &cset, NULL);
930 * readpipe - read messages from _sacpipe
934 void
935 readpipe()
937 struct pmmsg pmmsg; /* incoming message */
938 struct pmmsg *pp = &pmmsg; /* and a pointer to it */
939 struct sactab *sp; /* working pointer */
940 int ret; /* return value from read */
943 * This routine's main purpose is to maintain the state associated with
944 * each of the known port monitors. Because it may be confusing, following
945 * is a brief discussion of what is happening. Three different views of
946 * a port monitor's state exist: sc_sstate, sc_pstate, and sc_lstate.
947 * sc_sstate is the state in which the sac has been instructed to place
948 * a port monitor. sc_lstate is essentially a shadow of this field, however,
949 * it will only take on the values ENABLED, DISABLED, and NOTRUNNING.
950 * sc_lstate is used if a port monitor dies to restart it in the state in
951 * which it was last running. sc_pstate is the last state that the port
952 * monitor reported itself in. Note that if the administrator specifies
953 * a state change, there is a window where sc_sstate and sc_pstate will
954 * be different (until the port monitor enacts and acknowledges the change).
956 * These states interact with the polling loop to determine which message
957 * should be sent to a port monitor. If the states agree, an SC_STATUS
958 * is sent. If they disagree, the appropriate message to put the port
959 * monitor in the correct state is sent (SC_ENABLE or SC_DISABLE). sc_pstate
960 * is the state that is reported back to an AC_STATUS request. Finally,
961 * when in recovery (sc_sstate and sc_pstate both = UNKNOWN), the sac will
962 * take the port monitor's reported state as the true state. This is the
963 * only instance in which a port monitor can cause sc_sstate to change.
966 for (;;) {
967 if (read(Sfd, pp, sizeof (pmmsg)) < 0) {
968 if (errno != EINTR)
969 error(E_BADREAD, EXIT);
970 continue;
973 while (pp->pm_size) {
976 * there's data after the header, unfortunately, we don't understand
977 * any of it because only class 1 (no data) messages are defined. Just
978 * flush it
981 ret = read(Sfd, Scratch,
982 (pp->pm_size > SIZE) ? (unsigned) SIZE :
983 (unsigned) pp->pm_size);
984 if (ret < 0) {
985 if (errno != EINTR)
986 error(E_BADREAD, EXIT);
987 continue;
989 else
990 pp->pm_size -= ret;
993 sp = findpm(pp->pm_tag);
994 if (sp == NULL) {
995 log("message from unknown process");
996 continue;
998 switch (pp->pm_type) {
999 case PM_UNKNOWN:
1000 (void) sprintf(Scratch,
1001 "port monitor <%s> didn't recognize message",
1002 sp->sc_tag);
1003 log(Scratch);
1004 /* fall through */
1005 case PM_STATUS:
1007 * paranoia check, if port monitor reports garbage
1008 * state, pretend it said UNKNOWN
1010 if (!validstate(pp->pm_state)) {
1011 pp->pm_state = UNKNOWN;
1012 (void) sprintf(Scratch,
1013 "port monitor <%s> reporting invalid state",
1014 sp->sc_tag);
1015 log(Scratch);
1017 if (sp->sc_sstate == sp->sc_pstate) {
1018 /* everyone agrees on the current state */
1019 if (sp->sc_sstate == UNKNOWN) {
1020 /* special case for recovery */
1021 sp->sc_sstate = pp->pm_state;
1022 sp->sc_pstate = pp->pm_state;
1023 if (pp->pm_state == ENABLED ||
1024 pp->pm_state == DISABLED)
1025 /* sc_lstate NOTRUNNING by default */
1026 sp->sc_lstate = pp->pm_state;
1028 if (pp->pm_state != sp->sc_pstate) {
1030 * something isn't right here, PM
1031 * changed state without orders, try
1032 * to restore to correct state
1034 sp->sc_pstate = pp->pm_state;
1036 } else if (sp->sc_sstate == pp->pm_state) {
1037 /* PM changed to state requested */
1038 (void) sprintf(Scratch,
1039 "port monitor <%s> changed state from %s to %s",
1040 sp->sc_tag, pstate(sp->sc_pstate),
1041 pstate(pp->pm_state));
1042 log(Scratch);
1043 sp->sc_pstate = pp->pm_state;
1044 } else if (sp->sc_pstate != pp->pm_state) {
1046 * something isn't right here, PM isn't
1047 * in the state it was, nor is it in the
1048 * state we just tried to put it in, try
1049 * to restore to correct state if we should
1051 if (sp->sc_pstate != STOPPING)
1052 sp->sc_pstate = pp->pm_state;
1054 break;
1055 default:
1056 (void) sprintf(Scratch,
1057 "port monitor <%s> sent an invalid message - ignoring it",
1058 sp->sc_tag);
1059 log(Scratch);
1060 break;
1062 /* no matter what, PM did answer the poll */
1063 sp->sc_ok = 1;
1064 /* Note the messages it understands */
1065 sp->sc_maxclass = pp->pm_maxclass;
1071 * validstate - determine if arg s a valid return state from a port monitor
1072 * return 1 if ok, 0 otherwise
1074 * args: state - state to be verified
1077 validstate(unchar state)
1079 switch (state) {
1080 case PM_ENABLED:
1081 case PM_DISABLED:
1082 case PM_STARTING:
1083 case PM_STOPPING:
1084 return (1);
1085 default:
1086 return (0);
1092 * mk_cmd_pipe - create the command pipe used by sacadm
1096 mk_cmd_pipe()
1098 int fds[2]; /* pipe endpoints */
1099 int fd; /* scratch file descriptor */
1101 /* make sure there is a file here to mount on */
1102 (void) unlink(CMDPIPE);
1103 fd = open(CMDPIPE, O_RDWR | O_CREAT, 0600);
1104 if (fd < 0)
1105 error(E_CMDPIPE, EXIT);
1106 close(fd);
1107 if (pipe(fds) < 0)
1108 error(E_PIPE, EXIT);
1109 if (fattach(fds[0], CMDPIPE) < 0)
1110 error(E_FATTACH, EXIT);
1111 return (fds[1]);
1116 * startpoll - enable polling on command pipe by setting up to catch SIGPOLL
1120 void
1121 startpoll()
1123 struct sigaction sigact; /* for signal handling */
1125 if (ioctl(Cfd, I_SETSIG, S_INPUT) < 0)
1126 error(E_SETSIG, EXIT);
1127 sigact.sa_flags = 0;
1128 sigact.sa_handler = sigpoll;
1129 (void) sigemptyset(&sigact.sa_mask);
1130 (void) sigaddset(&sigact.sa_mask, SIGPOLL);
1131 (void) sigaction(SIGPOLL, &sigact, &Sigpoll);