957 lines
27 KiB
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;
|
|
}
|
|
}
|