/* * 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 1995 Digital Equipment Corporation. * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company * (c) Copyright 1993, 1994, 1995 International Business Machines Corp. * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc. * (c) Copyright 1993, 1994, 1995 Novell, Inc. * (c) Copyright 1995 FUJITSU LIMITED. * (c) Copyright 1995 Hitachi. */ /****************************************************************************** * * File: Main.c * RCS: $TOG: Main.c /main/9 1999/09/20 15:36:11 mgreess $ * Package: dtexec for CDE 1.0 * *****************************************************************************/ #define SPINBLOCK if (getenv("_DTEXEC_DEBUG")) {int i; i=1; while(i) i=1;} /* * _DTEXEC_NLS16 controls whether I18N code should be used. As of 7/94, * libDtSvc should be the only one using dtexec, and the command line * and parsing code are I18N insensitive, and thats o.k. * * If turned on, some routines from DtSvc/DtUtil2/DtNlUtils.c will be * needed. */ #undef _DTEXEC_NLS16 #include #include #include #include #include #include #include "osdep.h" /* select(2) mask width and bit manipulation macros */ #include #include #include #include #include #include
/* * From Dt/ActionP.h - never change since these are in effect protocol * codes! See Dt/ActionP.h for more details. */ #ifndef _ActionP_h #define _DtActCHILD_UNKNOWN (1<<0) /* 1 - child status unknown */ #define _DtActCHILD_PENDING_START (1<<1) /* 2 - child start pending */ #define _DtActCHILD_ALIVE_UNKNOWN (1<<2) /* 4 - child alive but unknown*/ #define _DtActCHILD_ALIVE (1<<3) /* 8 - child alive and well */ #define _DtActCHILD_DONE (1<<4) /* 16 - child done */ #define _DtActCHILD_FAILED (1<<5) /* 32 - child failed */ #define _DtActCHILD_CANCELED (1<<6) /* 64 - child canceled */ #endif /* _ActionP_h */ #define MAX_EXEC_ARGS 1000 /* Maximum number of arguments for */ /* execvp call. */ #define True 1 #define False 0 #define PERM_TERM 1 #define TRANSIENT 2 #define SHORT 3 /* * Triggered by a SIGCLD, the shutdown sequence. */ #define SDP_DONE_STARTING 1 #define SDP_DONE_REPLY_WAIT 2 #define SDP_DONE_REPLIED 3 #define SDP_DONE_PANIC_CLEANUP 4 #define SDP_FINAL_LINGER 5 /* * Timeout period in milliseconds for select() when we are rediscovering * signals and engage in a shutdown process. More often than not, we * will services signals out of select() immediately. The only interesting * moment is waiting for a Done(Reply). */ #define SHORT_SELECT_TIMEOUT 20 /* * Local func protos */ void DoneRequest(int doneCode); /* * Global variables. */ struct timeval startTimeG; struct timezone zoneG; int requestTypeG; pid_t childPidG; /* PID of child we fork/exec */ long waitTimeG; /* -open setting */ char *dtSvcProcIdG; /* TT Proc ID of our caller */ int dtSvcInvIdG; /* Invocation ID that our caller manages us by*/ int dtSvcChildIdG; /* Child ID that our caller manages us by */ int tmpFileCntG; /* tmp file count */ char **tmpFilesG; /* tmp files we might need to unlink */ fd_set allactivefdsG; /* all possible fildes of interest. */ /* - mskcnt generated from osdep.h */ int rediscoverSigCldG; /* if a SIGCLD goes off */ int rediscoverUrgentSigG; /* if a SIGTERM, SIGHUP or SIGQUIT goes off */ int shutdownPhaseG; /* shutdown progress state variable */ int ttfdG; /* tooltalk fildes */ int errorpipeG[2]; /* dtexec <--< child stderr pipe */ /****************************************************************************** * * Minaturized versions of tttk_*() routines. Used so we don't have to * pull in tttk which in turn would pull in Xt. * *****************************************************************************/ char *dtexec_Tttk_integer = "integer"; char *dtexec_Tttk_message_id = "messageID"; Tt_message dtexec_tttk_message_create( Tt_message context, Tt_class theClass, Tt_scope theScope, const char *handler, const char *op, Tt_message_callback callback ) { Tt_message msg; Tt_address address; Tt_status status; msg = tt_message_create(); status = tt_ptr_error( msg ); if (status != TT_OK) { return msg; } status = tt_message_class_set( msg, theClass ); if (status != TT_OK) { return (Tt_message)tt_error_pointer( status ); } status = tt_message_scope_set( msg, theScope ); if (status != TT_OK) { return (Tt_message)tt_error_pointer( status ); } address = TT_PROCEDURE; if (handler != 0) { status = tt_message_handler_set( msg, handler ); if (status != TT_OK) { return (Tt_message)tt_error_pointer( status ); } address = TT_HANDLER; } status = tt_message_address_set( msg, address ); if (status != TT_OK) { return (Tt_message)tt_error_pointer( status ); } if (op != 0) { status = tt_message_op_set( msg, op ); if (status != TT_OK) { return (Tt_message)tt_error_pointer( status ); } } if (callback != 0) { status = tt_message_callback_add( msg, callback ); if (status != TT_OK) { return (Tt_message)tt_error_pointer( status ); } } return msg; } Tt_status dtexec_tttk_message_destroy( Tt_message msg ) { Tt_status status; status = tt_message_destroy(msg); return status; } int dtexec_tttk_message_am_handling( Tt_message msg ) { char *handler; int am_handling; if (tt_message_class( msg ) != TT_REQUEST) { return 0; } if (tt_message_state( msg ) != TT_SENT) { return 0; } handler = tt_message_handler( msg ); am_handling = 0; if ((tt_ptr_error( handler ) == TT_OK) && (handler != 0)) { am_handling = 1; } tt_free( handler ); return am_handling; } Tt_status dtexec_tttk_message_abandon( Tt_message msg ) { int fail; Tt_status status; if (dtexec_tttk_message_am_handling( msg )) { fail = 0; if (tt_message_address( msg ) == TT_HANDLER) { fail = 1; } else if (tt_message_status( msg ) == TT_WRN_START_MESSAGE) { fail = 1; } if (fail) { if (tt_message_class( msg ) == TT_REQUEST) { tt_message_status_set(msg, TT_DESKTOP_ENOTSUP); status = tt_message_fail( msg ); dtexec_tttk_message_destroy( msg ); } else { status = dtexec_tttk_message_destroy( msg ); } } else { tt_message_status_set( msg, TT_DESKTOP_ENOTSUP ); status = tt_message_reject( msg ); dtexec_tttk_message_destroy( msg ); } } else { status = dtexec_tttk_message_destroy( msg ); } return status; } /****************************************************************************** * * Help - print the usage and exit. * *****************************************************************************/ static void Help( char *argv[] ) { (void) fprintf (stderr, "Usage:\n"); (void) fprintf (stderr, "\t%s [-options ...] cmd [cmd arg ...]\n", argv[0]); (void) fprintf (stderr, "\n"); (void) fprintf (stderr, "where options include:\n"); (void) fprintf (stderr, "\t-open open-option\n"); (void) fprintf (stderr, "\t\t-1 (default) continue to execute after cmd terminates,\n"); (void) fprintf (stderr, "\t\t thus keeping the terminal window open.\n"); (void) fprintf (stderr, "\t\t 0 exit as soon as cmd terminates, thus allowing\n"); (void) fprintf (stderr, "\t\t the terminal window to close.\n"); (void) fprintf (stderr, "\t\t n continue to execute if cmd terminates within n\n"); (void) fprintf (stderr, "\t\t seconds of starting.\n"); (void) fprintf (stderr, "\t-ttprocid procid\n"); (void) fprintf (stderr, "\t-tmp tmpfile [-tmp tmpfile ...]\n"); (void) fprintf (stderr, "\n"); _exit(0); } /****************************************************************************** * * PanicSignal - see InitializeSignalHandling() * *****************************************************************************/ static void #if defined(__aix) || defined(CSRG_BASED) || defined(__linux__) PanicSignal(int s) #else PanicSignal(void) #endif /* __aix */ { int i; /* * Crude, but let libDtSvc know we've been forced down. * Atleast libDtSvc will get a hint and know to cleanup. */ if (dtSvcProcIdG) DoneRequest(_DtActCHILD_FAILED); if (!dtSvcProcIdG) { /* * We cannot talk with caller, so do cleanup * of tmp files. */ for (i = 0; i < tmpFileCntG; i++ ) { chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) ); unlink( tmpFilesG[i] ); } } _exit(0); } /****************************************************************************** * * IgnoreSignal - see InitializeSignalHandling() * *****************************************************************************/ static void #if defined(__aix) || defined(CSRG_BASED) || defined(__linux__) IgnoreSignal(int i) #else IgnoreSignal(void) #endif /* __aix */ { /* * If the child is still in the same process group, it should be * getting the same signal too. */ if (rediscoverSigCldG) { if (shutdownPhaseG == SDP_FINAL_LINGER) { /* * We were shutdown long ago and lingering, so go ahead * and exit now. */ _exit(0); } else { /* * Still shutting down, so flip requestTypeG so that dtexec * will not linger when the shutdown process completes. */ requestTypeG = TRANSIENT; } } else { /* * If and when the child does repond to the signal we are * ignoring for now, don't allow dtexec to linger. */ requestTypeG = TRANSIENT; } } /****************************************************************************** * * UrgentSignal - see InitializeSignalHandling() * *****************************************************************************/ static void #if defined(__aix) || defined(CSRG_BASED) || defined(__linux__) UrgentSignal(int i) #else UrgentSignal(void) #endif /* __aix */ { /* * Set global so the central control point ( select() ) will * rediscover the urgent signal. */ rediscoverUrgentSigG = 1; /* * If the child is still in the same process group, it should be * getting the same signal too. */ if (rediscoverSigCldG) { if (shutdownPhaseG == SDP_FINAL_LINGER) { /* * We were shutdown long ago and lingering, so go ahead * and exit now. */ _exit(0); } else { /* * Still shutting down, so flip requestTypeG so that dtexec * will not linger when the shutdown process completes. */ requestTypeG = TRANSIENT; } } else { /* * If and when the child does repond to the signal we are * ignoring for now, don't allow dtexec to linger. * * This is mildly different than the IgnoreSignal case because * there is a timeout associated with UrgentSignals. */ requestTypeG = TRANSIENT; } } /****************************************************************************** * * SigCld - see InitializeSignalHandling() * *****************************************************************************/ static void #if defined(__aix) || defined(CSRG_BASED) || defined(__linux__) SigCld(int i) #else SigCld(void) #endif /* __aix */ { int exitStatus; pid_t pid; /* * Query why the SIGCLD happened - a true termination or just a stopage. * We only care about terminations. */ pid = wait(&exitStatus); if (pid == -1) { if (errno == ECHILD) { /* * No child found with wait(), so sure, act like we did * see a SIGCLD. */ rediscoverSigCldG = 1; } } else if (!WIFSTOPPED(exitStatus)) { /* * The SIGCLD was *not* the result of being stopped, so the child * has indeed terminated. */ rediscoverSigCldG = 1; } } /****************************************************************************** * * InitializeSignalHandling - Set up the signal catchers. * * Keep this code in sync with fork/exec code so as to NOT pass * these catchers onto our child. * * "Ignore" * 1. if already SIGCLD * A. if SDP_FINAL_LINGER, _exit(0) * B. else set requestTypeG to TRANSIENT * 2. else ignore signal * 3. select-loop-spin * * comment: if fact we don't ignore the signal, but instead let the * child control the pace of the response to the signal. * * "Urgent" * 1. if already SIGCLD * A. if SDP_FINAL_LINGER, _exit(0) * B. else set requestTypeG to TRANSIENT * 2. wait 5 seconds hoping to goto "SIGCLD" path from select-loop-spin * 3. else do "Panic" path after 5 seconds * * "SIGCLD" * 1. send Done(Request) * 2. wait for Done(Reply) in select-loop-spin * 3. send Quit(Reply) * 4. cleanup * 5. FinalLinger() * * "Panic" * 1. send Done(Request) * 2. send Quit(Reply) * 3. cleanup * 4. _exit(0) * * "Default" * 1. default signal action * *****************************************************************************/ static void InitializeSignalHandling( void ) { long oldMask; struct sigaction svec; /* * "Graceful Signal" handlers * - SIGCLD for normal child termination - best case. */ sigemptyset(&svec.sa_mask); svec.sa_flags = 0; svec.sa_handler = SigCld; (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL); /* * "Urgent Signal" handlers * - SIGTERM for standard kill(1) * * We treat these signals in a special way. When the signal comes * in, we hope to see the child respond with a SIGCLD within 5 * seconds, else we act like the SIGTERM was for us and go down. */ sigemptyset(&svec.sa_mask); svec.sa_flags = 0; svec.sa_handler = UrgentSignal; (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL); /* * "Panic Signal" handlers. * - SIGINT for BBA coverage. */ sigemptyset(&svec.sa_mask); svec.sa_flags = 0; svec.sa_handler = PanicSignal; (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL); /* * "Ignore Signal" handlers. * - SIGUSR1 - let child decide how to respond * - SIGUSR2 - let child decide how to respond * - SIGHUP - let child decide how to respond */ sigemptyset(&svec.sa_mask); svec.sa_flags = 0; svec.sa_handler = IgnoreSignal; (void) sigaction(SIGUSR1, &svec, (struct sigaction *) NULL); (void) sigaction(SIGUSR2, &svec, (struct sigaction *) NULL); (void) sigaction(SIGHUP, &svec, (struct sigaction *) NULL); /* * "Default Signal" handlers. * - SIGQUIT - let core dump happen as expected. If done, * libDtSvc will never get a done notice. */ } /****************************************************************************** * * After all is said and done, enter FinalLinger which * will decide if and how long the associated terminal * emulator will stay up. * *****************************************************************************/ void FinalLinger(void) { int exitStatus; struct timeval finalTime; /* * Make sure to reap child. The SIGCLD handler may have done * this already. */ (void) wait(&exitStatus); if (requestTypeG == PERM_TERM) (void) sleep (99999999); else if (requestTypeG == TRANSIENT) (void) _exit (0); else { /* * Check the time stamps and either sleep forever or exit. */ if ((gettimeofday (&finalTime, &zoneG)) == -1) (void) _exit (1); if ((finalTime.tv_sec - startTimeG.tv_sec) < waitTimeG) (void) sleep (99999999); else (void) _exit (0); } } /****************************************************************************** * * ExecuteCommand - * *****************************************************************************/ static int ExecuteCommand ( char **commandArray) { int i, index1; int exitStatus; struct sigaction svec; for (index1 = 0; (index1 < 10) && ((childPidG = fork()) < 0); index1++) { /* Out of resources ? */ if (errno != EAGAIN) break; /* If not out of resources, sleep and try again */ (void) sleep ((unsigned long) 2); } if (childPidG < 0) { return (False); } if (childPidG == 0) { /* * Child Process. */ /* * Hook stderr to error pipe back to parent. */ if ((errorpipeG[0] != -1) && (errorpipeG[1] != -1)) { dup2(errorpipeG[1], 2); close(errorpipeG[0]); } /* * The child should have default behavior for all signals. */ sigemptyset(&svec.sa_mask); svec.sa_flags = 0; svec.sa_handler = SIG_DFL; /* Normal */ (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL); /* Urgent */ (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL); /* Panic */ (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL); /* Ignore */ (void) sigaction(SIGUSR1, &svec, (struct sigaction *) NULL); (void) sigaction(SIGUSR2, &svec, (struct sigaction *) NULL); (void) sigaction(SIGHUP, &svec, (struct sigaction *) NULL); for (i=3; i < FOPEN_MAX; i++) { if ( i != errorpipeG[1] ) (void) fcntl (i, F_SETFD, FD_CLOEXEC); } (void) execvp(commandArray[0], commandArray); (void) fprintf (stderr, "Cannot execute \"%s\".\n", commandArray[0]); (void) _exit (1); } if (errorpipeG[1] != -1) { close(errorpipeG[1]); errorpipeG[1] = -1; } return (True); } /****************************************************************************** * * ParseCommandLine * * Peel off options to dtexec. As soon as an argv[n] is not a valid * dtexec option, assume it is the start of the cmd line and return. * *****************************************************************************/ static char ** ParseCommandLine( int argc, char **argv ) { char **argv2; char *tick1, *tick2; #ifdef _DTEXEC_NLS16 int tmpi; #endif /* _DTEXEC_NLS16 */ argv2 = (char **) argv; tmpFileCntG = 0; tmpFilesG = (char **) NULL; for (argc--, argv++; argc > 0; argc--, argv++) { if ( ! strcmp(argv[0] , "-open") ) { argc--; argv++; if ( argc == 0 ) Help( argv2 ); waitTimeG = atoi (argv[0]); if (waitTimeG < 0) requestTypeG = PERM_TERM; else if (waitTimeG == 0) requestTypeG = TRANSIENT; else { requestTypeG = SHORT; } } else if ( ! strcmp(argv[0] , "-ttprocid") ) { argc--; argv++; if ( argc == 0 ) Help( argv2 ); /* * Pull the -ttprocid argument apart for it's 3 components. * __ */ #ifdef _DTEXEC_NLS16 tmpi = mblen(argv[0]); dtSvcProcIdG = (char *) malloc( tmpi + 1 ); memcpy( dtSvcProcIdG, argv[0], tmpi ); dtSvcProcIdG[tmpi] = NULL; #else dtSvcProcIdG = (char *) malloc( strlen(argv[0]) + 1 ); strcpy( dtSvcProcIdG, argv[0] ); #endif /* _DTEXEC_NLS16 */ /* * Search from the end for underscore separators. */ #ifdef _DTEXEC_NLS16 tick2 = (char *) Dt_strrchr( dtSvcProcIdG, '_' ); #else tick2 = (char *) strrchr( dtSvcProcIdG, '_' ); #endif /* _DTEXEC_NLS16 */ if (tick2) *tick2 = 0; #ifdef _DTEXEC_NLS16 tick1 = (char *) Dt_strrchr( dtSvcProcIdG, '_' ); #else tick1 = (char *) strrchr( dtSvcProcIdG, '_' ); #endif /* _DTEXEC_NLS16 */ if ( tick1 && tick2 ) { *tick1 = 0; *tick2 = 0; dtSvcInvIdG = atoi((char *) (tick1 + 1)); dtSvcChildIdG = atoi((char *) (tick2 + 1)); if ( !(dtSvcInvIdG && dtSvcChildIdG) ) { /* * Don't have two non-zero values, so we cannot use the * -ttprocid provided. */ free(dtSvcProcIdG); dtSvcProcIdG = (char *) NULL; } } else { /* * Unable to find _ (underscore) separators. */ free(dtSvcProcIdG); dtSvcProcIdG = (char *) NULL; } } else if ( ! strcmp(argv[0] , "-tmp") ) { argc--; argv++; if ( argc == 0 ) Help( argv2 ); tmpFileCntG++; tmpFilesG = (char **) realloc( (char *) tmpFilesG, tmpFileCntG * sizeof(char *) ); tmpFilesG[tmpFileCntG-1] = argv[0]; } else if ( ! strncmp(argv[0], "-h", 2) ) { Help( argv2 ); /* * Technically we should see if a -ttprocid was given and * possibly send back a Done(Request). */ (void) _exit (1); } else { return( argv ); } } /* * No arguments to dtexec, so nothing to fork/exec. */ return( (char **) NULL ); } /****************************************************************************** * * Shutdown Tooltalk connection. * *****************************************************************************/ void DetachFromTooltalk( unsigned long *nocare1) /* if Xt - XtInputId *id; */ { char *sessid; if (dtSvcProcIdG) { /* * NULL the global to indicate that we no longer want to * chit-chat with Tooltalk. */ dtSvcProcIdG = (char *) NULL; sessid = tt_default_session(); tt_session_quit(sessid); tt_free(sessid); tt_close(); } /* * Unregister the Tooltalk fildes from the select mask. */ if (ttfdG != -1) { BITCLEAR(allactivefdsG, ttfdG); ttfdG = -1; } } /****************************************************************************** * * Alternate input handler to tttk_Xt_input_handler * * If we end up pulling Xt in, toss this routine and use * tttk_Xt_input_handler instead. * *****************************************************************************/ void input_handler( char *nocare1, /* if Xt - XtPointer w */ int *nocare2, /* if Xt - int *source */ unsigned long *nocare3) /* if Xt - XtInputId *id; */ { Tt_message msg; Tt_status status; msg = tt_message_receive(); status = tt_ptr_error( msg ); if (status != TT_OK) { /* * Problem; think about bailing. */ if (status == TT_ERR_NOMP) { /* * Big time lost. */ DetachFromTooltalk(NULL); } return; } if (msg == 0) { /* * A pattern callback ate the message for us. */ return; } /* * All messages should have been consumed by a callback. * Pick between failing and rejecting the msg. */ status = dtexec_tttk_message_abandon( msg ); if (status != TT_OK) { /* don't care */ } } /****************************************************************************** * * Initiallize Tooltalk world. * *****************************************************************************/ static void ToolTalkError(char *errfmt, Tt_status status) { char *statmsg; if (! tt_is_err(status)) return; statmsg = tt_status_message(status); DtMsgLogMessage( "Dtexec", DtMsgLogStderr, errfmt, statmsg ); } int InitializeTooltalk(void) { char * procid; Tt_status status; int fd; procid = tt_default_procid(); status = tt_ptr_error(procid); if ((status == TT_ERR_NOMP) || (status == TT_ERR_PROCID)) { /* * We need to try to establish a connection */ procid = tt_open(); status = tt_ptr_error(procid); if (status != TT_OK) { ToolTalkError("Could not connect to ToolTalk:\n%s\n", status); return (False); } tt_free(procid); /* * Determine the Tooltalk fildes. */ fd = tt_fd(); status = tt_int_error(fd); if (status != TT_OK) { ToolTalkError("Could not connect to ToolTalk:\n%s\n", status); tt_close(); ttfdG = -1; return(False); } else { ttfdG = fd; } #ifdef DtActUseXtOverSelect /* * Add the ToolTalk file descriptor to the set monitored by Xt */ XtAddInput(fd, (XtPointer)XtInputReadMask, input_handler, 0); #endif /* DtActUseXtOverSelect */ } return (True); } /****************************************************************************** * * Send some identification back to libDtSvc so it can talk back to * dtexec. The request format is: * * op(_DtActDtexecID) - the pattern * iarg(invID) - matches libDtSvc's invocation ID * iarg(childID) - matches libDtSvc's child ID * arg(dtexec's ProcID) - dtexec's procid handle * * libDtSvc should be able to pluck the invID and childID to immediately * dereference into it's Child-Invocation-Record that is tracking this * dtexec invocation. It just slips in "dtexec's ProcID" and then full * two-way communication is established. * *****************************************************************************/ /************************************************* * * Routine to catch identification reply. */ Tt_callback_action IdSelfToCallerReplyCB( Tt_message msg, Tt_pattern pattern) { Tt_state state; Tt_status status; char *errorMsg; status = tt_message_status(msg); state = tt_message_state(msg); if (state == TT_FAILED) { /* * tjg: At some point, may want to dump the following error * message into a log file. May have to wrap long messages. */ if (status < TT_ERR_LAST) errorMsg = tt_status_message(status); else errorMsg = tt_message_status_string(msg); dtexec_tttk_message_destroy(msg); DetachFromTooltalk(NULL); } else if (state == TT_HANDLED) { dtexec_tttk_message_destroy(msg); /* * Nothing substantial to do with the request-reply in the current * implementation. */ } else { } return( (Tt_callback_action) TT_CALLBACK_PROCESSED ); } /************************************************* * * Routine to send identification request. */ void IdSelfToCallerRequest(void) { Tt_message msg; Tt_status status; char *procid; procid = tt_default_procid(); msg = dtexec_tttk_message_create( (Tt_message) NULL, TT_REQUEST, TT_SESSION, dtSvcProcIdG, "_DtActDtexecID", IdSelfToCallerReplyCB ); tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcInvIdG ); tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcChildIdG ); tt_message_arg_add( msg, TT_IN, dtexec_Tttk_message_id, procid ); status = tt_message_send( msg ); tt_free(procid); if (status != TT_OK) { dtexec_tttk_message_destroy( msg ); DetachFromTooltalk(NULL); } } /****************************************************************************** * * Send a Done notice back to libDtSvc. * * _DtActDtexecDone * iarg(invID) - matches libDtSvc's invocation ID * iarg(childID) - matches libDtSvc's child ID * iarg(DtActionStatus) - a DtActionStatus style code * *****************************************************************************/ /************************************************* * * Routine to catch identification reply. */ Tt_callback_action DoneRequestReplyCB( Tt_message msg, Tt_pattern pattern) { Tt_state state; Tt_status replyStatus; state = tt_message_state(msg); if (state == TT_FAILED) { dtexec_tttk_message_destroy(msg); DetachFromTooltalk(NULL); shutdownPhaseG = SDP_DONE_PANIC_CLEANUP; } else if (state == TT_HANDLED) { dtexec_tttk_message_destroy(msg); shutdownPhaseG = SDP_DONE_REPLIED; } else { } return( (Tt_callback_action) TT_CALLBACK_PROCESSED ); } /************************************************* * * Routine to send done request. */ void DoneRequest(int doneCode) { static int beenhere = 0; Tt_message msg; Tt_status status; char *procid; /* * Only allow one Done(Request) to be issued. */ if (!beenhere) { beenhere = 1; procid = tt_default_procid(); msg = dtexec_tttk_message_create( (Tt_message) NULL, TT_REQUEST, TT_SESSION, dtSvcProcIdG, "_DtActDtexecDone", DoneRequestReplyCB ); tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcInvIdG ); tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcChildIdG ); tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, doneCode ); status = tt_message_send( msg ); tt_free(procid); if (status != TT_OK) { dtexec_tttk_message_destroy( msg ); DetachFromTooltalk(NULL); } } } /****************************************************************************** * * main * *****************************************************************************/ int main ( int argc, char **argv ) { char **cmdLine; int success; fd_set readfds, exceptfds; int nfound; struct timeval timeoutShort, timeoutLong; int junki,i; char *tmpBuffer; int errorBytes; int firstPass, tmpi; char *tmpProgName = NULL; setlocale( LC_ALL, "" ); #ifdef _DTEXEC_NLS16 Dt_nlInit(); #endif /* _DTEXEC_NLS16 */ /* * For debugging purposes, a way to pause the process and allow * time for a xdb -P debugger attach. If no args, (e.g. libDtSvc is * test running the executable), cruise on. */ if (getenv("_DTEXEC_DEBUG") && (argc > 1)) { /* * Don't block in a system call, or on libDtSvc's attempts to * just test exec us. */ SPINBLOCK } /* * Note: dtSvcProcIdG is used like a boolean to control whether * we are communicating with libDtSvc using Tooltalk. */ dtSvcProcIdG = (char *) NULL; /* assume not communicating with TT */ ttfdG = -1; cmdLine = ParseCommandLine (argc, argv); /* * If a signal goes off *outside* the upcoming select, we'll need to * rediscover the signal by letting select() timeout. * * We might also set a rediscover flag to fake a signal response. */ rediscoverSigCldG = 0; /* boolean and counter */ rediscoverUrgentSigG = 0; /* boolean and counter */ InitializeSignalHandling (); /* * Create a pipe for logging of errors for actions without * windows. */ errorpipeG[0] = -1; /* by default, no stderr redirection */ errorpipeG[1] = -1; if ( requestTypeG == TRANSIENT ) { /* should be WINDOW_TYPE NO_STDIO */ if ( pipe(errorpipeG) == -1 ) { errorpipeG[0] = -1; errorpipeG[1] = -1; } } if (cmdLine) { success = ExecuteCommand (cmdLine); if (!success) { /* * Act like we were killed - it will result in a * DtACTION_FAILED. */ childPidG = -1; rediscoverUrgentSigG = 1; } } else { /* * Act like we had a child and it went away - it will result * in a DtACTION_DONE. */ childPidG = -1; rediscoverSigCldG = 1; } /* * Note when we started so we can compare times when we finish. */ (void) gettimeofday (&startTimeG, &zoneG); if (dtSvcProcIdG) { if ( !InitializeTooltalk() ) { /* * We have no hope of talking to our caller via Tooltalk. */ dtSvcProcIdG = (char *) NULL; } } /* * Tie in to the default session and start chatting. */ if (dtSvcProcIdG) tt_session_join(tt_default_session()); /* * Finally send caller our current proc id so they can talk back. */ if (dtSvcProcIdG) IdSelfToCallerRequest(); /* * Monitor file descriptors for activity. If errors occur on a fds, * it will be removed from allactivefdsG after handling the error. */ CLEARBITS(allactivefdsG); /* * Add Tooltalk */ if ( ttfdG != -1 ) BITSET(allactivefdsG, ttfdG); /* add Tooltalk */ /* * Add Error Log */ if ( errorpipeG[0] != -1 ) BITSET(allactivefdsG, errorpipeG[0]); /* add read side of error pipe */ /* * Set options for rediscovery and not-rediscovery modes of * operation. */ shutdownPhaseG = SDP_DONE_STARTING; /* triggered with rediscoverSigCldG */ timeoutShort.tv_sec = 0; /* in quick rediscovery mode */ timeoutShort.tv_usec = SHORT_SELECT_TIMEOUT; timeoutLong.tv_sec = 86400; /* don't thrash on rediscovery */ timeoutLong.tv_usec = 0; for (;;) { COPYBITS(allactivefdsG, readfds); COPYBITS(allactivefdsG, exceptfds); #if defined(__linux__) /* JET 9/1/98 - linux select will actually modify the timeout struct - * if a select exits early then the timeout struct will contain the * amount remaining. When this gets to 0,0, an infinite loop * will occur. So... setup the timeouts each iteration. */ timeoutShort.tv_sec = 0; /* in quick rediscovery mode */ timeoutShort.tv_usec = SHORT_SELECT_TIMEOUT; timeoutLong.tv_sec = 86400; /* don't thrash on rediscovery */ timeoutLong.tv_usec = 0; #endif if (rediscoverSigCldG || rediscoverUrgentSigG) { nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL), FD_SET_CAST(&exceptfds), &timeoutShort); } else { nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL), FD_SET_CAST(&exceptfds), &timeoutLong); } if (nfound == -1) { /* * Handle select() problem. */ if (errno == EINTR) { /* * A signal happened - let rediscover flags redirect flow * via short select timeouts. */ } else if ((errno == EBADF) || (errno == EFAULT)) { /* * A connection probably dropped. */ if (ttfdG != -1) { if ( GETBIT(exceptfds, ttfdG) ) { /* * Tooltalk connection has gone bad. * * Judgement call - when the Tooltalk connection goes * bad, let dtexec continue rather than doing an exit. */ DetachFromTooltalk(NULL); } } if (errorpipeG[0] != -1) { if ( GETBIT(exceptfds, errorpipeG[0]) ) { /* * Error pipe has gone bad. */ close(errorpipeG[0]); BITCLEAR(allactivefdsG, errorpipeG[0]); errorpipeG[0] = -1; } } } else { /* * We have bad paremeters to select() */ } /* * So that select() errors cannot dominate, now behave as * though only a timeout had occurred. */ nfound = 0; } if (nfound > 0) { /* * Have some input to process. Figure out who. */ if (ttfdG != -1) { if ( GETBIT(readfds, ttfdG) ) { /* Clear bit first, since calling input_handler() could */ /* have the side-effect of setting ttfdG to -1! */ BITCLEAR(readfds, ttfdG); /* * Tooltalk activity. * * Note that the input_handler parameters match * an XtInputHandler() style callback in case Xt is * ever used. */ input_handler((char *) NULL, (int *) &junki, (unsigned long *) &junki); } } if (errorpipeG[0] != -1) { if ( GETBIT(readfds, errorpipeG[0]) ) { /* * Stderr activity. * * Read the errorpipe until no more seems available. * Call that good enough and write a time-stamped * block to the errorLog file. */ errorBytes = 0; /* what we have so far */ tmpBuffer = NULL; firstPass = 1; while (1) { char buf; nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL), FD_SET_CAST(NULL), &timeoutShort); if (nfound > 0) { tmpi = read (errorpipeG[0], &buf, 1); } else { tmpi = 0; } if ( tmpi > 0 ) { /* * Grow buffer to hold entire error stream. */ firstPass = 0; if (tmpBuffer == NULL) tmpBuffer = (char *) malloc( tmpi + 1); else tmpBuffer = (char *) realloc( tmpBuffer, errorBytes + tmpi + 1); /* * Drain error pipe. */ tmpBuffer[errorBytes] = buf; errorBytes += tmpi; tmpBuffer[errorBytes] = '\0'; if (errorBytes < 65535) { /* * Pause a bit and wait for a continuation of * the error stream if there is more. */ select(0, FD_SET_CAST(NULL), FD_SET_CAST(NULL), FD_SET_CAST(NULL), &timeoutShort); } else { /* * We have enough to do a dump now. */ break; } } else { /* * No more to read. */ if (firstPass) { /* * On the first pass after select(), if we have 0 bytes, * it really means the pipe has gone down. */ close(errorpipeG[0]); BITCLEAR(allactivefdsG, errorpipeG[0]); BITCLEAR(readfds, errorpipeG[0]); errorpipeG[0] = -1; } break; } } if (tmpBuffer) { if (!tmpProgName) { tmpProgName = (char *) malloc (strlen (argv[0]) + strlen (cmdLine[0]) + 5); if (!tmpProgName) tmpProgName = argv[0]; else { /* * To identify the process for this stderr, * use both argv[0] and the name of the * process that was execvp'd */ (void) strcpy (tmpProgName, "("); (void) strcat (tmpProgName, argv[0]); (void) strcat (tmpProgName, ") "); (void) strcat (tmpProgName, cmdLine[0]); } } DtMsgLogMessage( tmpProgName, DtMsgLogStderr, "%s", tmpBuffer ); free( tmpBuffer ); } if (errorpipeG[0] != -1) BITCLEAR(readfds, errorpipeG[0]); } } /* * So that select() data cannot dominate, now behave as * though only a timeout had occurred. */ nfound = 0; } if (nfound == 0) { /* * Timeout. We are probably rediscovering and have entered * a shutdown phase. The following rediscover handlers are * in priority order. * * Note that by way of timeouts and events, we will make * multiple passes through this block of code. */ if (rediscoverUrgentSigG) { /* * Handle urgent signal. * * Tact: wait awhile and see if a SIGCLD will happen. * If it does, then a normal shutdown will suffice. * If a SIGCLD does not happen, then do a raw exit(0). * Exit is required for BBA anyway. */ if (rediscoverSigCldG) /* * Rather than act on the Urgent Signal, defer to the * SIGCLD Signal shutdown process. */ rediscoverUrgentSigG = 0; else /* * Still in a mode where we have an outstanding * Urgent Signal but no SIGCLD. Bump a counter * which moves us closer to doing an exit(). */ rediscoverUrgentSigG++; /* * After 5 seconds (add select timeout too) waiting for * a SIGCLD, give up and exit. */ if (rediscoverUrgentSigG > ((1000/SHORT_SELECT_TIMEOUT)*5) ) { #if defined(__aix) || defined(CSRG_BASED) || defined(__linux__) PanicSignal(0); #else PanicSignal(); #endif } } if (rediscoverSigCldG) { /* * Handle SIGCLD signal. * * Under SIGCLD, we will make multiple passes through the * following, implementing a phased shutdown. */ if (shutdownPhaseG == SDP_DONE_STARTING) { /* * Send Done(Request) for starters. */ if (dtSvcProcIdG) DoneRequest(_DtActCHILD_DONE); if (dtSvcProcIdG) { /* * Sit and wait for the Done Reply in select() */ shutdownPhaseG = SDP_DONE_REPLY_WAIT; } else { /* * Unable to send Done Reply. Assume we're on * our own from now on. */ shutdownPhaseG = SDP_DONE_PANIC_CLEANUP; } } if (shutdownPhaseG == SDP_DONE_REPLY_WAIT) { /* * After 5 minutes of passing through REPLY_WAIT, * assume the Done(Reply) will never come in and * move on. */ rediscoverSigCldG++; if (rediscoverSigCldG > ((1000/SHORT_SELECT_TIMEOUT)*300)) { if (dtSvcProcIdG) { /* * Try to detatch from Tooltalk anyway. */ DetachFromTooltalk(NULL); } shutdownPhaseG = SDP_DONE_PANIC_CLEANUP; } /* * See if the Tooltalk connection is still alive. If * not, then no reason to wait around. */ else if (!dtSvcProcIdG) { shutdownPhaseG = SDP_DONE_PANIC_CLEANUP; } } if (shutdownPhaseG == SDP_DONE_REPLIED) { /* * We have our Done(Reply), so proceed. */ if (dtSvcProcIdG) shutdownPhaseG = SDP_FINAL_LINGER; else shutdownPhaseG = SDP_DONE_PANIC_CLEANUP; } if (shutdownPhaseG == SDP_DONE_PANIC_CLEANUP) { /* * We cannot talk with caller, so do cleanup * of tmp files. */ for (i = 0; i < tmpFileCntG; i++ ) { chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) ); unlink( tmpFilesG[i] ); } shutdownPhaseG = SDP_FINAL_LINGER; } if (shutdownPhaseG == SDP_FINAL_LINGER) { /* * All done. */ static int skipFirst = 1; if (skipFirst) { /* * Rather than a quick departure from the select() * loop, make one more pass. If the child has gone * down quickly, the SIGCLD may have caused us to * get here before any errorPipeG information has * had a chance to reach us. */ skipFirst = 0; } else { FinalLinger(); } } } } } }