cdesktopenv/cde/lib/tt/bin/ttsession/mp_server.C

957 lines
27 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
//%% (c) Copyright 1993, 1994 Novell, Inc.
//%% $TOG: mp_server.C /main/10 1999/10/14 18:38:32 mgreess $
/*
* @(#)mp_server.C 1.119 95/09/26
*
* Copyright (c) 1992 by Sun Microsystems, Inc.
*/
#include "tt_options.h"
#include "mp_s_global.h"
#include "mp/mp_mp.h"
#include "mp_s_mp.h"
#include "mp_ptype.h"
#include "mp_s_session.h"
#include "mp_typedb.h"
#include "util/copyright.h"
#include "util/tt_enumname.h"
#include "util/tt_global_env.h"
#include "util/tt_port.h"
#include "util/tt_gettext.h"
#include "db/db_server.h"
#include <locale.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#define MAXARGS 256
#define MAXPIDS 256
#include <sys/resource.h>
#include <unistd.h>
#if defined(sgi) || defined(CSRG_BASED)
#include <getopt.h>
#endif
TT_INSERT_COPYRIGHT
#ifdef OPT_PATCH
static char PatchID[] = "Patch Id: 100626_03.";
static int Patch_ID100626_03;
#endif
// flag set by the signal handler function whenever we receive a
// signal that the types database changed.
int signal_types_changed = 0;
int signal_toggle_trace = 0;
// option_classing_engine determines whether we use the Classing Engine
// to read the ptype/otype database. It's starting value determines the
// default.
int option_classing_engine = 0;
// if equal to 1 then this option means that non process-tree
// ttsessions will fork a child and exit when the child ttsession is
// ready to service clients.
int background_mode = 1;
// this is a special case of a process-tree session that forks a child
// ttsession (rather than forking a root process) and then prints out
// the child ttsession session id when it is ready to service clients.
int print_sessid = 0;
// the level of authorization we are to use. one of: "unix", "none",
// or "des". The default is "none".
char *option_auth_level = (char *)0;
// Whether to maximize the fd limit.
int option_maximize_procids = 0;
// the pid of the root process that gets forked in a process-tree
// session, or of the child ttsession
pid_t forked_pid = (pid_t)-1;
void sig_handler(int sig);
#ifdef OPT_XTHREADS
static void init_self();
#endif
// buffer used to write out the session id of a child ttsession when the
// special print_sessid option is turned on.
char session_buf[255];
// pipe used by print_sessid to communicate the session id from
// the child ttsession to the parent ttsession.
int ds_fds[2];
// Sink for ttsession error output. If 0, output goes to syslog.
FILE *errstr = stderr;
pid_t child_waited_for;
//
// failed_procs is used to record any forked processes that have
// exited. Essentially it is a circular list of pids. Because it is
// typically updated inside a signal handler, it cannot use
// dynamically allocated memory so it is implemented as a fixed-size
// array of pids with two pointers into the array: _tt_s_mp->fin is an
// index into the first unset slot in the array. When a child process
// exits it's pid is recorded in this slot and the _tt_s_mp->fin index
// is incremented. The other index records the last "processed" slot.
// It is the last slot that was processed by the notify_start_failure
// routine that processes this list of pids. Whenever _tt_s_mp->fin is
// not equal to _tt_s_mp->fout then new pids were added to the list
// and need to be processed by notify_start_failure. Note that the
// indexes wrap around to 0 when they reach MAXPIDS. This means that
// if more than MAXPIDS processes are entered into failed_procs before
// they are consumed by notify_start_failure then some of the failed
// processes won't get handled properly. This event was considered
// unlikely but if it starts to happen then a larger value of MAXPIDS
// should be chosen.
//
// (see notify_start_failure, sig_handler, _Tt_s_mp::main_loop, and
// _Tt_s_mp::_Tt_s_mp)
pid_t failed_procs[MAXPIDS];
// Forward declarations
void install_signal_handler();
void notify_start_failure();
int init_types();
void print_usage_and_exit();
//
// Returns diagnosed child exit status, or -1 if we are not convinced
// the child exited.
//
int
child_exit_status(pid_t child, _Tt_wait_status status)
{
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 2,
"child (%d) exited due to signal %d%s" ),
child, WTERMSIG(status),
WCOREDUMP(status) ?
catgets( _ttcatd, 3, 3,
" (core dumped)" )
: "" );
return 1;
} else {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 4,
"child (%d) neither exited nor "
"was signaled!" ),
child );
return -1;
}
}
int main(int argc, char **argv)
{
char *cmd = (char *)0;
char *cargv[MAXARGS];
int c;
extern char *optarg;
extern int optind;
extern int opterr;
int maxfds;
Tt_status status;
_Tt_wait_status ch_status;
int i;
//
// Initialize all the global objects needed.
//
_tt_global = new _Tt_global();
_tt_s_mp = new _Tt_s_mp;
_tt_mp = (_Tt_mp *)_tt_s_mp;
_tt_s_mp->exit_main_loop = 1;
//
// parse command-line options
//
// used by other objects when they need to print out error
// messages to the user.
_tt_global->progname = argv[0];
setlocale( LC_ALL, "" );
_tt_openlog( _tt_global->progname, LOG_PID | LOG_CONS | LOG_NOWAIT,
LOG_DAEMON );
// needed by the getopt call
opterr = 0;
while ((c = getopt(argc, argv, "A:pa:htvcd:sSXEN")) != -1) {
switch (c) {
case 'A':
// set maximum number of undelivered messages
// in ttsession.
_tt_s_mp->max_active_messages = atoi(optarg);
break;
case 'a':
// set authorization level
option_auth_level = optarg;
break;
case 'S':
// don't fork and exit (which is the default).
background_mode = 0;
break;
case 's':
// don't print out any status messages
_tt_global->silent = 1;
break;
case 'v':
// print versions and exit
_TT_PRINT_VERSIONS((char *)_tt_global->progname)
exit(0);
case 'c':
// process-tree session. Parse the rest of the
// command-line arguments as arguments to the
// process-tree program. This means that -c
// should always be the last ttsession option
// used.
// set up the server session to be a
// process-tree session.
_tt_mp->initial_session->set_env(_TT_ENV_PROCESS_TREE,
(char *)0);
// set up buffer of arguments to pass to the
// program we're going to fork as the root of
// the process-tree
for (i=0;argv[optind] && i < MAXARGS; i++, optind++) {
cargv[i] = argv[optind];
}
if (i==MAXARGS) {
errno = E2BIG;
_tt_syslog( errstr, LOG_ERR, "%m" );
exit(1);
}
cargv[i] = NULL;
// if no program given then use $SHELL
if (! cargv[0]) {
cargv[0] = getenv("SHELL");
cargv[1] = NULL;
}
cmd = cargv[0];
background_mode = 0;
// exit out of command-processing here
// (otherwise, getopt will want to reparse the
// arguments!)
goto endopt;
case 'd':
// set up an X tooltalk session to the given X
// display.
if (getenv("DISPLAY") == 0) {
_tt_putenv( "DISPLAY", optarg );
}
_tt_mp->initial_session->set_env(_TT_ENV_X11, optarg);
break;
case 't':
// turn message tracing on.
tt_trace_control( 1 );
break;
case 'p':
// set up a process-tree session but rather
// than fork a program as the root of the
// process-tree, just set up the tooltalk
// session and then print out the session id
// to stdout. Programs can then manually set
// the _TT_SESSION or _SUN_TT_SESSION environment variable to
// this value to communicate.
_tt_mp->initial_session->set_env(_TT_ENV_PROCESS_TREE,
(char *)0);
cmd = (char *)0;
print_sessid = 1;
break;
#ifdef OPT_CLASSING_ENGINE
case 'E':
// turn on use of Classing Engine
option_classing_engine = 1;
break;
#endif
case 'X':
option_classing_engine = 0;
break;
case 'N':
option_maximize_procids = 1;
break;
case 'h':
case '?':
default:
// error or help request.
print_usage_and_exit();
break;
}
}
endopt:
if (option_maximize_procids && (_tt_zoomdtablesize() != 0)) {
_tt_syslog( errstr, LOG_WARNING,
catgets( _ttcatd, 3, 5,
"cannot maximize clients because %m"));
}
// sanity check
if (_tt_mp->initial_session->env() == _TT_ENV_LAST) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 6,
"No scope to manage. Use -c, -p, "
"-d, or set $DISPLAY." ));
exit(1);
}
//
// set authorization level to either the command-line option,
// the value of $TOOLTALK_AUTH_LEVEL or the default.
//
if (option_auth_level == (char *)0) {
option_auth_level = getenv("TOOLTALK_AUTH_LEVEL");
}
if (option_auth_level == (char *)0) {
_tt_mp->initial_session->set_auth_level(_TT_AUTH_ICEAUTH);
} else if (!strcmp(option_auth_level, "unix")) {
_tt_mp->initial_session->set_auth_level(_TT_AUTH_UNIX);
_tt_s_mp->unix_cred_chk_flag = 1;
} else if (!strcmp(option_auth_level, "cookie")) {
_tt_mp->initial_session->set_auth_level(_TT_AUTH_ICEAUTH);
} else if (!strcmp(option_auth_level,"des")) {
_tt_mp->initial_session->set_auth_level(_TT_AUTH_DES);
} else if (!strcmp(option_auth_level,"none")) {
_tt_mp->initial_session->set_auth_level(_TT_AUTH_NONE);
} else {
print_usage_and_exit();
}
// check for conflicting options being set.
if (background_mode && ! print_sessid &&
_tt_mp->initial_session->env() == _TT_ENV_PROCESS_TREE) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 7,
"Cannot use -S option with -c" ));
exit(1);
}
install_signal_handler();
signal(SIGCHLD, SIG_DFL);
// set up a pipe which will be used by the child ttsession to
// communicate it's session id to the parent ttsession which
// will then print that out and then exit. (see SIGTERM case
// in sig_handler below).
if (print_sessid) {
pipe(ds_fds);
}
if (!background_mode || (forked_pid=fork())==0) {
// We are here if and only if we are a ttsession process
// that will manage a session.
// set ourselves to be the process group leader
// so that our parent's signals won't propagate
// down to us.
if (!background_mode) {
setpgid(0,0);
}
// re-install signal handler
install_signal_handler();
// Call the s_init method for _tt_mp. This will
// cause the initial server session to be initiated
// (ie. setup for rpc servicing and advertising our
// address to tooltalk clients).
status = _tt_s_mp->s_init();
// now we check startup status, if we found another
// session running then we return status=2. For other
// errors we return status=1. (See auto-start code in
// mp_session.cc in _Tt_session::init)
switch (status) {
case TT_OK:
// rpc servicing has been initiated. Now we
// can continue the initialization process.
// This process is done this way because we
// want to set up rpc servicing as soon as
// possible so that clients can begin
// contacting ttsession (they won't get
// replies back until we're done with
// initialization but that's ok).
if (print_sessid) {
// write our session id to a pipe that
// was set up by our parent.
sprintf(session_buf,"%s\n",
(char *)(_tt_mp->initial_session->address_string()));
write(ds_fds[1], session_buf, 255);
}
// initialize ptypes/otypes
if (! init_types()) {
// init_types() has emitted diagnostic
exit(1);
}
// if we are running as a child ttsession then
// we're now at a point where we're ready to
// start servicing clients so kill off our
// parent.
if (background_mode) {
// now were ready to accept requests so
// kill the parent app.
kill(getppid(), SIGTERM);
}
break;
case TT_ERR_SESSION:
// couldn't initialize because there's already
// a ttsession running in our session scope.
exit(2);
case TT_ERR_NOMP:
exit(1);
break;
default:
_tt_syslog( errstr, LOG_ERR,
"_Tt_s_session::s_init(): %d (%s)!",
status, _tt_enumname(status));
exit(1);
}
} else {
// We're running as a parent ttsession, and we will
// not be managing a ToolTalk session. Wait for our
// child ttsession to either exit or kill us off with
// a SIGTERM indicating it's ready.
if (waitpid(forked_pid, &ch_status, 0) < 0) {
//
// Our signal handler should have called exit()!
//
_tt_syslog( errstr, LOG_ERR, "wait(): %m" );
exit(1);
}
// if the wait returns then child ttsession must have
// died abnormally. Return the status returned by the
// child process.
if (WIFEXITED(ch_status)) {
// An exit status==2 is returned if the child
// ttsession already found a session that is
// active so we avoid printing out an error
// message in this case.
int exitStatus = WEXITSTATUS(ch_status);
if (exitStatus != 2) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 8,
"child ttsession exited"
" with status %d" ),
exitStatus );
}
exit(exitStatus);
} else if (WIFSIGNALED(ch_status)) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 9,
"child ttsession exited due "
"to signal %d%s" ),
WTERMSIG(ch_status),
WCOREDUMP(ch_status) ?
catgets( _ttcatd, 3, 10,
" (core dumped)" )
: "" );
exit(1);
} else {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 11,
"child ttsession neither "
"exited nor was signaled!" ));
exit(1);
}
}
//
// if we're in a process-tree session then fork the requested
// root process (contained in "cmd"). This process will then
// be monitored by the signal handler such that if it exits
// then the signal handler will also abort the tooltalk
// session.
//
if (cmd != (char *)0 &&
_tt_mp->initial_session->env() == _TT_ENV_PROCESS_TREE) {
int i;
switch(forked_pid = fork()) {
case -1:
_tt_syslog( errstr, LOG_ERR, "fork(): %m" );
exit(1);
case 0:
maxfds = _tt_getdtablesize();
for (i = 3; i < maxfds; i++) {
close(i);
}
_tt_restoredtablesize();
signal(SIGHUP, SIG_IGN);
execvp(cmd,
cargv);
_tt_syslog( errstr, LOG_ERR, "execvp(): %m" );
exit(1);
break;
default:
#if !defined(OPT_BSD_WAIT)
child_waited_for = waitpid(-1, 0, WNOHANG);
#else
child_waited_for = wait3(&ch_status, WNOHANG, 0);
#endif
if (child_waited_for < 0) {
_tt_syslog( errstr, LOG_ERR, "waitpid(): %m" );
if (errno == ECHILD) {
exit(1);
}
} else if (child_waited_for == forked_pid) {
//
// XXX This really cannot happen here,
// because we handle SIGCHLD, so this
// all happens in sig_handler()
//
int xstat = child_exit_status(
child_waited_for, ch_status );
if (xstat >= 0) {
exit( xstat );
}
}
break;
}
}
if ((! background_mode) && (print_sessid)) {
printf("%s\n",
(char *)_tt_mp->initial_session->address_string());
fflush(stdout);
}
#ifdef OPT_XTHREADS
// Set the global lock. This basically locks out the init_self
// thread until it is given permission to proceed. Locks are
// given up just before RPC calls and upon thread exits.
// Only this initial call should use mutex_lock as opposed
// to _tt_global->grab_mutex, because here we do not want
// to wait on the condition variable here.
_tt_global->grab_mutex();
// Initialize as a client of ourself. Ignore failure.
xthread_fork((void *(*)(void *)) init_self, NULL);
#else
_tt_s_mp->init_self();
#endif
//
// main service loop. We basically check for any programs we
// forked exiting, check for flags set by the signal handler
// (_tt_s_mp->exit_main_loop means exit out of rpc servicing,
// signal_types_changed means we got a signal to re-read the
// types database). If no signal flags were set or programs
// exited, then we just invoke the _Tt_s_mp::main_loop method
// to service rpc requests.
//
if (background_mode) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 12, "starting" ));
// We are in daemon mode, so error output goes to syslog.
errstr = 0;
}
while (1) {
_tt_s_mp->exit_main_loop = 0;
if (_tt_s_mp->fout != _tt_s_mp->fin) {
notify_start_failure();
}
_tt_s_mp->main_loop();
if (_tt_s_mp->xfd == -2) {
// X server exited
break;
}
//
// exit from main_loop method is the result of
// a signal (see sig_handler below)
//
if (signal_types_changed) {
if (init_types()) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 13,
"have re-read types"));
} else {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 14,
"error in types; "
"keeping old types" ));
}
signal_types_changed = 0;
}
if (signal_toggle_trace) {
int was_on = tt_trace_control( -1 );
_tt_syslog( errstr, LOG_ERR,
"tt_trace_control( %d )", !was_on );
signal_toggle_trace = 0;
}
if (_tt_s_mp->fout != _tt_s_mp->fin) {
notify_start_failure();
}
}
delete _tt_mp;
exit(0);
}
#ifdef OPT_XTHREADS
static void init_self()
{
_tt_s_mp->init_self();
xthread_exit(0);
}
#endif
//
// This routine is called whenever _tt_s_mp->fin is not equal to
// _tt_s_mp->fout (see comment for failed_procs above). This condition
// signals that an asynchronous signal from the system came in telling us
// that a child process exited. What we need to do is search the list of
// ptypes for ptypes that are in the process of launching (see
// _Tt_ptype::launch). If we find one whose start pid matches one of the
// pids in the failed_procs array then we invoke the
// _Tt_ptype::launch_failed method to arrange for the _Tt_ptype object to
// handle this condition. The reason that this launch failure is special
// is that a ptype that is in the process of being launched may not have
// gotten far enough to launch a process that connects with ttsession so
// the only mechanism we have to detect the failure is that the process
// exited. What this means is that a process in a ptype's start string
// cannot exit until it either connects with ttsession and replies to its
// start message or until a process it launches does so.
//
void notify_start_failure()
{
// one of the starting ptypes failed to start.
// Find which one it was and fail all the
// messages waiting for it.
// XXX: there should be better data-structure
// support so we wouldn't have to iterate
// through all the ptypes.
_Tt_ptype_table_cursor ptypes;
int ptype_found;
while (_tt_s_mp->fout != _tt_s_mp->fin) {
ptypes.reset(_tt_s_mp->ptable);
ptype_found = 0;
while (!ptype_found && ptypes.next()) {
if (failed_procs[_tt_s_mp->fout]==ptypes->start_pid()) {
if (ptypes->launching()) {
ptypes->launch_failed();
ptype_found = 1;
}
}
}
_tt_s_mp->fout++;
if (_tt_s_mp->fout == MAXPIDS) {
_tt_s_mp->fout = 0;
}
}
}
//
// Install sig_handler as the handler for all the signals it handles.
//
void
install_signal_handler()
{
_Tt_string err = "_tt_sigset(SIG";
if (_tt_sigset(SIGHUP, &sig_handler) == 0) {
_tt_syslog( errstr, LOG_WARNING, err.cat("HUP)"));
}
if (_tt_sigset(SIGTERM, &sig_handler) == 0) {
_tt_syslog( errstr, LOG_WARNING, err.cat("TERM)") );
}
if (_tt_sigset(SIGINT, &sig_handler) == 0) {
_tt_syslog( errstr, LOG_WARNING, err.cat("INT)") );
}
if (_tt_sigset(SIGUSR1, &sig_handler) == 0) {
_tt_syslog( errstr, LOG_WARNING, err.cat("USR1)") );
}
if (_tt_sigset(SIGUSR2, &sig_handler) == 0) {
_tt_syslog( errstr, LOG_WARNING, err.cat("USR2)") );
}
if (_tt_sigset(SIGCHLD, &sig_handler) == 0) {
_tt_syslog( errstr, LOG_WARNING, err.cat("CHLD)") );
}
if (_tt_sigset(SIGPIPE, &sig_handler) == 0) {
_tt_syslog( errstr, LOG_WARNING, err.cat("PIPE)") );
}
}
//
// Reads in the ptype/otype database from the XDR (or CE) database.
// Once the types are read in from the database the types are installed
// via the appropiate _Tt_s_mp methods install_ptable and install_otable.
// Keep a pointer to the _Tt_typedb structure so we can merge in
// more types later on via tt_session_types_load.
//
int
init_types()
{
Tt_status err;
#ifdef OPT_CLASSING_ENGINE
_Tt_typedb::ce2xdr();
#endif
_tt_s_mp->tdb = new _Tt_typedb();
if (option_classing_engine) {
err = _tt_s_mp->tdb->init_ce();
if (err != TT_OK) {
if (0==getenv("OPENWINHOME")) {
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 15,
"$OPENWINHOME not set"));
}
return(0);
}
} else {
err = _tt_s_mp->tdb->init_xdr();
switch (err) {
case TT_OK:
case TT_ERR_NO_MATCH:
case TT_ERR_DBCONSIST:
case TT_ERR_PATH:
break;
default:
_tt_syslog( errstr, LOG_ERR,
"_Tt_typedb::init_xdr(): %s",
_tt_enumname(err));
}
if (err != TT_OK) {
return(0);
}
}
_tt_s_mp->install_ptable(_tt_s_mp->tdb->ptable);
_tt_s_mp->install_otable(_tt_s_mp->tdb->otable);
return(1);
}
//
// Prints out a usage string and exits.
//
void
print_usage_and_exit()
{
_tt_syslog( errstr, LOG_ERR, "%s%s%s%s%s",
catgets( _ttcatd, 3, 16,
"\nUsage: ttsession [-a cookie|unix|des][-d display][-spStvhNX" ),
#if defined(OPT_CLASSING_ENGINE)
"E",
#else
"",
#endif
catgets( _ttcatd, 3, 17,
"][-c command]\n"
" -c [command] start a process tree session, and run command in it.\n"
" Subsequent options are passed to command. Default: $SHELL\n"
" -p start a process tree session, and print its id\n"
" -d display start an X session on display\n"
"\n"
" -a cookie|unix|des set server authentication level\n"
" -s silent. Don't print out any warnings\n"
" -S don't fork into the background\n"
" -N maximize the number of clients allowed\n"
" -t turn on message tracing\n"
" -X use XDR databases for static types (default)\n" ),
#if defined(OPT_CLASSING_ENGINE)
catgets( _ttcatd, 3, 18,
" -E use Classing Engine for static types\n" ),
#else
"",
#endif
catgets( _ttcatd, 3, 19,
"\n"
" -v print out version number\n"
" -h print out this message\n"
"\n"
"Signal interface:\n"
" kill -USR1 ttsession_pid toggle message tracing\n"
" kill -USR2 ttsession_pid re-read static types" ) );
exit(1);
}
//
// Global signal handler for ttsession. All signals are handled by this
// function (ie. no signal handlers should be defined in any other files)
//
void
sig_handler(int sig)
{
int intrs = 100;
pid_t child_pid;
_Tt_wait_status status;
switch (sig) {
case SIGPIPE:
// usually the result of a write on a broken tcp
// socket. Default action is to ignore it.
break;
case SIGHUP:
break;
case SIGCHLD:
// a child process has exited. If the process that
// exited is the root process of a process-tree
// session then we exit. Otherwise, it is possibly a
// ptype that we launched so we record its pid in the
// failed_procs list (see comment above for
// failed_procs).
#if !defined(OPT_BSD_WAIT)
// XXX: the sysv code should do the same loop as
// below.
child_pid = waitpid(-1, &status, WNOHANG);
#else
// we do an asynchronous wait on the child to get its
// pid. However the wait3 call can be interrupted and
// return an EINTR so we keep trying for a bounded
// amount of time.
while (((child_pid = wait3(&status, WNOHANG, 0)) == -1)
&& errno == EINTR) {
if (! intrs--) {
_tt_syslog( errstr, LOG_ERR, "wait3(): %m" );
break;
}
}
#endif // OPT_BSD_WAIT
// check for the child pid being the root process of a
// process-tree id and exit if it is. Otherwise record
// the pid in the failed_procs list and set
// _tt_s_mp->exit_main_loop to 1 to signal the main
// event loop to break out of rpc servicing to handle
// this condition.
if (child_pid > 0) {
int status2exit = child_exit_status(child_pid, status);
int isdead = status2exit >= 0;
if (isdead) {
if (child_pid == forked_pid) {
// calling free in a sig handler is a no-no
// delete _tt_mp;
exit(status2exit);
}
else
#ifdef OPT_XTHREADS
if(child_pid !=
_tt_s_mp->garbage_collector_pid)
#endif
{
failed_procs[_tt_s_mp->fin] = child_pid;
_tt_s_mp->fin++;
if (_tt_s_mp->fin == MAXPIDS) {
_tt_s_mp->fin = 0;
}
}
}
_tt_s_mp->exit_main_loop = 1;
return;
}
_tt_syslog( errstr, LOG_WARNING, "waitpid(): %m");
break;
case SIGTERM:
// this signal is sent by a forked ttsession when it
// is ready to start servicing clients. It is our
// signal to exit. For the special case of a "dialin"
// session we print out the child session's id.
if ((background_mode) && (forked_pid > 0)) {
if (print_sessid) {
memset(session_buf, 0, 255);
read(ds_fds[0], session_buf, 255 - 1);
printf("%s", session_buf);
}
// this is the signal from the forked
// ttsession that we should exit.
exit(0);
}
// The child ttsession falls through to clean itself up
case SIGINT:
_tt_syslog( errstr, LOG_ERR,
catgets( _ttcatd, 3, 20, "exiting" ));
// if this is a process tree system, let our kid know
// it's time to depart. It's important to allow the
// process tree root process to exit beforehand so
// that the controlling terminal will get properly
// reassigned. Otherwise, the SIGINT will cause the
// entire process hierarchy that has the same
// controlling terminal to exit (so for example your
// shelltool window would die because ttsession was
// killed in process-tree mode).
if (forked_pid > 0
&& 0 == kill(forked_pid, SIGINT)) {
// Wait for a decent interval to give him a chance
// to clean up
int i;
for (i=0; i<10; i++) {
sleep(1);
if (forked_pid ==
waitpid(forked_pid, 0, WNOHANG)) {
break;
}
}
if (i==10) {
// Won't go away, eh? Time to get tough.
if (0 == kill(forked_pid, SIGKILL)) {
waitpid(forked_pid, 0, 0);
}
}
}
// calling free in a sig handler is a no-no
// delete _tt_mp;
exit(1);
break;
case SIGUSR1:
// signal to toggle message tracing
signal_toggle_trace = 1;
break;
case SIGTYPES:
// signal to reread types database
_tt_s_mp->exit_main_loop = 1;
signal_types_changed = 1;
break;
default:
break;
}
}