2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * (c) Copyright 1995 Digital Equipment Corporation.
25 * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
26 * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
27 * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
28 * (c) Copyright 1993, 1994, 1995 Novell, Inc.
29 * (c) Copyright 1995 FUJITSU LIMITED.
30 * (c) Copyright 1995 Hitachi.
32 /******************************************************************************
35 * RCS: $TOG: Main.c /main/9 1999/09/20 15:36:11 mgreess $
36 * Package: dtexec for CDE 1.0
38 *****************************************************************************/
40 #define SPINBLOCK if (getenv("_DTEXEC_DEBUG")) {int i; i=1; while(i) i=1;}
43 * _DTEXEC_NLS16 controls whether I18N code should be used. As of 7/94,
44 * libDtSvc should be the only one using dtexec, and the command line
45 * and parsing code are I18N insensitive, and thats o.k.
47 * If turned on, some routines from DtSvc/DtUtil2/DtNlUtils.c will be
59 #include "osdep.h" /* select(2) mask width and bit manipulation macros */
63 #include <Dt/MsgLog.h>
66 * From Dt/ActionP.h - never change since these are in effect protocol
67 * codes! See Dt/ActionP.h for more details.
70 #define _DtActCHILD_UNKNOWN (1<<0) /* 1 - child status unknown */
71 #define _DtActCHILD_PENDING_START (1<<1) /* 2 - child start pending */
72 #define _DtActCHILD_ALIVE_UNKNOWN (1<<2) /* 4 - child alive but unknown*/
73 #define _DtActCHILD_ALIVE (1<<3) /* 8 - child alive and well */
74 #define _DtActCHILD_DONE (1<<4) /* 16 - child done */
75 #define _DtActCHILD_FAILED (1<<5) /* 32 - child failed */
76 #define _DtActCHILD_CANCELED (1<<6) /* 64 - child canceled */
77 #endif /* _ActionP_h */
79 #define MAX_EXEC_ARGS 1000 /* Maximum number of arguments for */
88 * Triggered by a SIGCLD, the shutdown sequence.
90 #define SDP_DONE_STARTING 1
91 #define SDP_DONE_REPLY_WAIT 2
92 #define SDP_DONE_REPLIED 3
93 #define SDP_DONE_PANIC_CLEANUP 4
94 #define SDP_FINAL_LINGER 5
97 * Timeout period in milliseconds for select() when we are rediscovering
98 * signals and engage in a shutdown process. More often than not, we
99 * will services signals out of select() immediately. The only interesting
100 * moment is waiting for a Done(Reply).
102 #define SHORT_SELECT_TIMEOUT 20
105 * External system calls:
107 extern pid_t fork ();
108 extern int execvp ();
109 extern pid_t wait ();
111 extern void _exit ();
112 extern unsigned int sleep ();
114 extern char *getenv();
119 void DoneRequest(int doneCode);
124 struct timeval startTimeG;
125 struct timezone zoneG;
127 pid_t childPidG; /* PID of child we fork/exec */
128 long waitTimeG; /* -open setting */
129 char *dtSvcProcIdG; /* TT Proc ID of our caller */
130 int dtSvcInvIdG; /* Invocation ID that our caller manages us by*/
131 int dtSvcChildIdG; /* Child ID that our caller manages us by */
132 int tmpFileCntG; /* tmp file count */
133 char **tmpFilesG; /* tmp files we might need to unlink */
134 fd_set allactivefdsG; /* all possible fildes of interest. */
135 /* - mskcnt generated from osdep.h */
136 int rediscoverSigCldG; /* if a SIGCLD goes off */
137 int rediscoverUrgentSigG; /* if a SIGTERM, SIGHUP or SIGQUIT goes off */
138 int shutdownPhaseG; /* shutdown progress state variable */
139 int ttfdG; /* tooltalk fildes */
140 int errorpipeG[2]; /* dtexec <--< child stderr pipe */
143 /******************************************************************************
145 * Minaturized versions of tttk_*() routines. Used so we don't have to
146 * pull in tttk which in turn would pull in Xt.
148 *****************************************************************************/
150 char *dtexec_Tttk_integer = "integer";
151 char *dtexec_Tttk_message_id = "messageID";
154 dtexec_tttk_message_create(
160 Tt_message_callback callback
168 msg = tt_message_create();
169 status = tt_ptr_error( msg );
170 if (status != TT_OK) {
174 status = tt_message_class_set( msg, theClass );
175 if (status != TT_OK) {
176 return (Tt_message)tt_error_pointer( status );
179 status = tt_message_scope_set( msg, theScope );
180 if (status != TT_OK) {
181 return (Tt_message)tt_error_pointer( status );
184 address = TT_PROCEDURE;
186 status = tt_message_handler_set( msg, handler );
187 if (status != TT_OK) {
188 return (Tt_message)tt_error_pointer( status );
190 address = TT_HANDLER;
193 status = tt_message_address_set( msg, address );
194 if (status != TT_OK) {
195 return (Tt_message)tt_error_pointer( status );
199 status = tt_message_op_set( msg, op );
200 if (status != TT_OK) {
201 return (Tt_message)tt_error_pointer( status );
206 status = tt_message_callback_add( msg, callback );
207 if (status != TT_OK) {
208 return (Tt_message)tt_error_pointer( status );
216 dtexec_tttk_message_destroy(
222 status = tt_message_destroy(msg);
227 dtexec_tttk_message_am_handling(
234 if (tt_message_class( msg ) != TT_REQUEST) {
237 if (tt_message_state( msg ) != TT_SENT) {
240 handler = tt_message_handler( msg );
242 if ((tt_ptr_error( handler ) == TT_OK) && (handler != 0)) {
250 dtexec_tttk_message_abandon(
257 if (dtexec_tttk_message_am_handling( msg )) {
259 if (tt_message_address( msg ) == TT_HANDLER) {
261 } else if (tt_message_status( msg ) == TT_WRN_START_MESSAGE) {
266 if (tt_message_class( msg ) == TT_REQUEST) {
267 tt_message_status_set(msg, TT_DESKTOP_ENOTSUP);
268 status = tt_message_fail( msg );
269 dtexec_tttk_message_destroy( msg );
272 status = dtexec_tttk_message_destroy( msg );
275 tt_message_status_set( msg, TT_DESKTOP_ENOTSUP );
276 status = tt_message_reject( msg );
277 dtexec_tttk_message_destroy( msg );
281 status = dtexec_tttk_message_destroy( msg );
288 /******************************************************************************
290 * Help - print the usage and exit.
292 *****************************************************************************/
298 (void) fprintf (stderr, "Usage:\n");
299 (void) fprintf (stderr, "\t%s [-options ...] cmd [cmd arg ...]\n", argv[0]);
300 (void) fprintf (stderr, "\n");
301 (void) fprintf (stderr, "where options include:\n");
302 (void) fprintf (stderr, "\t-open open-option\n");
303 (void) fprintf (stderr, "\t\t-1 (default) continue to execute after cmd terminates,\n");
304 (void) fprintf (stderr, "\t\t thus keeping the terminal window open.\n");
305 (void) fprintf (stderr, "\t\t 0 exit as soon as cmd terminates, thus allowing\n");
306 (void) fprintf (stderr, "\t\t the terminal window to close.\n");
307 (void) fprintf (stderr, "\t\t n continue to execute if cmd terminates within n\n");
308 (void) fprintf (stderr, "\t\t seconds of starting.\n");
309 (void) fprintf (stderr, "\t-ttprocid procid\n");
310 (void) fprintf (stderr, "\t-tmp tmpfile [-tmp tmpfile ...]\n");
311 (void) fprintf (stderr, "\n");
316 /******************************************************************************
318 * PanicSignal - see InitializeSignalHandling()
320 *****************************************************************************/
323 #if defined(__aix) || defined (__osf__)
327 #endif /* __aix || __osf__ */
332 * Crude, but let libDtSvc know we've been forced down.
333 * Atleast libDtSvc will get a hint and know to cleanup.
336 DoneRequest(_DtActCHILD_FAILED);
340 * We cannot talk with caller, so do cleanup
343 for (i = 0; i < tmpFileCntG; i++ ) {
344 chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) );
345 unlink( tmpFilesG[i] );
353 /******************************************************************************
355 * IgnoreSignal - see InitializeSignalHandling()
357 *****************************************************************************/
360 #if defined(__aix) || defined (__osf__)
364 #endif /* __aix || __osf__ */
367 * If the child is still in the same process group, it should be
368 * getting the same signal too.
370 if (rediscoverSigCldG) {
371 if (shutdownPhaseG == SDP_FINAL_LINGER) {
373 * We were shutdown long ago and lingering, so go ahead
380 * Still shutting down, so flip requestTypeG so that dtexec
381 * will not linger when the shutdown process completes.
383 requestTypeG = TRANSIENT;
388 * If and when the child does repond to the signal we are
389 * ignoring for now, don't allow dtexec to linger.
391 requestTypeG = TRANSIENT;
395 /******************************************************************************
397 * UrgentSignal - see InitializeSignalHandling()
399 *****************************************************************************/
402 #if defined(__aix) || defined (__osf__)
406 #endif /* __aix || __osf__ */
409 * Set global so the central control point ( select() ) will
410 * rediscover the urgent signal.
412 rediscoverUrgentSigG = 1;
415 * If the child is still in the same process group, it should be
416 * getting the same signal too.
418 if (rediscoverSigCldG) {
419 if (shutdownPhaseG == SDP_FINAL_LINGER) {
421 * We were shutdown long ago and lingering, so go ahead
428 * Still shutting down, so flip requestTypeG so that dtexec
429 * will not linger when the shutdown process completes.
431 requestTypeG = TRANSIENT;
436 * If and when the child does repond to the signal we are
437 * ignoring for now, don't allow dtexec to linger.
439 * This is mildly different than the IgnoreSignal case because
440 * there is a timeout associated with UrgentSignals.
442 requestTypeG = TRANSIENT;
447 /******************************************************************************
449 * SigCld - see InitializeSignalHandling()
451 *****************************************************************************/
453 #if defined(__aix) || defined (__osf__)
457 #endif /* __aix || __osf__ */
463 * Query why the SIGCLD happened - a true termination or just a stopage.
464 * We only care about terminations.
466 pid = wait(&exitStatus);
469 if (errno == ECHILD) {
471 * No child found with wait(), so sure, act like we did
474 rediscoverSigCldG = 1;
477 else if (!WIFSTOPPED(exitStatus)) {
479 * The SIGCLD was *not* the result of being stopped, so the child
480 * has indeed terminated.
482 rediscoverSigCldG = 1;
487 /******************************************************************************
489 * InitializeSignalHandling - Set up the signal catchers.
491 * Keep this code in sync with fork/exec code so as to NOT pass
492 * these catchers onto our child.
495 * 1. if already SIGCLD
496 * A. if SDP_FINAL_LINGER, _exit(0)
497 * B. else set requestTypeG to TRANSIENT
498 * 2. else ignore signal
499 * 3. select-loop-spin
501 * comment: if fact we don't ignore the signal, but instead let the
502 * child control the pace of the response to the signal.
505 * 1. if already SIGCLD
506 * A. if SDP_FINAL_LINGER, _exit(0)
507 * B. else set requestTypeG to TRANSIENT
508 * 2. wait 5 seconds hoping to goto "SIGCLD" path from select-loop-spin
509 * 3. else do "Panic" path after 5 seconds
512 * 1. send Done(Request)
513 * 2. wait for Done(Reply) in select-loop-spin
514 * 3. send Quit(Reply)
519 * 1. send Done(Request)
520 * 2. send Quit(Reply)
525 * 1. default signal action
527 *****************************************************************************/
529 InitializeSignalHandling( void )
532 struct sigaction svec;
535 * "Graceful Signal" handlers
536 * - SIGCLD for normal child termination - best case.
538 sigemptyset(&svec.sa_mask);
540 svec.sa_handler = SigCld;
541 (void) sigaction(SIGCLD, &svec, (struct sigaction *) NULL);
544 * "Urgent Signal" handlers
545 * - SIGTERM for standard kill(1)
547 * We treat these signals in a special way. When the signal comes
548 * in, we hope to see the child respond with a SIGCLD within 5
549 * seconds, else we act like the SIGTERM was for us and go down.
551 sigemptyset(&svec.sa_mask);
553 svec.sa_handler = UrgentSignal;
554 (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL);
557 * "Panic Signal" handlers.
558 * - SIGINT for BBA coverage.
560 sigemptyset(&svec.sa_mask);
562 svec.sa_handler = PanicSignal;
563 (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL);
566 * "Ignore Signal" handlers.
567 * - SIGUSR1 - let child decide how to respond
568 * - SIGUSR2 - let child decide how to respond
569 * - SIGHUP - let child decide how to respond
571 sigemptyset(&svec.sa_mask);
573 svec.sa_handler = IgnoreSignal;
574 (void) sigaction(SIGUSR1, &svec, (struct sigaction *) NULL);
575 (void) sigaction(SIGUSR2, &svec, (struct sigaction *) NULL);
576 (void) sigaction(SIGHUP, &svec, (struct sigaction *) NULL);
579 * "Default Signal" handlers.
580 * - SIGQUIT - let core dump happen as expected. If done,
581 * libDtSvc will never get a done notice.
586 /******************************************************************************
588 * After all is said and done, enter FinalLinger which
589 * will decide if and how long the associated terminal
590 * emulator will stay up.
592 *****************************************************************************/
593 void FinalLinger(void)
596 struct timeval finalTime;
599 * Make sure to reap child. The SIGCLD handler may have done
602 (void) wait(&exitStatus);
604 if (requestTypeG == PERM_TERM)
605 (void) sleep (99999999);
606 else if (requestTypeG == TRANSIENT)
610 * Check the time stamps and either sleep forever or exit.
612 if ((gettimeofday (&finalTime, &zoneG)) == -1)
615 if ((finalTime.tv_sec - startTimeG.tv_sec) < waitTimeG)
616 (void) sleep (99999999);
623 /******************************************************************************
627 *****************************************************************************/
635 struct sigaction svec;
637 for (index1 = 0; (index1 < 10) && ((childPidG = fork()) < 0); index1++) {
638 /* Out of resources ? */
641 /* If not out of resources, sleep and try again */
642 (void) sleep ((unsigned long) 2);
649 if (childPidG == 0) {
655 * Hook stderr to error pipe back to parent.
657 if ((errorpipeG[0] != -1) && (errorpipeG[1] != -1)) {
658 dup2(errorpipeG[1], 2);
659 close(errorpipeG[0]);
663 * The child should have default behavior for all signals.
665 sigemptyset(&svec.sa_mask);
667 svec.sa_handler = SIG_DFL;
670 (void) sigaction(SIGCLD, &svec, (struct sigaction *) NULL);
673 (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL);
676 (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL);
679 (void) sigaction(SIGUSR1, &svec, (struct sigaction *) NULL);
680 (void) sigaction(SIGUSR2, &svec, (struct sigaction *) NULL);
681 (void) sigaction(SIGHUP, &svec, (struct sigaction *) NULL);
683 for (i=3; i < FOPEN_MAX; i++) {
684 if ( i != errorpipeG[1] )
685 (void) fcntl (i, F_SETFD, 1);
688 (void) execvp(commandArray[0], commandArray);
690 (void) fprintf (stderr, "Cannot execute \"%s\".\n", commandArray[0]);
695 if (errorpipeG[1] != -1) {
696 close(errorpipeG[1]);
703 /******************************************************************************
707 * Peel off options to dtexec. As soon as an argv[n] is not a valid
708 * dtexec option, assume it is the start of the cmd line and return.
710 *****************************************************************************/
721 #endif /* _DTEXEC_NLS16 */
723 argv2 = (char **) argv;
726 tmpFilesG = (char **) NULL;
728 for (argc--, argv++; argc > 0; argc--, argv++) {
729 if ( ! strcmp(argv[0] , "-open") ) {
732 if ( argc == 0 ) Help( argv2 );
733 waitTimeG = atoi (argv[0]);
736 requestTypeG = PERM_TERM;
737 else if (waitTimeG == 0)
738 requestTypeG = TRANSIENT;
740 requestTypeG = SHORT;
743 else if ( ! strcmp(argv[0] , "-ttprocid") ) {
745 if ( argc == 0 ) Help( argv2 );
748 * Pull the -ttprocid argument apart for it's 3 components.
749 * <libDtSvc's ProcId>_<Invocation Id>_<Child Id>
752 tmpi = mblen(argv[0]);
753 dtSvcProcIdG = (char *) malloc( tmpi + 1 );
755 memcpy( dtSvcProcIdG, argv[0], tmpi );
756 dtSvcProcIdG[tmpi] = NULL;
758 dtSvcProcIdG = (char *) malloc( strlen(argv[0]) + 1 );
759 strcpy( dtSvcProcIdG, argv[0] );
760 #endif /* _DTEXEC_NLS16 */
763 * Search from the end for underscore seperators.
766 tick2 = (char *) Dt_strrchr( dtSvcProcIdG, '_' );
768 tick2 = (char *) strrchr( dtSvcProcIdG, '_' );
769 #endif /* _DTEXEC_NLS16 */
775 tick1 = (char *) Dt_strrchr( dtSvcProcIdG, '_' );
777 tick1 = (char *) strrchr( dtSvcProcIdG, '_' );
778 #endif /* _DTEXEC_NLS16 */
780 if ( tick1 && tick2 ) {
784 dtSvcInvIdG = atoi((char *) (tick1 + 1));
785 dtSvcChildIdG = atoi((char *) (tick2 + 1));
787 if ( !(dtSvcInvIdG && dtSvcChildIdG) ) {
789 * Don't have two non-zero values, so we cannot use the
790 * -ttprocid provided.
793 dtSvcProcIdG = (char *) NULL;
798 * Unable to find _ (underscore) seperators.
801 dtSvcProcIdG = (char *) NULL;
804 else if ( ! strcmp(argv[0] , "-tmp") ) {
806 if ( argc == 0 ) Help( argv2 );
808 tmpFilesG = (char **) realloc( (char *) tmpFilesG,
809 tmpFileCntG * sizeof(char *) );
810 tmpFilesG[tmpFileCntG-1] = argv[0];
812 else if ( ! strncmp(argv[0], "-h", 2) ) {
815 * Technically we should see if a -ttprocid was given and
816 * possibly send back a Done(Request).
825 * No arguments to dtexec, so nothing to fork/exec.
827 return( (char **) NULL );
830 /******************************************************************************
832 * Shutdown Tooltalk connection.
834 *****************************************************************************/
835 void DetachFromTooltalk(
836 unsigned long *nocare1) /* if Xt - XtInputId *id; */
842 * NULL the global to indicate that we no longer want to
843 * chit-chat with Tooltalk.
845 dtSvcProcIdG = (char *) NULL;
847 sessid = tt_default_session();
848 tt_session_quit(sessid);
854 * Unregister the Tooltalk fildes from the select mask.
857 BITCLEAR(allactivefdsG, ttfdG);
862 /******************************************************************************
864 * Alternate input handler to tttk_Xt_input_handler
866 * If we end up pulling Xt in, toss this routine and use
867 * tttk_Xt_input_handler instead.
869 *****************************************************************************/
872 char *nocare1, /* if Xt - XtPointer w */
873 int *nocare2, /* if Xt - int *source */
874 unsigned long *nocare3) /* if Xt - XtInputId *id; */
880 msg = tt_message_receive();
881 status = tt_ptr_error( msg );
883 if (status != TT_OK) {
885 * Problem; think about bailing.
887 if (status == TT_ERR_NOMP) {
891 DetachFromTooltalk(NULL);
898 * A pattern callback ate the message for us.
904 * All messages should have been consumed by a callback.
905 * Pick between failing and rejecting the msg.
907 status = dtexec_tttk_message_abandon( msg );
908 if (status != TT_OK) {
914 /******************************************************************************
916 * Initiallize Tooltalk world.
918 *****************************************************************************/
920 ToolTalkError(char *errfmt, Tt_status status)
924 if (! tt_is_err(status)) return;
926 statmsg = tt_status_message(status);
927 DtMsgLogMessage( "Dtexec", DtMsgLogStderr, errfmt, statmsg );
930 int InitializeTooltalk(void)
936 procid = tt_default_procid();
937 status = tt_ptr_error(procid);
938 if ((status == TT_ERR_NOMP) || (status == TT_ERR_PROCID)) {
940 * We need to try to establish a connection
943 status = tt_ptr_error(procid);
944 if (status != TT_OK) {
945 ToolTalkError("Could not connect to ToolTalk:\n%s\n", status);
951 * Determine the Tooltalk fildes.
954 status = tt_int_error(fd);
955 if (status != TT_OK) {
956 ToolTalkError("Could not connect to ToolTalk:\n%s\n", status);
965 #ifdef DtActUseXtOverSelect
967 * Add the ToolTalk file descriptor to the set monitored by Xt
969 XtAddInput(fd, (XtPointer)XtInputReadMask, input_handler, 0);
970 #endif /* DtActUseXtOverSelect */
976 /******************************************************************************
978 * Send some identification back to libDtSvc so it can talk back to
979 * dtexec. The request format is:
981 * op(_DtActDtexecID) - the pattern
982 * iarg(invID) - matches libDtSvc's invocation ID
983 * iarg(childID) - matches libDtSvc's child ID
984 * arg(dtexec's ProcID) - dtexec's procid handle
986 * libDtSvc should be able to pluck the invID and childID to immediately
987 * dereference into it's Child-Invocation-Record that is tracking this
988 * dtexec invocation. It just slips in "dtexec's ProcID" and then full
989 * two-way communication is established.
991 *****************************************************************************/
992 /*************************************************
994 * Routine to catch identification reply.
996 Tt_callback_action IdSelfToCallerReplyCB(
1005 status = tt_message_status(msg);
1006 state = tt_message_state(msg);
1008 if (state == TT_FAILED) {
1010 * tjg: At some point, may want to dump the following error
1011 * message into a log file. May have to wrap long messages.
1013 if (status < TT_ERR_LAST)
1014 errorMsg = tt_status_message(status);
1016 errorMsg = tt_message_status_string(msg);
1018 dtexec_tttk_message_destroy(msg);
1019 DetachFromTooltalk(NULL);
1021 else if (state == TT_HANDLED) {
1022 dtexec_tttk_message_destroy(msg);
1024 * Nothing substantial to do with the request-reply in the current
1031 return( (Tt_callback_action) TT_CALLBACK_PROCESSED );
1034 /*************************************************
1036 * Routine to send identification request.
1038 void IdSelfToCallerRequest(void)
1044 procid = tt_default_procid();
1046 msg = dtexec_tttk_message_create( (Tt_message) NULL, TT_REQUEST, TT_SESSION,
1049 IdSelfToCallerReplyCB );
1050 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcInvIdG );
1051 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcChildIdG );
1052 tt_message_arg_add( msg, TT_IN, dtexec_Tttk_message_id, procid );
1054 status = tt_message_send( msg );
1058 if (status != TT_OK) {
1059 dtexec_tttk_message_destroy( msg );
1060 DetachFromTooltalk(NULL);
1064 /******************************************************************************
1066 * Send a Done notice back to libDtSvc.
1069 * iarg(invID) - matches libDtSvc's invocation ID
1070 * iarg(childID) - matches libDtSvc's child ID
1071 * iarg(DtActionStatus) - a DtActionStatus style code
1073 *****************************************************************************/
1074 /*************************************************
1076 * Routine to catch identification reply.
1078 Tt_callback_action DoneRequestReplyCB(
1083 Tt_status replyStatus;
1086 state = tt_message_state(msg);
1088 if (state == TT_FAILED) {
1089 dtexec_tttk_message_destroy(msg);
1090 DetachFromTooltalk(NULL);
1092 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1094 else if (state == TT_HANDLED) {
1095 dtexec_tttk_message_destroy(msg);
1097 shutdownPhaseG = SDP_DONE_REPLIED;
1102 return( (Tt_callback_action) TT_CALLBACK_PROCESSED );
1105 /*************************************************
1107 * Routine to send done request.
1109 void DoneRequest(int doneCode)
1112 static int beenhere = 0;
1118 * Only allow one Done(Request) to be issued.
1123 procid = tt_default_procid();
1125 msg = dtexec_tttk_message_create( (Tt_message) NULL,
1126 TT_REQUEST, TT_SESSION,
1129 DoneRequestReplyCB );
1130 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcInvIdG );
1131 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcChildIdG );
1132 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, doneCode );
1134 status = tt_message_send( msg );
1138 if (status != TT_OK) {
1139 dtexec_tttk_message_destroy( msg );
1140 DetachFromTooltalk(NULL);
1145 /******************************************************************************
1149 *****************************************************************************/
1157 fd_set readfds, exceptfds;
1159 struct timeval timeoutShort, timeoutLong;
1163 struct stat statBuffer;
1164 int firstPass, tmpi;
1165 char *tmpProgName = NULL;
1168 setlocale( LC_ALL, "" );
1170 #ifdef _DTEXEC_NLS16
1172 #endif /* _DTEXEC_NLS16 */
1175 * For debugging purposes, a way to pause the process and allow
1176 * time for a xdb -P debugger attach. If no args, (e.g. libDtSvc is
1177 * test running the executable), cruise on.
1179 if (getenv("_DTEXEC_DEBUG") && (argc > 1)) {
1181 * Don't block in a system call, or on libDtSvc's attempts to
1182 * just test exec us.
1188 * Note: dtSvcProcIdG is used like a boolean to control whether
1189 * we are communicating with libDtSvc using Tooltalk.
1191 dtSvcProcIdG = (char *) NULL; /* assume not communicating with TT */
1194 cmdLine = ParseCommandLine (argc, argv);
1197 * If a signal goes off *outside* the upcoming select, we'll need to
1198 * rediscover the signal by letting select() timeout.
1200 * We might also set a rediscover flag to fake a signal response.
1202 rediscoverSigCldG = 0; /* boolean and counter */
1203 rediscoverUrgentSigG = 0; /* boolean and counter */
1205 InitializeSignalHandling ();
1208 * Create a pipe for logging of errors for actions without
1211 errorpipeG[0] = -1; /* by default, no stderr redirection */
1213 if ( requestTypeG == TRANSIENT ) { /* should be WINDOW_TYPE NO_STDIO */
1214 if ( pipe(errorpipeG) == -1 ) {
1221 success = ExecuteCommand (cmdLine);
1224 * Act like we were killed - it will result in a
1228 rediscoverUrgentSigG = 1;
1233 * Act like we had a child and it went away - it will result
1234 * in a DtACTION_DONE.
1237 rediscoverSigCldG = 1;
1241 * Note when we started so we can compare times when we finish.
1243 (void) gettimeofday (&startTimeG, &zoneG);
1246 if ( !InitializeTooltalk() ) {
1248 * We have no hope of talking to our caller via Tooltalk.
1250 dtSvcProcIdG = (char *) NULL;
1255 * Tie in to the default session and start chatting.
1257 if (dtSvcProcIdG) tt_session_join(tt_default_session());
1260 * Finally send caller our current proc id so they can talk back.
1262 if (dtSvcProcIdG) IdSelfToCallerRequest();
1265 * Monitor file descriptors for activity. If errors occur on a fds,
1266 * it will be removed from allactivefdsG after handling the error.
1268 CLEARBITS(allactivefdsG);
1274 BITSET(allactivefdsG, ttfdG); /* add Tooltalk */
1279 if ( errorpipeG[0] != -1 )
1280 BITSET(allactivefdsG, errorpipeG[0]); /* add read side of error pipe */
1283 * Set options for rediscovery and not-rediscovery modes of
1286 shutdownPhaseG = SDP_DONE_STARTING; /* triggered with rediscoverSigCldG */
1287 timeoutShort.tv_sec = 0; /* in quick rediscovery mode */
1288 timeoutShort.tv_usec = SHORT_SELECT_TIMEOUT;
1289 timeoutLong.tv_sec = 86400; /* don't thrash on rediscovery */
1290 timeoutLong.tv_usec = 0;
1293 COPYBITS(allactivefdsG, readfds);
1294 COPYBITS(allactivefdsG, exceptfds);
1296 if (rediscoverSigCldG || rediscoverUrgentSigG) {
1297 nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1298 FD_SET_CAST(&exceptfds), &timeoutShort);
1301 nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1302 FD_SET_CAST(&exceptfds), &timeoutLong);
1307 * Handle select() problem.
1309 if (errno == EINTR) {
1311 * A signal happened - let rediscover flags redirect flow
1312 * via short select timeouts.
1315 else if ((errno == EBADF) || (errno == EFAULT)) {
1317 * A connection probably dropped.
1320 if ( GETBIT(exceptfds, ttfdG) ) {
1322 * Tooltalk connection has gone bad.
1324 * Judgement call - when the Tooltalk connection goes
1325 * bad, let dtexec continue rather than doing an exit.
1327 DetachFromTooltalk(NULL);
1331 if (errorpipeG[0] != -1) {
1332 if ( GETBIT(exceptfds, errorpipeG[0]) ) {
1334 * Error pipe has gone bad.
1336 close(errorpipeG[0]);
1337 BITCLEAR(allactivefdsG, errorpipeG[0]);
1344 * We have bad paremeters to select()
1348 * So that select() errors cannot dominate, now behave as
1349 * though only a timeout had occured.
1356 * Have some input to process. Figure out who.
1359 if ( GETBIT(readfds, ttfdG) ) {
1360 /* Clear bit first, since calling input_handler() could */
1361 /* have the side-effect of setting ttfdG to -1! */
1362 BITCLEAR(readfds, ttfdG);
1365 * Tooltalk activity.
1367 * Note that the input_handler parameters match
1368 * an XtInputHandler() style callback in case Xt is
1371 input_handler((char *) NULL, (int *) &junki,
1372 (unsigned long *) &junki);
1376 if (errorpipeG[0] != -1) {
1377 if ( GETBIT(readfds, errorpipeG[0]) ) {
1381 * Read the errorpipe until no more seems available.
1382 * Call that good enough and write a time-stamped
1383 * block to the errorLog file.
1385 errorBytes = 0; /* what we have so far */
1390 if ( fstat(errorpipeG[0], &statBuffer) ) {
1393 else if ( statBuffer.st_size > 0 ) {
1395 * Grow buffer to hold entire error stream.
1398 if (tmpBuffer == NULL)
1399 tmpBuffer = (char *) malloc(
1400 statBuffer.st_size + 1);
1402 tmpBuffer = (char *) realloc( tmpBuffer,
1404 statBuffer.st_size + 1);
1408 tmpi = read (errorpipeG[0], &tmpBuffer[errorBytes],
1409 statBuffer.st_size);
1410 if (tmpi > 0) /* else let fstat() redetect problem */
1412 tmpBuffer[errorBytes] = '\0';
1414 if (errorBytes < 65535) {
1416 * Pause a bit and wait for a continuation of
1417 * the error stream if there is more.
1419 select(0, FD_SET_CAST(NULL),
1421 FD_SET_CAST(NULL), &timeoutShort);
1425 * We have enough to do a dump now.
1436 * On the first pass after select(), if we have 0 bytes,
1437 * it really means the pipe has gone down.
1439 close(errorpipeG[0]);
1440 BITCLEAR(allactivefdsG, errorpipeG[0]);
1441 BITCLEAR(readfds, errorpipeG[0]);
1451 tmpProgName = (char *) malloc (strlen (argv[0]) +
1452 strlen (cmdLine[0]) +
1455 tmpProgName = argv[0];
1458 * To identify the process for this stderr,
1459 * use both argv[0] and the name of the
1460 * process that was execvp'd
1462 (void) strcpy (tmpProgName, "(");
1463 (void) strcat (tmpProgName, argv[0]);
1464 (void) strcat (tmpProgName, ") ");
1465 (void) strcat (tmpProgName, cmdLine[0]);
1468 DtMsgLogMessage( tmpProgName, DtMsgLogStderr, "%s",
1473 if (errorpipeG[0] != -1)
1474 BITCLEAR(readfds, errorpipeG[0]);
1478 * So that select() data cannot dominate, now behave as
1479 * though only a timeout had occured.
1486 * Timeout. We are probably rediscovering and have entered
1487 * a shutdown phase. The following rediscover handlers are
1488 * in priority order.
1490 * Note that by way of timeouts and events, we will make
1491 * multiple passes through this block of code.
1494 if (rediscoverUrgentSigG) {
1496 * Handle urgent signal.
1498 * Tact: wait awhile and see if a SIGCLD will happen.
1499 * If it does, then a normal shutdown will suffice.
1500 * If a SIGCLD does not happen, then do a raw exit(0).
1501 * Exit is required for BBA anyway.
1504 if (rediscoverSigCldG)
1506 * Rather than act on the Urgent Signal, defer to the
1507 * SIGCLD Signal shutdown process.
1509 rediscoverUrgentSigG = 0;
1512 * Still in a mode where we have an outstanding
1513 * Urgent Signal but no SIGCLD. Bump a counter
1514 * which moves us closer to doing an exit().
1516 rediscoverUrgentSigG++;
1519 * After 5 seconds (add select timeout too) waiting for
1520 * a SIGCLD, give up and exit.
1522 if (rediscoverUrgentSigG > ((1000/SHORT_SELECT_TIMEOUT)*5) ) {
1523 #if defined(__aix) || defined (__osf__)
1531 if (rediscoverSigCldG) {
1533 * Handle SIGCLD signal.
1535 * Under SIGCLD, we will make multiple passes through the
1536 * following, implementing a phased shutdown.
1538 if (shutdownPhaseG == SDP_DONE_STARTING) {
1540 * Send Done(Request) for starters.
1542 if (dtSvcProcIdG) DoneRequest(_DtActCHILD_DONE);
1546 * Sit and wait for the Done Reply in select()
1548 shutdownPhaseG = SDP_DONE_REPLY_WAIT;
1552 * Unable to send Done Reply. Assume we're on
1553 * our own from now on.
1555 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1559 if (shutdownPhaseG == SDP_DONE_REPLY_WAIT) {
1561 * After 5 minutes of passing through REPLY_WAIT,
1562 * assume the Done(Reply) will never come in and
1565 rediscoverSigCldG++;
1566 if (rediscoverSigCldG > ((1000/SHORT_SELECT_TIMEOUT)*300)) {
1570 * Try to detatch from Tooltalk anyway.
1572 DetachFromTooltalk(NULL);
1575 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1579 * See if the Tooltalk connection is still alive. If
1580 * not, then no reason to wait around.
1582 else if (!dtSvcProcIdG) {
1583 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1588 if (shutdownPhaseG == SDP_DONE_REPLIED) {
1590 * We have our Done(Reply), so proceed.
1594 shutdownPhaseG = SDP_FINAL_LINGER;
1596 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1599 if (shutdownPhaseG == SDP_DONE_PANIC_CLEANUP) {
1601 * We cannot talk with caller, so do cleanup
1604 for (i = 0; i < tmpFileCntG; i++ ) {
1605 chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) );
1606 unlink( tmpFilesG[i] );
1609 shutdownPhaseG = SDP_FINAL_LINGER;
1612 if (shutdownPhaseG == SDP_FINAL_LINGER) {
1616 static int skipFirst = 1;
1620 * Rather than a quick departure from the select()
1621 * loop, make one more pass. If the child has gone
1622 * down quickly, the SIGCLD may have caused us to
1623 * get here before any errorPipeG information has
1624 * had a chance to reach us.