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 libraries 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 */
66 #include <Dt/MsgLog.h>
69 * From Dt/ActionP.h - never change since these are in effect protocol
70 * codes! See Dt/ActionP.h for more details.
73 #define _DtActCHILD_UNKNOWN (1<<0) /* 1 - child status unknown */
74 #define _DtActCHILD_PENDING_START (1<<1) /* 2 - child start pending */
75 #define _DtActCHILD_ALIVE_UNKNOWN (1<<2) /* 4 - child alive but unknown*/
76 #define _DtActCHILD_ALIVE (1<<3) /* 8 - child alive and well */
77 #define _DtActCHILD_DONE (1<<4) /* 16 - child done */
78 #define _DtActCHILD_FAILED (1<<5) /* 32 - child failed */
79 #define _DtActCHILD_CANCELED (1<<6) /* 64 - child canceled */
80 #endif /* _ActionP_h */
82 #define MAX_EXEC_ARGS 1000 /* Maximum number of arguments for */
91 * Triggered by a SIGCLD, the shutdown sequence.
93 #define SDP_DONE_STARTING 1
94 #define SDP_DONE_REPLY_WAIT 2
95 #define SDP_DONE_REPLIED 3
96 #define SDP_DONE_PANIC_CLEANUP 4
97 #define SDP_FINAL_LINGER 5
100 * Timeout period in milliseconds for select() when we are rediscovering
101 * signals and engage in a shutdown process. More often than not, we
102 * will services signals out of select() immediately. The only interesting
103 * moment is waiting for a Done(Reply).
105 #define SHORT_SELECT_TIMEOUT 20
108 * External system calls:
110 extern pid_t fork ();
111 extern int execvp ();
112 extern pid_t wait ();
114 extern void _exit ();
115 extern unsigned int sleep ();
117 extern char *getenv();
122 void DoneRequest(int doneCode);
127 struct timeval startTimeG;
128 struct timezone zoneG;
130 pid_t childPidG; /* PID of child we fork/exec */
131 long waitTimeG; /* -open setting */
132 char *dtSvcProcIdG; /* TT Proc ID of our caller */
133 int dtSvcInvIdG; /* Invocation ID that our caller manages us by*/
134 int dtSvcChildIdG; /* Child ID that our caller manages us by */
135 int tmpFileCntG; /* tmp file count */
136 char **tmpFilesG; /* tmp files we might need to unlink */
137 fd_set allactivefdsG; /* all possible fildes of interest. */
138 /* - mskcnt generated from osdep.h */
139 int rediscoverSigCldG; /* if a SIGCLD goes off */
140 int rediscoverUrgentSigG; /* if a SIGTERM, SIGHUP or SIGQUIT goes off */
141 int shutdownPhaseG; /* shutdown progress state variable */
142 int ttfdG; /* tooltalk fildes */
143 int errorpipeG[2]; /* dtexec <--< child stderr pipe */
146 /******************************************************************************
148 * Minaturized versions of tttk_*() routines. Used so we don't have to
149 * pull in tttk which in turn would pull in Xt.
151 *****************************************************************************/
153 char *dtexec_Tttk_integer = "integer";
154 char *dtexec_Tttk_message_id = "messageID";
157 dtexec_tttk_message_create(
163 Tt_message_callback callback
171 msg = tt_message_create();
172 status = tt_ptr_error( msg );
173 if (status != TT_OK) {
177 status = tt_message_class_set( msg, theClass );
178 if (status != TT_OK) {
179 return (Tt_message)tt_error_pointer( status );
182 status = tt_message_scope_set( msg, theScope );
183 if (status != TT_OK) {
184 return (Tt_message)tt_error_pointer( status );
187 address = TT_PROCEDURE;
189 status = tt_message_handler_set( msg, handler );
190 if (status != TT_OK) {
191 return (Tt_message)tt_error_pointer( status );
193 address = TT_HANDLER;
196 status = tt_message_address_set( msg, address );
197 if (status != TT_OK) {
198 return (Tt_message)tt_error_pointer( status );
202 status = tt_message_op_set( msg, op );
203 if (status != TT_OK) {
204 return (Tt_message)tt_error_pointer( status );
209 status = tt_message_callback_add( msg, callback );
210 if (status != TT_OK) {
211 return (Tt_message)tt_error_pointer( status );
219 dtexec_tttk_message_destroy(
225 status = tt_message_destroy(msg);
230 dtexec_tttk_message_am_handling(
237 if (tt_message_class( msg ) != TT_REQUEST) {
240 if (tt_message_state( msg ) != TT_SENT) {
243 handler = tt_message_handler( msg );
245 if ((tt_ptr_error( handler ) == TT_OK) && (handler != 0)) {
253 dtexec_tttk_message_abandon(
260 if (dtexec_tttk_message_am_handling( msg )) {
262 if (tt_message_address( msg ) == TT_HANDLER) {
264 } else if (tt_message_status( msg ) == TT_WRN_START_MESSAGE) {
269 if (tt_message_class( msg ) == TT_REQUEST) {
270 tt_message_status_set(msg, TT_DESKTOP_ENOTSUP);
271 status = tt_message_fail( msg );
272 dtexec_tttk_message_destroy( msg );
275 status = dtexec_tttk_message_destroy( msg );
278 tt_message_status_set( msg, TT_DESKTOP_ENOTSUP );
279 status = tt_message_reject( msg );
280 dtexec_tttk_message_destroy( msg );
284 status = dtexec_tttk_message_destroy( msg );
291 /******************************************************************************
293 * Help - print the usage and exit.
295 *****************************************************************************/
301 (void) fprintf (stderr, "Usage:\n");
302 (void) fprintf (stderr, "\t%s [-options ...] cmd [cmd arg ...]\n", argv[0]);
303 (void) fprintf (stderr, "\n");
304 (void) fprintf (stderr, "where options include:\n");
305 (void) fprintf (stderr, "\t-open open-option\n");
306 (void) fprintf (stderr, "\t\t-1 (default) continue to execute after cmd terminates,\n");
307 (void) fprintf (stderr, "\t\t thus keeping the terminal window open.\n");
308 (void) fprintf (stderr, "\t\t 0 exit as soon as cmd terminates, thus allowing\n");
309 (void) fprintf (stderr, "\t\t the terminal window to close.\n");
310 (void) fprintf (stderr, "\t\t n continue to execute if cmd terminates within n\n");
311 (void) fprintf (stderr, "\t\t seconds of starting.\n");
312 (void) fprintf (stderr, "\t-ttprocid procid\n");
313 (void) fprintf (stderr, "\t-tmp tmpfile [-tmp tmpfile ...]\n");
314 (void) fprintf (stderr, "\n");
319 /******************************************************************************
321 * PanicSignal - see InitializeSignalHandling()
323 *****************************************************************************/
326 #if defined(__aix) || defined(CSRG_BASED) || defined(linux)
335 * Crude, but let libDtSvc know we've been forced down.
336 * Atleast libDtSvc will get a hint and know to cleanup.
339 DoneRequest(_DtActCHILD_FAILED);
343 * We cannot talk with caller, so do cleanup
346 for (i = 0; i < tmpFileCntG; i++ ) {
347 chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) );
348 unlink( tmpFilesG[i] );
356 /******************************************************************************
358 * IgnoreSignal - see InitializeSignalHandling()
360 *****************************************************************************/
363 #if defined(__aix) || defined(CSRG_BASED) || defined(linux)
370 * If the child is still in the same process group, it should be
371 * getting the same signal too.
373 if (rediscoverSigCldG) {
374 if (shutdownPhaseG == SDP_FINAL_LINGER) {
376 * We were shutdown long ago and lingering, so go ahead
383 * Still shutting down, so flip requestTypeG so that dtexec
384 * will not linger when the shutdown process completes.
386 requestTypeG = TRANSIENT;
391 * If and when the child does repond to the signal we are
392 * ignoring for now, don't allow dtexec to linger.
394 requestTypeG = TRANSIENT;
398 /******************************************************************************
400 * UrgentSignal - see InitializeSignalHandling()
402 *****************************************************************************/
405 #if defined(__aix) || defined(CSRG_BASED) || defined(linux)
412 * Set global so the central control point ( select() ) will
413 * rediscover the urgent signal.
415 rediscoverUrgentSigG = 1;
418 * If the child is still in the same process group, it should be
419 * getting the same signal too.
421 if (rediscoverSigCldG) {
422 if (shutdownPhaseG == SDP_FINAL_LINGER) {
424 * We were shutdown long ago and lingering, so go ahead
431 * Still shutting down, so flip requestTypeG so that dtexec
432 * will not linger when the shutdown process completes.
434 requestTypeG = TRANSIENT;
439 * If and when the child does repond to the signal we are
440 * ignoring for now, don't allow dtexec to linger.
442 * This is mildly different than the IgnoreSignal case because
443 * there is a timeout associated with UrgentSignals.
445 requestTypeG = TRANSIENT;
450 /******************************************************************************
452 * SigCld - see InitializeSignalHandling()
454 *****************************************************************************/
456 #if defined(__aix) || defined(CSRG_BASED) || defined(linux)
466 * Query why the SIGCLD happened - a true termination or just a stopage.
467 * We only care about terminations.
469 pid = wait(&exitStatus);
472 if (errno == ECHILD) {
474 * No child found with wait(), so sure, act like we did
477 rediscoverSigCldG = 1;
480 else if (!WIFSTOPPED(exitStatus)) {
482 * The SIGCLD was *not* the result of being stopped, so the child
483 * has indeed terminated.
485 rediscoverSigCldG = 1;
490 /******************************************************************************
492 * InitializeSignalHandling - Set up the signal catchers.
494 * Keep this code in sync with fork/exec code so as to NOT pass
495 * these catchers onto our child.
498 * 1. if already SIGCLD
499 * A. if SDP_FINAL_LINGER, _exit(0)
500 * B. else set requestTypeG to TRANSIENT
501 * 2. else ignore signal
502 * 3. select-loop-spin
504 * comment: if fact we don't ignore the signal, but instead let the
505 * child control the pace of the response to the signal.
508 * 1. if already SIGCLD
509 * A. if SDP_FINAL_LINGER, _exit(0)
510 * B. else set requestTypeG to TRANSIENT
511 * 2. wait 5 seconds hoping to goto "SIGCLD" path from select-loop-spin
512 * 3. else do "Panic" path after 5 seconds
515 * 1. send Done(Request)
516 * 2. wait for Done(Reply) in select-loop-spin
517 * 3. send Quit(Reply)
522 * 1. send Done(Request)
523 * 2. send Quit(Reply)
528 * 1. default signal action
530 *****************************************************************************/
532 InitializeSignalHandling( void )
535 struct sigaction svec;
538 * "Graceful Signal" handlers
539 * - SIGCLD for normal child termination - best case.
541 sigemptyset(&svec.sa_mask);
543 svec.sa_handler = SigCld;
544 (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL);
547 * "Urgent Signal" handlers
548 * - SIGTERM for standard kill(1)
550 * We treat these signals in a special way. When the signal comes
551 * in, we hope to see the child respond with a SIGCLD within 5
552 * seconds, else we act like the SIGTERM was for us and go down.
554 sigemptyset(&svec.sa_mask);
556 svec.sa_handler = UrgentSignal;
557 (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL);
560 * "Panic Signal" handlers.
561 * - SIGINT for BBA coverage.
563 sigemptyset(&svec.sa_mask);
565 svec.sa_handler = PanicSignal;
566 (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL);
569 * "Ignore Signal" handlers.
570 * - SIGUSR1 - let child decide how to respond
571 * - SIGUSR2 - let child decide how to respond
572 * - SIGHUP - let child decide how to respond
574 sigemptyset(&svec.sa_mask);
576 svec.sa_handler = IgnoreSignal;
577 (void) sigaction(SIGUSR1, &svec, (struct sigaction *) NULL);
578 (void) sigaction(SIGUSR2, &svec, (struct sigaction *) NULL);
579 (void) sigaction(SIGHUP, &svec, (struct sigaction *) NULL);
582 * "Default Signal" handlers.
583 * - SIGQUIT - let core dump happen as expected. If done,
584 * libDtSvc will never get a done notice.
589 /******************************************************************************
591 * After all is said and done, enter FinalLinger which
592 * will decide if and how long the associated terminal
593 * emulator will stay up.
595 *****************************************************************************/
596 void FinalLinger(void)
599 struct timeval finalTime;
602 * Make sure to reap child. The SIGCLD handler may have done
605 (void) wait(&exitStatus);
607 if (requestTypeG == PERM_TERM)
608 (void) sleep (99999999);
609 else if (requestTypeG == TRANSIENT)
613 * Check the time stamps and either sleep forever or exit.
615 if ((gettimeofday (&finalTime, &zoneG)) == -1)
618 if ((finalTime.tv_sec - startTimeG.tv_sec) < waitTimeG)
619 (void) sleep (99999999);
626 /******************************************************************************
630 *****************************************************************************/
638 struct sigaction svec;
640 for (index1 = 0; (index1 < 10) && ((childPidG = fork()) < 0); index1++) {
641 /* Out of resources ? */
644 /* If not out of resources, sleep and try again */
645 (void) sleep ((unsigned long) 2);
652 if (childPidG == 0) {
658 * Hook stderr to error pipe back to parent.
660 if ((errorpipeG[0] != -1) && (errorpipeG[1] != -1)) {
661 dup2(errorpipeG[1], 2);
662 close(errorpipeG[0]);
666 * The child should have default behavior for all signals.
668 sigemptyset(&svec.sa_mask);
670 svec.sa_handler = SIG_DFL;
673 (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL);
676 (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL);
679 (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL);
682 (void) sigaction(SIGUSR1, &svec, (struct sigaction *) NULL);
683 (void) sigaction(SIGUSR2, &svec, (struct sigaction *) NULL);
684 (void) sigaction(SIGHUP, &svec, (struct sigaction *) NULL);
686 for (i=3; i < FOPEN_MAX; i++) {
687 if ( i != errorpipeG[1] )
688 (void) fcntl (i, F_SETFD, FD_CLOEXEC);
691 (void) execvp(commandArray[0], commandArray);
693 (void) fprintf (stderr, "Cannot execute \"%s\".\n", commandArray[0]);
698 if (errorpipeG[1] != -1) {
699 close(errorpipeG[1]);
706 /******************************************************************************
710 * Peel off options to dtexec. As soon as an argv[n] is not a valid
711 * dtexec option, assume it is the start of the cmd line and return.
713 *****************************************************************************/
724 #endif /* _DTEXEC_NLS16 */
726 argv2 = (char **) argv;
729 tmpFilesG = (char **) NULL;
731 for (argc--, argv++; argc > 0; argc--, argv++) {
732 if ( ! strcmp(argv[0] , "-open") ) {
735 if ( argc == 0 ) Help( argv2 );
736 waitTimeG = atoi (argv[0]);
739 requestTypeG = PERM_TERM;
740 else if (waitTimeG == 0)
741 requestTypeG = TRANSIENT;
743 requestTypeG = SHORT;
746 else if ( ! strcmp(argv[0] , "-ttprocid") ) {
748 if ( argc == 0 ) Help( argv2 );
751 * Pull the -ttprocid argument apart for it's 3 components.
752 * <libDtSvc's ProcId>_<Invocation Id>_<Child Id>
755 tmpi = mblen(argv[0]);
756 dtSvcProcIdG = (char *) malloc( tmpi + 1 );
758 memcpy( dtSvcProcIdG, argv[0], tmpi );
759 dtSvcProcIdG[tmpi] = NULL;
761 dtSvcProcIdG = (char *) malloc( strlen(argv[0]) + 1 );
762 strcpy( dtSvcProcIdG, argv[0] );
763 #endif /* _DTEXEC_NLS16 */
766 * Search from the end for underscore seperators.
769 tick2 = (char *) Dt_strrchr( dtSvcProcIdG, '_' );
771 tick2 = (char *) strrchr( dtSvcProcIdG, '_' );
772 #endif /* _DTEXEC_NLS16 */
778 tick1 = (char *) Dt_strrchr( dtSvcProcIdG, '_' );
780 tick1 = (char *) strrchr( dtSvcProcIdG, '_' );
781 #endif /* _DTEXEC_NLS16 */
783 if ( tick1 && tick2 ) {
787 dtSvcInvIdG = atoi((char *) (tick1 + 1));
788 dtSvcChildIdG = atoi((char *) (tick2 + 1));
790 if ( !(dtSvcInvIdG && dtSvcChildIdG) ) {
792 * Don't have two non-zero values, so we cannot use the
793 * -ttprocid provided.
796 dtSvcProcIdG = (char *) NULL;
801 * Unable to find _ (underscore) seperators.
804 dtSvcProcIdG = (char *) NULL;
807 else if ( ! strcmp(argv[0] , "-tmp") ) {
809 if ( argc == 0 ) Help( argv2 );
811 tmpFilesG = (char **) realloc( (char *) tmpFilesG,
812 tmpFileCntG * sizeof(char *) );
813 tmpFilesG[tmpFileCntG-1] = argv[0];
815 else if ( ! strncmp(argv[0], "-h", 2) ) {
818 * Technically we should see if a -ttprocid was given and
819 * possibly send back a Done(Request).
828 * No arguments to dtexec, so nothing to fork/exec.
830 return( (char **) NULL );
833 /******************************************************************************
835 * Shutdown Tooltalk connection.
837 *****************************************************************************/
838 void DetachFromTooltalk(
839 unsigned long *nocare1) /* if Xt - XtInputId *id; */
845 * NULL the global to indicate that we no longer want to
846 * chit-chat with Tooltalk.
848 dtSvcProcIdG = (char *) NULL;
850 sessid = tt_default_session();
851 tt_session_quit(sessid);
857 * Unregister the Tooltalk fildes from the select mask.
860 BITCLEAR(allactivefdsG, ttfdG);
865 /******************************************************************************
867 * Alternate input handler to tttk_Xt_input_handler
869 * If we end up pulling Xt in, toss this routine and use
870 * tttk_Xt_input_handler instead.
872 *****************************************************************************/
875 char *nocare1, /* if Xt - XtPointer w */
876 int *nocare2, /* if Xt - int *source */
877 unsigned long *nocare3) /* if Xt - XtInputId *id; */
883 msg = tt_message_receive();
884 status = tt_ptr_error( msg );
886 if (status != TT_OK) {
888 * Problem; think about bailing.
890 if (status == TT_ERR_NOMP) {
894 DetachFromTooltalk(NULL);
901 * A pattern callback ate the message for us.
907 * All messages should have been consumed by a callback.
908 * Pick between failing and rejecting the msg.
910 status = dtexec_tttk_message_abandon( msg );
911 if (status != TT_OK) {
917 /******************************************************************************
919 * Initiallize Tooltalk world.
921 *****************************************************************************/
923 ToolTalkError(char *errfmt, Tt_status status)
927 if (! tt_is_err(status)) return;
929 statmsg = tt_status_message(status);
930 DtMsgLogMessage( "Dtexec", DtMsgLogStderr, errfmt, statmsg );
933 int InitializeTooltalk(void)
939 procid = tt_default_procid();
940 status = tt_ptr_error(procid);
941 if ((status == TT_ERR_NOMP) || (status == TT_ERR_PROCID)) {
943 * We need to try to establish a connection
946 status = tt_ptr_error(procid);
947 if (status != TT_OK) {
948 ToolTalkError("Could not connect to ToolTalk:\n%s\n", status);
954 * Determine the Tooltalk fildes.
957 status = tt_int_error(fd);
958 if (status != TT_OK) {
959 ToolTalkError("Could not connect to ToolTalk:\n%s\n", status);
968 #ifdef DtActUseXtOverSelect
970 * Add the ToolTalk file descriptor to the set monitored by Xt
972 XtAddInput(fd, (XtPointer)XtInputReadMask, input_handler, 0);
973 #endif /* DtActUseXtOverSelect */
979 /******************************************************************************
981 * Send some identification back to libDtSvc so it can talk back to
982 * dtexec. The request format is:
984 * op(_DtActDtexecID) - the pattern
985 * iarg(invID) - matches libDtSvc's invocation ID
986 * iarg(childID) - matches libDtSvc's child ID
987 * arg(dtexec's ProcID) - dtexec's procid handle
989 * libDtSvc should be able to pluck the invID and childID to immediately
990 * dereference into it's Child-Invocation-Record that is tracking this
991 * dtexec invocation. It just slips in "dtexec's ProcID" and then full
992 * two-way communication is established.
994 *****************************************************************************/
995 /*************************************************
997 * Routine to catch identification reply.
999 Tt_callback_action IdSelfToCallerReplyCB(
1008 status = tt_message_status(msg);
1009 state = tt_message_state(msg);
1011 if (state == TT_FAILED) {
1013 * tjg: At some point, may want to dump the following error
1014 * message into a log file. May have to wrap long messages.
1016 if (status < TT_ERR_LAST)
1017 errorMsg = tt_status_message(status);
1019 errorMsg = tt_message_status_string(msg);
1021 dtexec_tttk_message_destroy(msg);
1022 DetachFromTooltalk(NULL);
1024 else if (state == TT_HANDLED) {
1025 dtexec_tttk_message_destroy(msg);
1027 * Nothing substantial to do with the request-reply in the current
1034 return( (Tt_callback_action) TT_CALLBACK_PROCESSED );
1037 /*************************************************
1039 * Routine to send identification request.
1041 void IdSelfToCallerRequest(void)
1047 procid = tt_default_procid();
1049 msg = dtexec_tttk_message_create( (Tt_message) NULL, TT_REQUEST, TT_SESSION,
1052 IdSelfToCallerReplyCB );
1053 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcInvIdG );
1054 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcChildIdG );
1055 tt_message_arg_add( msg, TT_IN, dtexec_Tttk_message_id, procid );
1057 status = tt_message_send( msg );
1061 if (status != TT_OK) {
1062 dtexec_tttk_message_destroy( msg );
1063 DetachFromTooltalk(NULL);
1067 /******************************************************************************
1069 * Send a Done notice back to libDtSvc.
1072 * iarg(invID) - matches libDtSvc's invocation ID
1073 * iarg(childID) - matches libDtSvc's child ID
1074 * iarg(DtActionStatus) - a DtActionStatus style code
1076 *****************************************************************************/
1077 /*************************************************
1079 * Routine to catch identification reply.
1081 Tt_callback_action DoneRequestReplyCB(
1086 Tt_status replyStatus;
1089 state = tt_message_state(msg);
1091 if (state == TT_FAILED) {
1092 dtexec_tttk_message_destroy(msg);
1093 DetachFromTooltalk(NULL);
1095 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1097 else if (state == TT_HANDLED) {
1098 dtexec_tttk_message_destroy(msg);
1100 shutdownPhaseG = SDP_DONE_REPLIED;
1105 return( (Tt_callback_action) TT_CALLBACK_PROCESSED );
1108 /*************************************************
1110 * Routine to send done request.
1112 void DoneRequest(int doneCode)
1115 static int beenhere = 0;
1121 * Only allow one Done(Request) to be issued.
1126 procid = tt_default_procid();
1128 msg = dtexec_tttk_message_create( (Tt_message) NULL,
1129 TT_REQUEST, TT_SESSION,
1132 DoneRequestReplyCB );
1133 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcInvIdG );
1134 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, dtSvcChildIdG );
1135 tt_message_iarg_add( msg, TT_IN, dtexec_Tttk_integer, doneCode );
1137 status = tt_message_send( msg );
1141 if (status != TT_OK) {
1142 dtexec_tttk_message_destroy( msg );
1143 DetachFromTooltalk(NULL);
1148 /******************************************************************************
1152 *****************************************************************************/
1160 fd_set readfds, exceptfds;
1162 struct timeval timeoutShort, timeoutLong;
1166 int firstPass, tmpi;
1167 char *tmpProgName = NULL;
1170 setlocale( LC_ALL, "" );
1172 #ifdef _DTEXEC_NLS16
1174 #endif /* _DTEXEC_NLS16 */
1177 * For debugging purposes, a way to pause the process and allow
1178 * time for a xdb -P debugger attach. If no args, (e.g. libDtSvc is
1179 * test running the executable), cruise on.
1181 if (getenv("_DTEXEC_DEBUG") && (argc > 1)) {
1183 * Don't block in a system call, or on libDtSvc's attempts to
1184 * just test exec us.
1190 * Note: dtSvcProcIdG is used like a boolean to control whether
1191 * we are communicating with libDtSvc using Tooltalk.
1193 dtSvcProcIdG = (char *) NULL; /* assume not communicating with TT */
1196 cmdLine = ParseCommandLine (argc, argv);
1199 * If a signal goes off *outside* the upcoming select, we'll need to
1200 * rediscover the signal by letting select() timeout.
1202 * We might also set a rediscover flag to fake a signal response.
1204 rediscoverSigCldG = 0; /* boolean and counter */
1205 rediscoverUrgentSigG = 0; /* boolean and counter */
1207 InitializeSignalHandling ();
1210 * Create a pipe for logging of errors for actions without
1213 errorpipeG[0] = -1; /* by default, no stderr redirection */
1215 if ( requestTypeG == TRANSIENT ) { /* should be WINDOW_TYPE NO_STDIO */
1216 if ( pipe(errorpipeG) == -1 ) {
1223 success = ExecuteCommand (cmdLine);
1226 * Act like we were killed - it will result in a
1230 rediscoverUrgentSigG = 1;
1235 * Act like we had a child and it went away - it will result
1236 * in a DtACTION_DONE.
1239 rediscoverSigCldG = 1;
1243 * Note when we started so we can compare times when we finish.
1245 (void) gettimeofday (&startTimeG, &zoneG);
1248 if ( !InitializeTooltalk() ) {
1250 * We have no hope of talking to our caller via Tooltalk.
1252 dtSvcProcIdG = (char *) NULL;
1257 * Tie in to the default session and start chatting.
1259 if (dtSvcProcIdG) tt_session_join(tt_default_session());
1262 * Finally send caller our current proc id so they can talk back.
1264 if (dtSvcProcIdG) IdSelfToCallerRequest();
1267 * Monitor file descriptors for activity. If errors occur on a fds,
1268 * it will be removed from allactivefdsG after handling the error.
1270 CLEARBITS(allactivefdsG);
1276 BITSET(allactivefdsG, ttfdG); /* add Tooltalk */
1281 if ( errorpipeG[0] != -1 )
1282 BITSET(allactivefdsG, errorpipeG[0]); /* add read side of error pipe */
1285 * Set options for rediscovery and not-rediscovery modes of
1288 shutdownPhaseG = SDP_DONE_STARTING; /* triggered with rediscoverSigCldG */
1289 timeoutShort.tv_sec = 0; /* in quick rediscovery mode */
1290 timeoutShort.tv_usec = SHORT_SELECT_TIMEOUT;
1291 timeoutLong.tv_sec = 86400; /* don't thrash on rediscovery */
1292 timeoutLong.tv_usec = 0;
1295 COPYBITS(allactivefdsG, readfds);
1296 COPYBITS(allactivefdsG, exceptfds);
1299 /* JET 9/1/98 - linux select will actually modify the timeout struct -
1300 * if a select exits early then the timeout struct will contain the
1301 * amount remaining. When this gets to 0,0, an infinite loop
1302 * will occur. So... setup the timeouts each iteration.
1305 timeoutShort.tv_sec = 0; /* in quick rediscovery mode */
1306 timeoutShort.tv_usec = SHORT_SELECT_TIMEOUT;
1307 timeoutLong.tv_sec = 86400; /* don't thrash on rediscovery */
1308 timeoutLong.tv_usec = 0;
1312 if (rediscoverSigCldG || rediscoverUrgentSigG) {
1313 nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1314 FD_SET_CAST(&exceptfds), &timeoutShort);
1317 nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1318 FD_SET_CAST(&exceptfds), &timeoutLong);
1323 * Handle select() problem.
1325 if (errno == EINTR) {
1327 * A signal happened - let rediscover flags redirect flow
1328 * via short select timeouts.
1331 else if ((errno == EBADF) || (errno == EFAULT)) {
1333 * A connection probably dropped.
1336 if ( GETBIT(exceptfds, ttfdG) ) {
1338 * Tooltalk connection has gone bad.
1340 * Judgement call - when the Tooltalk connection goes
1341 * bad, let dtexec continue rather than doing an exit.
1343 DetachFromTooltalk(NULL);
1347 if (errorpipeG[0] != -1) {
1348 if ( GETBIT(exceptfds, errorpipeG[0]) ) {
1350 * Error pipe has gone bad.
1352 close(errorpipeG[0]);
1353 BITCLEAR(allactivefdsG, errorpipeG[0]);
1360 * We have bad paremeters to select()
1364 * So that select() errors cannot dominate, now behave as
1365 * though only a timeout had occurred.
1372 * Have some input to process. Figure out who.
1375 if ( GETBIT(readfds, ttfdG) ) {
1376 /* Clear bit first, since calling input_handler() could */
1377 /* have the side-effect of setting ttfdG to -1! */
1378 BITCLEAR(readfds, ttfdG);
1381 * Tooltalk activity.
1383 * Note that the input_handler parameters match
1384 * an XtInputHandler() style callback in case Xt is
1387 input_handler((char *) NULL, (int *) &junki,
1388 (unsigned long *) &junki);
1392 if (errorpipeG[0] != -1) {
1393 if ( GETBIT(readfds, errorpipeG[0]) ) {
1397 * Read the errorpipe until no more seems available.
1398 * Call that good enough and write a time-stamped
1399 * block to the errorLog file.
1401 errorBytes = 0; /* what we have so far */
1407 nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1408 FD_SET_CAST(NULL), &timeoutShort);
1410 tmpi = read (errorpipeG[0], &buf, 1);
1417 * Grow buffer to hold entire error stream.
1420 if (tmpBuffer == NULL)
1421 tmpBuffer = (char *) malloc(
1424 tmpBuffer = (char *) realloc( tmpBuffer,
1425 errorBytes + tmpi + 1);
1429 tmpBuffer[errorBytes] = buf;
1431 tmpBuffer[errorBytes] = '\0';
1433 if (errorBytes < 65535) {
1435 * Pause a bit and wait for a continuation of
1436 * the error stream if there is more.
1438 select(0, FD_SET_CAST(NULL),
1440 FD_SET_CAST(NULL), &timeoutShort);
1444 * We have enough to do a dump now.
1455 * On the first pass after select(), if we have 0 bytes,
1456 * it really means the pipe has gone down.
1458 close(errorpipeG[0]);
1459 BITCLEAR(allactivefdsG, errorpipeG[0]);
1460 BITCLEAR(readfds, errorpipeG[0]);
1470 tmpProgName = (char *) malloc (strlen (argv[0]) +
1471 strlen (cmdLine[0]) +
1474 tmpProgName = argv[0];
1477 * To identify the process for this stderr,
1478 * use both argv[0] and the name of the
1479 * process that was execvp'd
1481 (void) strcpy (tmpProgName, "(");
1482 (void) strcat (tmpProgName, argv[0]);
1483 (void) strcat (tmpProgName, ") ");
1484 (void) strcat (tmpProgName, cmdLine[0]);
1487 DtMsgLogMessage( tmpProgName, DtMsgLogStderr, "%s",
1492 if (errorpipeG[0] != -1)
1493 BITCLEAR(readfds, errorpipeG[0]);
1497 * So that select() data cannot dominate, now behave as
1498 * though only a timeout had occurred.
1505 * Timeout. We are probably rediscovering and have entered
1506 * a shutdown phase. The following rediscover handlers are
1507 * in priority order.
1509 * Note that by way of timeouts and events, we will make
1510 * multiple passes through this block of code.
1513 if (rediscoverUrgentSigG) {
1515 * Handle urgent signal.
1517 * Tact: wait awhile and see if a SIGCLD will happen.
1518 * If it does, then a normal shutdown will suffice.
1519 * If a SIGCLD does not happen, then do a raw exit(0).
1520 * Exit is required for BBA anyway.
1523 if (rediscoverSigCldG)
1525 * Rather than act on the Urgent Signal, defer to the
1526 * SIGCLD Signal shutdown process.
1528 rediscoverUrgentSigG = 0;
1531 * Still in a mode where we have an outstanding
1532 * Urgent Signal but no SIGCLD. Bump a counter
1533 * which moves us closer to doing an exit().
1535 rediscoverUrgentSigG++;
1538 * After 5 seconds (add select timeout too) waiting for
1539 * a SIGCLD, give up and exit.
1541 if (rediscoverUrgentSigG > ((1000/SHORT_SELECT_TIMEOUT)*5) ) {
1542 #if defined(__aix) || defined(CSRG_BASED) || defined(linux)
1550 if (rediscoverSigCldG) {
1552 * Handle SIGCLD signal.
1554 * Under SIGCLD, we will make multiple passes through the
1555 * following, implementing a phased shutdown.
1557 if (shutdownPhaseG == SDP_DONE_STARTING) {
1559 * Send Done(Request) for starters.
1561 if (dtSvcProcIdG) DoneRequest(_DtActCHILD_DONE);
1565 * Sit and wait for the Done Reply in select()
1567 shutdownPhaseG = SDP_DONE_REPLY_WAIT;
1571 * Unable to send Done Reply. Assume we're on
1572 * our own from now on.
1574 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1578 if (shutdownPhaseG == SDP_DONE_REPLY_WAIT) {
1580 * After 5 minutes of passing through REPLY_WAIT,
1581 * assume the Done(Reply) will never come in and
1584 rediscoverSigCldG++;
1585 if (rediscoverSigCldG > ((1000/SHORT_SELECT_TIMEOUT)*300)) {
1589 * Try to detatch from Tooltalk anyway.
1591 DetachFromTooltalk(NULL);
1594 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1598 * See if the Tooltalk connection is still alive. If
1599 * not, then no reason to wait around.
1601 else if (!dtSvcProcIdG) {
1602 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1607 if (shutdownPhaseG == SDP_DONE_REPLIED) {
1609 * We have our Done(Reply), so proceed.
1613 shutdownPhaseG = SDP_FINAL_LINGER;
1615 shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1618 if (shutdownPhaseG == SDP_DONE_PANIC_CLEANUP) {
1620 * We cannot talk with caller, so do cleanup
1623 for (i = 0; i < tmpFileCntG; i++ ) {
1624 chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) );
1625 unlink( tmpFilesG[i] );
1628 shutdownPhaseG = SDP_FINAL_LINGER;
1631 if (shutdownPhaseG == SDP_FINAL_LINGER) {
1635 static int skipFirst = 1;
1639 * Rather than a quick departure from the select()
1640 * loop, make one more pass. If the child has gone
1641 * down quickly, the SIGCLD may have caused us to
1642 * get here before any errorPipeG information has
1643 * had a chance to reach us.