/****************************************************************************** * * 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 (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 occured. */ 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 occured. */ 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 (__osf__) || 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(); } } } } } }
WaitForInput() { int Quit(); int i; struct timeval waittime, *wt; long timeout; long readyClients[mskcnt]; long curclient; int selecterr; CLEARBITS(readyClients); COPYBITS(AllSockets, LastSelectMask); wt = NULL; /* if (!ANYSET(AllClients)) { wt = &waittime; waittime.tv_sec = 5*60; waittime.tv_usec = 0; } else { sleep(1); alarm(NSECONDS); signal(SIGALRM, catchit); while(wait((int *)0) > 0); alarm(0); wt = NULL; } */ i = select (MAXSOCKS, LastSelectMask, (int *) NULL, (int *) NULL, wt); selecterr = errno; if (i <= 0) /* An error or timeout occurred */ { if(i == 0) Quit(); if (i < 0) if (selecterr == EBADF) /* Some client disconnected */ CheckConnections (); else if (selecterr != EINTR) fprintf(stderr, "WaitForInput(): select: errno=%d\n", selecterr); } else { MASKANDSETBITS(readyClients, LastSelectMask, AllClients); if (LastSelectMask[0] & WellKnownConnections) EstablishNewConnections(); } if (ANYSET(readyClients)) { for (i=0; i<mskcnt; i++) { while (readyClients[i]) { curclient = NextSetBit (readyClients[i]) - 1; ServiceClient(curclient); readyClients[i] &= ~(1 << curclient); } } } }