/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <utmpx.h>
#include <memory.h>
#include "msgs.h"
#include "extern.h"
#include <sac.h>
#include "misc.h"
#include "structs.h"
#include <security/pam_appl.h>
/* signal whose dispositions will be changed */
void usage(void);
void initialize(void);
void startpms(void);
void readutmpx(void);
void pollpms(void);
void reap(int);
void readpipe(void);
int validstate(unchar);
int mk_cmd_pipe(void);
void startpoll(void);
/*
* main - scan args for sac, initialize everything, and wait for commands
* from sacadm via the command pipe
*/
int
{
int c; /* place to hold options */
if (argc == 1)
usage();
(void) setpgrp();
switch (c) {
case 't':
if (Stime != 0)
usage();
if (Stime <= 0)
usage();
break;
case '?':
usage();
}
}
usage();
initialize();
/*
* minimize time spent in STARTING or UNKNOWN, pollpms() sets alarm
*/
pollpms();
for (;;)
readpipe();
}
/*
* usage - output a usage message on the console
*/
void
usage()
{
if (fp)
exit(1);
}
/*
* initialize - initialization stuff
*/
void
{
openlog();
log("*** SAC starting ***");
#ifdef DEBUG
opendebug();
log("Debugging turned on");
#endif
/*
* pass an invalid fd, shouldn't be doing pushes and pops in this per-system
* configuration script (_sysconfig)
*/
if (ret == -1)
else {
"Error in _sysconfig: line %d", ret);
}
}
/*
* establish pipe for PMS to communicate with sac
*/
if (access("_sacpipe", 0) != 0) {
/* not there, create one */
(void) umask(0);
}
if (Sfd < 0)
/*
* establish pipe for sacadm to communicate with sac
*/
Cfd = mk_cmd_pipe();
/*
* read in _sactab, but don't start port monitors as a by-product
* since we may be in recovery - start them explicitly instead
*/
startpoll();
startpms();
}
/*
* startpms - start initial set of port monitors
*/
void
startpms()
{
/*
* check to see if we're really a recovering SAC (if any port monitors hold
* locks, assume that we're in recovery), if so, start differently
*/
rflag = 0;
rflag = 1;
/*
* if we get into here, we're in deep trouble. PM seems to be running
* and we're trying to recover, but we can't talk to it. Unfortunately,
* there's not much that can be done other than to try and restore a
* sane state. By setting sp->sc_ok to 0, this will look like a poll failure
* and if sp->rs_rsmax > 0, PM will be restarted.
*/
"Could not open _pmpipe for port monitor <%s>",
}
}
}
if (rflag) {
readutmpx();
log("SAC in recovery");
return;
}
/*
* normal startup
*/
/* System Administator specified don't start */
continue;
}
}
}
/*
* readutmpx - read the utmpx file to find out the ids of running port
* monitors (only called during a recover start up). Note:
* after a sac failure, init will inherit all of the port
* monitors and should get the SIGCLD's if they die (and
* will clean up). This is mainly for stuck processes,
* although init would get the SIGCLD when the stuckie gets
* killed, it doesn't hurt to have the sac check. This is
* only done once.
*
*/
void
{
setutxent();
/* we're only interested in login processes */
continue;
/*
* possible port monitor and name is short enough to do a normal compare
*/
/* found one */
}
} else {
/*
* possible port monitor name, but it could have been truncated. If
* a match is found on a unique prefix, then it should be the correct
* entry. If an ambiguity is found, ignore the entry, init will clean
* up the entry if it dies.
*/
if (savesp) {
/* already found a match */
"ambiguous utmpx entry <%.8s>",
break;
} else {
}
}
}
/* found it */
IDLEN);
}
}
}
endutxent();
}
/*
* startpm - start a particular PM, return code:
* -1: _pid file locked
* -2: any other reason
*
* args: sp - pointer to sac's port monitor information for
* designated port monitor
*/
int
{
#ifdef DEBUG
debug("in startpm");
#endif
return (-1);
}
/* not there, create one */
(void) umask(0);
"Could not create _pmpipe for port monitor <%s>, errno is %d",
return (-2);
}
}
"Could not open _pmpipe for port monitor <%s>, errno is %d",
return (-2);
}
/* in case child dies too quickly */
return (-2);
} else if (!pid) {
/* no return */
}
/*
* clean up old utmpx if its there
*/
/*
* create a utmpx entry and set initial states
*/
else
/* ok to take signals now that the table is up-to-table */
return (0);
}
/*
* cleanutx - clean out a utmpx record for a port monitor
*
* args: sp - pointer to sac's port monitor information for
* designated port monitor
*/
void
{
int i; /* scratch variable */
int pid;
/*
* check to see if there is a utmpx entry to clean up (indicated by a non
* zero utmpx id
*/
zerocheck = 0;
for (i = 0; i < IDLEN; ++i) {
}
if (zerocheck == 0)
return;
setutxent();
/*
* Cleaned up elsewhere.
*/
break;
}
PAM_SUCCESS) {
(void) pam_close_session(pamh, 0);
}
/*
* Since modutx failed we'll
* write out the new entry
* ourselves.
*/
(void) pututxline(up);
}
break;
}
}
endutxent();
}
/*
* account - create a utmp record for a port monitor
*
* args: pid - process id of port monitor
*/
void
{
log("Could not create utmpx entry");
} else {
}
}
/*
* startit - finish starting a particular port monitor, establish environment,
* etc. (Note: this is the child at this point)
*
* args: sp - pointer to sac's port monitor information for
* designated port monitor
*/
void
{
int i; /* loop control variable */
/*
* establish the home directory
*/
"Cannot chdir to <%s/%s>, port monitor not started",
exit(1);
}
/*
* interpret the configuration script, pass an invalid fd, shouldn't be
* doing pushes and pops in this script
*/
if (ret == -1) {
"system error in _config script for <%s>",
exit(1);
} else {
"Error in _config script for <%s>: line %d",
exit(1);
}
}
/*
* add the promised environment variables
*/
else
"can't expand port monitor <%s> environment",
exit(1);
}
"can't expand port monitor <%s> environment",
exit(1);
}
/*
* build an argv
*/
for (i = 0; i < ndesc; i++)
/* restore orignal handlers and mask */
exit(1);
}
/*
* mkargv - Given a pointer to a struct sactab, construct argv
* for an exec system call.
*
* args: sp - pointer to sac's port monitor information for
* designated port montior
*/
char **
{
*argvp = 0;
savep = p;
while (p && *p) {
switch (*p) {
case ' ':
case '\t':
/* "normal" cases */
*p++ = '\0';
/* zap trailing white space */
while (isspace(*p))
p++;
savep = p;
break;
case '"':
case '\'':
/* found a string */
delch = *p; /* remember the delimiter */
savep = ++p;
/*
* We work the string in place, embedded instances of the string delimiter,
* i.e. \" must have the '\' removed. Since we'd have to do a compare to
* decide if a copy were needed, it's less work to just do the copy, even
* though it is most likely unnecessary.
*/
tp = p;
for (;;) {
if (*p == '\0') {
"invalid command line, non-terminated string for port monitor %s",
exit(1);
}
if (*p == delch) {
/* \delim */
*(tp - 1) = *p;
p++;
} else { /* end of string */
*tp = 0;
p++;
/* zap trailing white space */
while (isspace(*p))
p++;
savep = p;
break;
}
} else {
*tp++ = *p++;
}
}
break;
default:
log("Internal error in parse routine");
exit(1);
}
}
else
}
*argvp = 0;
return (newargv);
}
/*
* pollpms - send out sanity polls, if sc_sstate and sc_pstate are
* the same (everyone agrees on the state) or if SAC thinks PM
* should be stopping, send out a status message;
* otherwise, send out a message indicating the state the SAC
* thinks the PM should be entering
*/
void
pollpms()
{
#ifdef DEBUG
debug("alarm went off");
#endif
/* don't bother if no one is home */
continue;
}
/* PM has stopped responding */
continue;
}
/*
* note - if we're in recovery, a SC_STATUS message is sent
* (sc_sstate = UNKNOWN and sc_pstate = UNKNOWN)
*/
} else {
case ENABLED:
break;
case DISABLED:
break;
case STARTING:
case STOPPING:
case NOTRUNNING:
case FAILED:
case UNKNOWN:
/*
* if NOTRUNNING or FAILED, PM will probably
* not respond to poll, that's how we detect
* that it's gone
*/
break;
default:
}
}
/* send the message */
}
}
/*
* reap - clean up dead children, equivalent to a "fast" poll failure
*
* args: signo - signal #
*/
void
{
break;
}
/* not from a port monitor we know about */
return;
}
/* only call pollfail for "stuck" and stopping processes */
}
/*
* pollfail - handle the case where a PM stops responding to a sanity poll
*
* args: sp - pointer to sac's port monitor information for
* designated port monitor
* reason - RESP or DEATH (indicates why pollfail called)
*/
void
{
#ifdef DEBUG
debug("in pollfail");
#endif
/* first, remove the utmpx entry and clean up any links */
} else {
/*
* PM in trouble - if it's still there, try to put it out of its misery
* We play with SIGCLD here to that after SIGKILL is sent, the catcher
* routine reap() is not called until we're ready (note: when a catcher
* is established for SIGCLD and any zombies are present, the signal is
* immediately received)
*/
/* try to restart it */
"<%s> stopped responding to sanity polls - trying to restart",
else
"<%s> has died - trying to restart",
} else {
}
}
}
/*
* readpipe - read messages from _sacpipe
*/
void
readpipe()
{
/*
* This routine's main purpose is to maintain the state associated with
* each of the known port monitors. Because it may be confusing, following
* is a brief discussion of what is happening. Three different views of
* a port monitor's state exist: sc_sstate, sc_pstate, and sc_lstate.
* sc_sstate is the state in which the sac has been instructed to place
* a port monitor. sc_lstate is essentially a shadow of this field, however,
* it will only take on the values ENABLED, DISABLED, and NOTRUNNING.
* sc_lstate is used if a port monitor dies to restart it in the state in
* which it was last running. sc_pstate is the last state that the port
* monitor reported itself in. Note that if the administrator specifies
* a state change, there is a window where sc_sstate and sc_pstate will
* be different (until the port monitor enacts and acknowledges the change).
*
* These states interact with the polling loop to determine which message
* should be sent to a port monitor. If the states agree, an SC_STATUS
* is sent. If they disagree, the appropriate message to put the port
* monitor in the correct state is sent (SC_ENABLE or SC_DISABLE). sc_pstate
* is the state that is reported back to an AC_STATUS request. Finally,
* when in recovery (sc_sstate and sc_pstate both = UNKNOWN), the sac will
* take the port monitor's reported state as the true state. This is the
* only instance in which a port monitor can cause sc_sstate to change.
*/
for (;;) {
continue;
}
/*
* there's data after the header, unfortunately, we don't understand
* any of it because only class 1 (no data) messages are defined. Just
* flush it
*/
if (ret < 0) {
continue;
}
else
}
log("message from unknown process");
continue;
}
case PM_UNKNOWN:
"port monitor <%s> didn't recognize message",
/* fall through */
case PM_STATUS:
/*
* paranoia check, if port monitor reports garbage
* state, pretend it said UNKNOWN
*/
"port monitor <%s> reporting invalid state",
}
/* everyone agrees on the current state */
/* special case for recovery */
/* sc_lstate NOTRUNNING by default */
}
/*
* something isn't right here, PM
* changed state without orders, try
* to restore to correct state
*/
}
/* PM changed to state requested */
"port monitor <%s> changed state from %s to %s",
/*
* something isn't right here, PM isn't
* in the state it was, nor is it in the
* state we just tried to put it in, try
* to restore to correct state if we should
*/
}
break;
default:
"port monitor <%s> sent an invalid message - ignoring it",
break;
}
/* no matter what, PM did answer the poll */
/* Note the messages it understands */
}
}
/*
* validstate - determine if arg s a valid return state from a port monitor
* return 1 if ok, 0 otherwise
*
* args: state - state to be verified
*/
int
{
switch (state) {
case PM_ENABLED:
case PM_DISABLED:
case PM_STARTING:
case PM_STOPPING:
return (1);
default:
return (0);
}
}
/*
* mk_cmd_pipe - create the command pipe used by sacadm
*/
int
{
/* make sure there is a file here to mount on */
if (fd < 0)
return (fds[1]);
}
/*
* startpoll - enable polling on command pipe by setting up to catch SIGPOLL
*/
void
{
}