dtwm: basic multihead(xinerama only) support
[oweals/cde.git] / cde / programs / dtexec / Main.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 /*
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.
31  */
32 /******************************************************************************
33  *
34  * File:         Main.c
35  * RCS:          $TOG: Main.c /main/9 1999/09/20 15:36:11 mgreess $
36  * Package:      dtexec for CDE 1.0
37  *
38  *****************************************************************************/
39
40 #define SPINBLOCK       if (getenv("_DTEXEC_DEBUG")) {int i; i=1; while(i) i=1;}
41
42 /*
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.
46  *
47  * If turned on, some routines from DtSvc/DtUtil2/DtNlUtils.c will be
48  * needed.
49  */
50 #undef _DTEXEC_NLS16
51
52 #include <fcntl.h>
53 #include <sys/stat.h>
54 #include <signal.h>
55 #include <stdio.h>
56 #include <errno.h>
57 #include <sys/wait.h>
58
59 #include "osdep.h"      /* select(2) mask width and bit manipulation macros */
60 #include <Tt/tt_c.h>
61 #include <locale.h>
62 #include <unistd.h>
63 #include <stdlib.h>
64 #include <string.h>
65
66 #include <Dt/MsgLog.h>
67
68 /*
69  * From Dt/ActionP.h - never change since these are in effect protocol
70  * codes!   See Dt/ActionP.h for more details.
71  */
72 #ifndef _ActionP_h
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 */
81
82 #define MAX_EXEC_ARGS           1000    /* Maximum number of arguments for */
83                                         /* execvp call. */
84 #define True            1
85 #define False           0
86 #define PERM_TERM       1
87 #define TRANSIENT       2
88 #define SHORT           3
89
90 /*
91  * Triggered by a SIGCLD, the shutdown sequence.
92  */
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
98
99 /*
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).
104  */
105 #define SHORT_SELECT_TIMEOUT    20
106
107 /*
108  * External system calls:
109  */
110 extern pid_t    fork ();
111 extern int      execvp ();
112 extern pid_t    wait ();
113 extern int      atoi ();
114 extern void     _exit ();
115 extern unsigned int     sleep ();
116 extern void     exit ();
117 extern char     *getenv();
118
119 /*
120  * Local func protos
121  */
122 void DoneRequest(int doneCode);
123
124 /*
125  * Global variables.
126  */
127 struct timeval startTimeG;
128 struct timezone zoneG;
129 int    requestTypeG;
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 */
144
145
146 /******************************************************************************
147  *
148  * Minaturized versions of tttk_*() routines.   Used so we don't have to
149  * pull in tttk which in turn would pull in Xt.
150  *
151  *****************************************************************************/
152
153 char  *dtexec_Tttk_integer    = "integer";
154 char  *dtexec_Tttk_message_id = "messageID";
155
156 Tt_message
157 dtexec_tttk_message_create(
158         Tt_message              context,
159         Tt_class                theClass,
160         Tt_scope                theScope,
161         const char             *handler,
162         const char             *op,
163         Tt_message_callback     callback
164 )
165 {
166         Tt_message msg;
167         Tt_address address;
168         Tt_status status;
169
170
171         msg = tt_message_create();
172         status = tt_ptr_error( msg );
173         if (status != TT_OK) {
174                 return msg;
175         }
176
177         status = tt_message_class_set( msg, theClass );
178         if (status != TT_OK) {
179                 return (Tt_message)tt_error_pointer( status );
180         }
181
182         status = tt_message_scope_set( msg, theScope );
183         if (status != TT_OK) {
184                 return (Tt_message)tt_error_pointer( status );
185         }
186
187         address = TT_PROCEDURE;
188         if (handler != 0) {
189                 status = tt_message_handler_set( msg, handler );
190                 if (status != TT_OK) {
191                         return (Tt_message)tt_error_pointer( status );
192                 }
193                 address = TT_HANDLER;
194         }
195
196         status = tt_message_address_set( msg, address );
197         if (status != TT_OK) {
198                 return (Tt_message)tt_error_pointer( status );
199         }
200
201         if (op != 0) {
202                 status = tt_message_op_set( msg, op );
203                 if (status != TT_OK) {
204                         return (Tt_message)tt_error_pointer( status );
205                 }
206         }
207
208         if (callback != 0) {
209                 status = tt_message_callback_add( msg, callback );
210                 if (status != TT_OK) {
211                         return (Tt_message)tt_error_pointer( status );
212                 }
213         }
214
215         return msg;
216 }
217
218 Tt_status
219 dtexec_tttk_message_destroy(
220         Tt_message msg
221 )
222 {
223         Tt_status status;
224
225         status = tt_message_destroy(msg);
226         return status;
227 }
228
229 int
230 dtexec_tttk_message_am_handling(
231         Tt_message msg
232 )
233 {
234         char *handler;
235         int am_handling;
236
237         if (tt_message_class( msg ) != TT_REQUEST) {
238                 return 0;
239         }
240         if (tt_message_state( msg ) != TT_SENT) {
241                 return 0;
242         }
243         handler = tt_message_handler( msg );
244         am_handling = 0;
245         if ((tt_ptr_error( handler ) == TT_OK) && (handler != 0)) {
246                 am_handling = 1;
247         }
248         tt_free( handler );
249         return am_handling;
250 }
251
252 Tt_status
253 dtexec_tttk_message_abandon(
254         Tt_message msg
255 )
256 {
257         int fail;
258         Tt_status status;
259
260         if (dtexec_tttk_message_am_handling( msg )) {
261                 fail = 0;
262                 if (tt_message_address( msg ) == TT_HANDLER) {
263                         fail = 1;
264                 } else if (tt_message_status( msg ) == TT_WRN_START_MESSAGE) {
265                         fail = 1;
266                 }
267
268                 if (fail) {
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 );
273                         }
274                         else {
275                                 status = dtexec_tttk_message_destroy( msg );
276                         }
277                 } else {
278                         tt_message_status_set( msg, TT_DESKTOP_ENOTSUP );
279                         status = tt_message_reject( msg );
280                         dtexec_tttk_message_destroy( msg );
281                 }
282
283         } else {
284                 status = dtexec_tttk_message_destroy( msg );
285         }
286
287         return status;
288 }
289
290
291 /******************************************************************************
292  *
293  * Help - print the usage and exit.
294  *
295  *****************************************************************************/
296
297 static void
298 Help(
299         char *argv[] )
300 {
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");
315    _exit(0);
316 }
317
318
319 /******************************************************************************
320  *
321  * PanicSignal - see InitializeSignalHandling()
322  *
323  *****************************************************************************/
324
325 static void
326 #if defined(__aix) || defined (__osf__) || defined(CSRG_BASED) || defined(linux)
327 PanicSignal(int s)
328 #else
329 PanicSignal(void)
330 #endif /* __aix || __osf__ */
331 {
332     int i;
333
334     /*
335      * Crude, but let libDtSvc know we've been forced down.
336      * Atleast libDtSvc will get a hint and know to cleanup.
337      */
338     if (dtSvcProcIdG)
339         DoneRequest(_DtActCHILD_FAILED);
340
341     if (!dtSvcProcIdG) {
342         /*
343          * We cannot talk with caller, so do cleanup
344          * of tmp files.
345          */
346         for (i = 0; i < tmpFileCntG; i++ ) {
347             chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) );
348             unlink( tmpFilesG[i] );
349         }
350     }
351
352     _exit(0);
353 }
354
355
356 /******************************************************************************
357  *
358  * IgnoreSignal - see InitializeSignalHandling()
359  *
360  *****************************************************************************/
361
362 static void
363 #if defined(__aix) || defined (__osf__) || defined(CSRG_BASED) || defined(linux)
364 IgnoreSignal(int i)
365 #else
366 IgnoreSignal(void)
367 #endif /* __aix || __osf__ */
368 {
369     /*
370      * If the child is still in the same process group, it should be
371      * getting the same signal too.
372      */
373     if (rediscoverSigCldG) {
374         if (shutdownPhaseG == SDP_FINAL_LINGER) {
375             /*
376              * We were shutdown long ago and lingering, so go ahead
377              * and exit now.
378              */
379             _exit(0);
380         }
381         else {
382             /*
383              * Still shutting down, so flip requestTypeG so that dtexec
384              * will not linger when the shutdown process completes.
385              */
386             requestTypeG = TRANSIENT;
387         }
388     }
389     else {
390         /*
391          * If and when the child does repond to the signal we are
392          * ignoring for now, don't allow dtexec to linger.
393          */
394         requestTypeG = TRANSIENT;
395     }
396 }
397
398 /******************************************************************************
399  *
400  * UrgentSignal - see InitializeSignalHandling()
401  *
402  *****************************************************************************/
403
404 static void
405 #if defined(__aix) || defined (__osf__) || defined(CSRG_BASED) || defined(linux)
406 UrgentSignal(int i)
407 #else
408 UrgentSignal(void)
409 #endif /* __aix || __osf__ */
410 {
411     /*
412      * Set global so the central control point ( select() ) will
413      * rediscover the urgent signal.
414      */
415     rediscoverUrgentSigG = 1;
416
417     /*
418      * If the child is still in the same process group, it should be
419      * getting the same signal too.
420      */
421     if (rediscoverSigCldG) {
422         if (shutdownPhaseG == SDP_FINAL_LINGER) {
423             /*
424              * We were shutdown long ago and lingering, so go ahead
425              * and exit now.
426              */
427             _exit(0);
428         }
429         else {
430             /*
431              * Still shutting down, so flip requestTypeG so that dtexec
432              * will not linger when the shutdown process completes.
433              */
434             requestTypeG = TRANSIENT;
435         }
436     }
437     else {
438         /*
439          * If and when the child does repond to the signal we are
440          * ignoring for now, don't allow dtexec to linger.
441          *
442          * This is mildly different than the IgnoreSignal case because
443          * there is a timeout associated with UrgentSignals.
444          */
445         requestTypeG = TRANSIENT;
446     }
447 }
448
449
450 /******************************************************************************
451  *
452  * SigCld - see InitializeSignalHandling()
453  *
454  *****************************************************************************/
455 static void
456 #if defined(__aix) || defined (__osf__) || defined(CSRG_BASED) || defined(linux)
457 SigCld(int i)
458 #else
459 SigCld(void)
460 #endif /* __aix || __osf__ */
461 {
462     int exitStatus;
463     pid_t pid;
464
465     /*
466      * Query why the SIGCLD happened - a true termination or just a stopage.
467      * We only care about terminations.
468      */
469     pid = wait(&exitStatus);
470
471     if (pid == -1) {
472         if (errno == ECHILD) {
473             /*
474              * No child found with wait(), so sure, act like we did
475              * see a SIGCLD.
476              */
477             rediscoverSigCldG = 1;
478         }
479     }
480     else if (!WIFSTOPPED(exitStatus)) {
481         /*
482          * The SIGCLD was *not* the result of being stopped, so the child
483          * has indeed terminated. 
484          */
485         rediscoverSigCldG = 1;
486     }
487 }
488
489
490 /******************************************************************************
491  *
492  * InitializeSignalHandling - Set up the signal catchers.
493  *
494  * Keep this code in sync with fork/exec code so as to NOT pass
495  * these catchers onto our child.
496  *
497  * "Ignore"
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
503  *
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.
506  *
507  * "Urgent"
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
513  *
514  * "SIGCLD"
515  *    1. send Done(Request)
516  *    2. wait for Done(Reply) in select-loop-spin
517  *    3. send Quit(Reply)
518  *    4. cleanup
519  *    5. FinalLinger()
520  *
521  * "Panic"
522  *    1. send Done(Request)
523  *    2. send Quit(Reply)
524  *    3. cleanup
525  *    4. _exit(0)
526  *
527  * "Default"
528  *    1. default signal action
529  *
530  *****************************************************************************/
531 static void
532 InitializeSignalHandling( void )
533 {
534    long                    oldMask;
535    struct sigaction        svec;
536
537    /*
538     * "Graceful Signal" handlers
539     *    - SIGCLD for normal child termination - best case.
540     */
541    sigemptyset(&svec.sa_mask);
542    svec.sa_flags   = 0;
543    svec.sa_handler = SigCld;
544    (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL);
545
546    /*
547     * "Urgent Signal" handlers
548     *    - SIGTERM for standard kill(1)
549     *
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.
553     */
554    sigemptyset(&svec.sa_mask);
555    svec.sa_flags   = 0;
556    svec.sa_handler = UrgentSignal;
557    (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL);
558
559    /*
560     * "Panic Signal" handlers.
561     *    - SIGINT for BBA coverage.
562     */
563    sigemptyset(&svec.sa_mask);
564    svec.sa_flags   = 0;
565    svec.sa_handler = PanicSignal;
566    (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL);
567
568    /*
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
573     */
574    sigemptyset(&svec.sa_mask);
575    svec.sa_flags   = 0;
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);
580
581    /*
582     * "Default Signal" handlers.
583     *    - SIGQUIT - let core dump happen as expected.   If done,
584     *                libDtSvc will never get a done notice.
585     */
586 }
587
588
589 /******************************************************************************
590  *
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.
594  *
595  *****************************************************************************/
596 void FinalLinger(void)
597 {
598     int exitStatus;
599     struct timeval finalTime;
600
601     /*
602      * Make sure to reap child.   The SIGCLD handler may have done
603      * this already.
604      */
605     (void) wait(&exitStatus);
606
607     if (requestTypeG == PERM_TERM)
608         (void) sleep (99999999);
609     else if (requestTypeG == TRANSIENT)
610         (void) _exit (0);
611     else {
612         /*
613          * Check the time stamps and either sleep forever or exit.
614          */
615         if ((gettimeofday (&finalTime, &zoneG)) == -1)
616             (void) _exit (1);
617
618         if ((finalTime.tv_sec - startTimeG.tv_sec) < waitTimeG)
619             (void) sleep (99999999);
620         else
621             (void) _exit (0);
622     }
623 }
624
625
626 /******************************************************************************
627  *
628  * ExecuteCommand -
629  *
630  *****************************************************************************/
631
632 static int
633 ExecuteCommand (
634         char **commandArray)
635 {
636    int i, index1;
637    int exitStatus;
638    struct sigaction svec;
639
640    for (index1 = 0; (index1 < 10) && ((childPidG = fork()) < 0); index1++) {
641       /* Out of resources ? */
642       if (errno != EAGAIN)
643          break;
644       /* If not out of resources, sleep and try again */
645       (void) sleep ((unsigned long) 2);
646    }
647
648    if (childPidG < 0) {
649       return (False);
650    }
651
652    if (childPidG == 0) {
653       /*
654        * Child Process.
655        */
656
657       /*
658        * Hook stderr to error pipe back to parent.
659        */
660       if ((errorpipeG[0] != -1) && (errorpipeG[1] != -1)) {
661          dup2(errorpipeG[1], 2);
662          close(errorpipeG[0]);
663       }
664
665       /*
666        * The child should have default behavior for all signals.
667        */
668       sigemptyset(&svec.sa_mask);
669       svec.sa_flags   = 0;
670       svec.sa_handler = SIG_DFL;
671
672       /* Normal */
673       (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL);
674
675       /* Urgent */
676       (void) sigaction(SIGTERM, &svec, (struct sigaction *) NULL);
677
678       /* Panic */
679       (void) sigaction(SIGINT, &svec, (struct sigaction *) NULL);
680
681       /* Ignore */
682       (void) sigaction(SIGUSR1, &svec, (struct sigaction *) NULL);
683       (void) sigaction(SIGUSR2, &svec, (struct sigaction *) NULL);
684       (void) sigaction(SIGHUP, &svec, (struct sigaction *) NULL);
685
686       for (i=3; i < FOPEN_MAX; i++) {
687          if ( i != errorpipeG[1] )
688              (void) fcntl (i, F_SETFD, FD_CLOEXEC);
689       }
690
691       (void) execvp(commandArray[0], commandArray);
692
693       (void) fprintf (stderr, "Cannot execute \"%s\".\n", commandArray[0]);
694
695       (void) _exit (1);
696    }
697
698    if (errorpipeG[1] != -1) {
699       close(errorpipeG[1]);
700       errorpipeG[1] = -1;
701    }
702
703    return (True);
704 }
705
706 /******************************************************************************
707  *
708  * ParseCommandLine
709  *
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.
712  *
713  *****************************************************************************/
714
715 static char **
716 ParseCommandLine(
717         int argc,
718         char **argv )
719 {
720     char **argv2;
721     char *tick1, *tick2;
722 #ifdef _DTEXEC_NLS16
723     int  tmpi;
724 #endif /* _DTEXEC_NLS16 */
725
726     argv2 = (char **) argv;
727
728     tmpFileCntG = 0;
729     tmpFilesG = (char **) NULL;
730
731     for (argc--, argv++; argc > 0; argc--, argv++) {
732         if ( ! strcmp(argv[0] , "-open") ) {
733             argc--; argv++;
734
735             if ( argc == 0 ) Help( argv2 );
736             waitTimeG = atoi (argv[0]);
737
738             if (waitTimeG < 0)
739                 requestTypeG = PERM_TERM;
740             else if (waitTimeG == 0)
741                 requestTypeG = TRANSIENT;
742             else {
743                 requestTypeG = SHORT;
744             }
745         }
746         else if ( ! strcmp(argv[0] , "-ttprocid") ) {
747             argc--; argv++;
748             if ( argc == 0 ) Help( argv2 );
749
750             /*
751              * Pull the -ttprocid argument apart for it's 3 components.
752              *    <libDtSvc's ProcId>_<Invocation Id>_<Child Id>
753              */
754 #ifdef _DTEXEC_NLS16
755             tmpi = mblen(argv[0]);
756             dtSvcProcIdG = (char *) malloc( tmpi + 1 );
757
758             memcpy( dtSvcProcIdG, argv[0], tmpi );
759             dtSvcProcIdG[tmpi] = NULL;
760 #else
761             dtSvcProcIdG = (char *) malloc( strlen(argv[0]) + 1 );
762             strcpy( dtSvcProcIdG, argv[0] );
763 #endif /* _DTEXEC_NLS16 */
764
765             /*
766              * Search from the end for underscore seperators.
767              */
768 #ifdef _DTEXEC_NLS16
769             tick2 = (char *) Dt_strrchr( dtSvcProcIdG, '_' );
770 #else
771             tick2 = (char *) strrchr( dtSvcProcIdG, '_' );
772 #endif /* _DTEXEC_NLS16 */
773
774             if (tick2)
775                 *tick2 = 0;
776
777 #ifdef _DTEXEC_NLS16
778             tick1 = (char *) Dt_strrchr( dtSvcProcIdG, '_' );
779 #else
780             tick1 = (char *) strrchr( dtSvcProcIdG, '_' );
781 #endif /* _DTEXEC_NLS16 */
782
783             if ( tick1 && tick2 ) {
784                 *tick1 = 0;
785                 *tick2 = 0;
786
787                 dtSvcInvIdG = atoi((char *) (tick1 + 1));
788                 dtSvcChildIdG = atoi((char *) (tick2 + 1));
789
790                 if ( !(dtSvcInvIdG && dtSvcChildIdG) ) {
791                     /*
792                      * Don't have two non-zero values, so we cannot use the
793                      * -ttprocid provided.
794                      */
795                     free(dtSvcProcIdG);
796                     dtSvcProcIdG = (char *) NULL;
797                 }
798             }
799             else {
800                 /*
801                  * Unable to find _ (underscore) seperators.
802                  */
803                 free(dtSvcProcIdG);
804                 dtSvcProcIdG = (char *) NULL;
805             }
806         }
807         else if ( ! strcmp(argv[0] , "-tmp") ) {
808             argc--; argv++;
809             if ( argc == 0 ) Help( argv2 );
810             tmpFileCntG++;
811             tmpFilesG = (char **) realloc( (char *) tmpFilesG,
812                                                 tmpFileCntG * sizeof(char *) );
813             tmpFilesG[tmpFileCntG-1] = argv[0];
814         }
815         else if ( ! strncmp(argv[0], "-h", 2) ) {
816             Help( argv2 );
817             /*
818              * Technically we should see if a -ttprocid was given and
819              * possibly send back a Done(Request).
820              */
821             (void) _exit (1);
822         }
823         else {
824             return( argv );
825         }
826     }
827     /*
828      * No arguments to dtexec, so nothing to fork/exec.
829      */
830     return( (char **) NULL );
831 }
832
833 /******************************************************************************
834  *
835  * Shutdown Tooltalk connection.
836  *
837  *****************************************************************************/
838 void DetachFromTooltalk(
839     unsigned long *nocare1)     /* if Xt - XtInputId *id; */
840 {
841     char *sessid;
842
843     if (dtSvcProcIdG) {
844         /*
845          * NULL the global to indicate that we no longer want to
846          * chit-chat with Tooltalk.
847          */
848         dtSvcProcIdG = (char *) NULL;
849
850         sessid = tt_default_session();
851         tt_session_quit(sessid);
852         tt_free(sessid);
853         tt_close();
854     }
855
856     /*
857      * Unregister the Tooltalk fildes from the select mask.
858      */
859     if (ttfdG != -1) {
860         BITCLEAR(allactivefdsG, ttfdG);
861         ttfdG = -1;
862     }
863 }
864
865 /******************************************************************************
866  *
867  * Alternate input handler to tttk_Xt_input_handler
868  *
869  * If we end up pulling Xt in, toss this routine and use
870  * tttk_Xt_input_handler instead.
871  *
872  *****************************************************************************/
873 void
874 input_handler(
875     char          *nocare1,     /* if Xt - XtPointer w */
876     int           *nocare2,     /* if Xt - int *source */
877     unsigned long *nocare3)     /* if Xt - XtInputId *id; */
878 {
879     Tt_message msg;
880     Tt_status status;
881
882
883     msg = tt_message_receive();
884     status = tt_ptr_error( msg );
885
886     if (status != TT_OK) {
887         /*
888          * Problem; think about bailing.
889          */
890         if (status == TT_ERR_NOMP) {
891             /*
892              * Big time lost.
893              */
894             DetachFromTooltalk(NULL);
895         }
896         return;
897     }
898
899     if (msg == 0) {
900         /*
901          * A pattern callback ate the message for us.
902          */
903         return;
904     }
905
906     /*
907      * All messages should have been consumed by a callback.
908      * Pick between failing and rejecting the msg.
909      */
910     status = dtexec_tttk_message_abandon( msg );
911     if (status != TT_OK) {
912         /* don't care */
913     }
914 }
915
916
917 /******************************************************************************
918  *
919  * Initiallize Tooltalk world.
920  *
921  *****************************************************************************/
922 static void
923 ToolTalkError(char *errfmt, Tt_status status)
924 {
925     char        *statmsg;
926
927     if (! tt_is_err(status)) return;
928
929     statmsg = tt_status_message(status);
930     DtMsgLogMessage( "Dtexec", DtMsgLogStderr, errfmt, statmsg );
931 }
932
933 int InitializeTooltalk(void)
934 {
935     char * procid;
936     Tt_status status;
937     int fd;
938
939     procid = tt_default_procid();
940     status = tt_ptr_error(procid);
941     if ((status == TT_ERR_NOMP) || (status == TT_ERR_PROCID)) {
942         /*
943          * We need to try to establish a connection
944          */
945         procid = tt_open();
946         status = tt_ptr_error(procid);
947         if (status != TT_OK) {
948             ToolTalkError("Could not connect to ToolTalk:\n%s\n", status);
949             return (False);
950         }
951         tt_free(procid);
952
953         /*
954          * Determine the Tooltalk fildes.
955          */
956         fd = tt_fd();
957         status = tt_int_error(fd);
958         if (status != TT_OK) {
959             ToolTalkError("Could not connect to ToolTalk:\n%s\n", status);
960             tt_close();
961             ttfdG = -1;
962             return(False);
963         }
964         else {
965             ttfdG = fd;
966         }
967
968 #ifdef DtActUseXtOverSelect
969         /*
970          * Add the ToolTalk file descriptor to the set monitored by Xt
971          */
972         XtAddInput(fd, (XtPointer)XtInputReadMask, input_handler, 0);
973 #endif /* DtActUseXtOverSelect */
974     }
975
976     return (True);
977 }
978
979 /******************************************************************************
980  *
981  * Send some identification back to libDtSvc so it can talk back to
982  * dtexec.  The request format is:
983  *
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
988  *
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.
993  *
994  *****************************************************************************/
995 /*************************************************
996  *
997  * Routine to catch identification reply.
998  */
999 Tt_callback_action IdSelfToCallerReplyCB(
1000     Tt_message msg,
1001     Tt_pattern pattern)
1002 {
1003     Tt_state state;
1004     Tt_status status;
1005     char *errorMsg;
1006
1007
1008     status = tt_message_status(msg);
1009     state = tt_message_state(msg);
1010
1011     if (state == TT_FAILED) {
1012         /*
1013          * tjg: At some point, may want to dump the following error
1014          * message into a log file.  May have to wrap long messages.
1015          */
1016         if (status < TT_ERR_LAST)
1017             errorMsg = tt_status_message(status);
1018         else
1019             errorMsg = tt_message_status_string(msg);
1020
1021         dtexec_tttk_message_destroy(msg);
1022         DetachFromTooltalk(NULL);
1023     }
1024     else if (state == TT_HANDLED) {
1025         dtexec_tttk_message_destroy(msg);
1026         /*
1027          * Nothing substantial to do with the request-reply in the current
1028          * implementation.
1029          */
1030     }
1031     else {
1032     }
1033
1034     return( (Tt_callback_action) TT_CALLBACK_PROCESSED );
1035 }
1036
1037 /*************************************************
1038  *
1039  * Routine to send identification request.
1040  */
1041 void IdSelfToCallerRequest(void)
1042 {
1043     Tt_message msg;
1044     Tt_status status;
1045     char *procid;
1046
1047     procid = tt_default_procid();
1048
1049     msg = dtexec_tttk_message_create( (Tt_message) NULL, TT_REQUEST, TT_SESSION,
1050                                 dtSvcProcIdG,
1051                                 "_DtActDtexecID",
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 );
1056
1057     status = tt_message_send( msg );
1058
1059     tt_free(procid);
1060
1061     if (status != TT_OK) {
1062         dtexec_tttk_message_destroy( msg );
1063         DetachFromTooltalk(NULL);
1064     }
1065 }
1066
1067 /******************************************************************************
1068  *
1069  * Send a Done notice back to libDtSvc.
1070  *
1071  *     _DtActDtexecDone
1072  *        iarg(invID)               - matches libDtSvc's invocation ID
1073  *        iarg(childID)             - matches libDtSvc's child ID
1074  *        iarg(DtActionStatus)      - a DtActionStatus style code
1075  *
1076  *****************************************************************************/
1077 /*************************************************
1078  *
1079  * Routine to catch identification reply.
1080  */
1081 Tt_callback_action DoneRequestReplyCB(
1082     Tt_message msg,
1083     Tt_pattern pattern)
1084 {
1085     Tt_state state;
1086     Tt_status replyStatus;
1087
1088
1089     state = tt_message_state(msg);
1090
1091     if (state == TT_FAILED) {
1092         dtexec_tttk_message_destroy(msg);
1093         DetachFromTooltalk(NULL);
1094
1095         shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1096     }
1097     else if (state == TT_HANDLED) {
1098         dtexec_tttk_message_destroy(msg);
1099
1100         shutdownPhaseG = SDP_DONE_REPLIED;
1101     }
1102     else {
1103     }
1104
1105     return( (Tt_callback_action) TT_CALLBACK_PROCESSED );
1106 }
1107
1108 /*************************************************
1109  *
1110  * Routine to send done request.
1111  */
1112 void DoneRequest(int doneCode)
1113
1114 {
1115     static int beenhere = 0;
1116     Tt_message  msg;
1117     Tt_status status;
1118     char *procid;
1119
1120     /*
1121      * Only allow one Done(Request) to be issued.
1122      */
1123     if (!beenhere) {
1124         beenhere = 1;
1125
1126         procid = tt_default_procid();
1127
1128         msg = dtexec_tttk_message_create( (Tt_message) NULL,
1129                                 TT_REQUEST, TT_SESSION,
1130                                 dtSvcProcIdG,
1131                                 "_DtActDtexecDone",
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 );
1136
1137         status = tt_message_send( msg );
1138
1139         tt_free(procid);
1140
1141         if (status != TT_OK) {
1142             dtexec_tttk_message_destroy( msg );
1143             DetachFromTooltalk(NULL);
1144         }
1145     }
1146 }
1147
1148 /******************************************************************************
1149  *
1150  * main
1151  *
1152  *****************************************************************************/
1153 int
1154 main (
1155         int argc,
1156         char **argv )
1157 {
1158     char **cmdLine;
1159     int    success;
1160     fd_set readfds, exceptfds;
1161     int    nfound;
1162     struct timeval timeoutShort, timeoutLong;
1163     int    junki,i;
1164     char  *tmpBuffer;
1165     int    errorBytes;
1166     int    firstPass, tmpi;
1167     char  *tmpProgName = NULL;
1168
1169
1170     setlocale( LC_ALL, "" );
1171
1172 #ifdef _DTEXEC_NLS16
1173     Dt_nlInit();
1174 #endif /* _DTEXEC_NLS16 */
1175  
1176     /*
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.
1180      */
1181     if (getenv("_DTEXEC_DEBUG") && (argc > 1)) {
1182         /*
1183          * Don't block in a system call, or on libDtSvc's attempts to
1184          * just test exec us.
1185          */
1186         SPINBLOCK
1187     }
1188
1189     /*
1190      * Note: dtSvcProcIdG is used like a boolean to control whether
1191      * we are communicating with libDtSvc using Tooltalk.
1192      */
1193     dtSvcProcIdG = (char *) NULL;       /* assume not communicating with TT */
1194     ttfdG = -1;
1195
1196     cmdLine = ParseCommandLine (argc, argv);
1197
1198     /*
1199      * If a signal goes off *outside* the upcoming select, we'll need to
1200      * rediscover the signal by letting select() timeout.
1201      *
1202      * We might also set a rediscover flag to fake a signal response.
1203      */
1204     rediscoverSigCldG = 0;              /* boolean and counter */
1205     rediscoverUrgentSigG = 0;           /* boolean and counter */
1206
1207     InitializeSignalHandling ();
1208
1209     /*
1210      * Create a pipe for logging of errors for actions without
1211      * windows.
1212      */
1213     errorpipeG[0] = -1;                 /* by default, no stderr redirection */
1214     errorpipeG[1] = -1;
1215     if ( requestTypeG == TRANSIENT ) {    /* should be WINDOW_TYPE NO_STDIO  */
1216         if ( pipe(errorpipeG) == -1 ) {
1217             errorpipeG[0] = -1;
1218             errorpipeG[1] = -1;
1219         }
1220     }
1221
1222     if (cmdLine) {
1223         success = ExecuteCommand (cmdLine);
1224         if (!success) {
1225             /*
1226              * Act like we were killed - it will result in a
1227              * DtACTION_FAILED.
1228              */
1229             childPidG = -1;
1230             rediscoverUrgentSigG = 1;
1231         }
1232     }
1233     else {
1234         /*
1235          * Act like we had a child and it went away - it will result
1236          * in a DtACTION_DONE.
1237          */
1238         childPidG = -1;
1239         rediscoverSigCldG = 1;
1240     }
1241
1242    /*
1243     * Note when we started so we can compare times when we finish.
1244     */
1245    (void) gettimeofday (&startTimeG, &zoneG);
1246
1247     if (dtSvcProcIdG) {
1248         if ( !InitializeTooltalk() ) {
1249             /*
1250              * We have no hope of talking to our caller via Tooltalk.
1251              */
1252             dtSvcProcIdG = (char *) NULL;
1253         }
1254     }
1255
1256     /*
1257      * Tie in to the default session and start chatting.
1258      */
1259     if (dtSvcProcIdG) tt_session_join(tt_default_session());
1260
1261     /*
1262      * Finally send caller our current proc id so they can talk back.
1263      */
1264     if (dtSvcProcIdG) IdSelfToCallerRequest();
1265
1266     /*
1267      * Monitor file descriptors for activity.  If errors occur on a fds,
1268      * it will be removed from allactivefdsG after handling the error.
1269      */
1270     CLEARBITS(allactivefdsG);
1271
1272     /*
1273      * Add Tooltalk
1274      */
1275     if ( ttfdG != -1 )
1276         BITSET(allactivefdsG, ttfdG);          /* add Tooltalk */
1277
1278     /*
1279      * Add Error Log
1280      */
1281     if ( errorpipeG[0] != -1 )
1282         BITSET(allactivefdsG, errorpipeG[0]);  /* add read side of error pipe */
1283
1284     /*
1285      * Set options for rediscovery and not-rediscovery modes of
1286      * operation. 
1287      */
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;
1293
1294     for (;;) {
1295         COPYBITS(allactivefdsG, readfds);
1296         COPYBITS(allactivefdsG, exceptfds);
1297
1298 #if defined(linux)
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. 
1303         */
1304
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;
1309 #endif
1310
1311
1312         if (rediscoverSigCldG || rediscoverUrgentSigG) {
1313             nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1314                                 FD_SET_CAST(&exceptfds), &timeoutShort);
1315         }
1316         else {
1317             nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1318                                 FD_SET_CAST(&exceptfds), &timeoutLong);
1319         }
1320
1321         if (nfound == -1) {
1322             /*
1323              * Handle select() problem.
1324              */
1325             if (errno == EINTR) {
1326                 /*
1327                  * A signal happened - let rediscover flags redirect flow
1328                  * via short select timeouts.
1329                  */
1330             }
1331             else if ((errno == EBADF) || (errno == EFAULT)) {
1332                 /*
1333                  * A connection probably dropped.
1334                  */
1335                 if (ttfdG != -1) {
1336                     if ( GETBIT(exceptfds, ttfdG) ) {
1337                         /*
1338                          * Tooltalk connection has gone bad.
1339                          *
1340                          * Judgement call - when the Tooltalk connection goes
1341                          * bad, let dtexec continue rather than doing an exit.
1342                          */
1343                         DetachFromTooltalk(NULL);
1344                     }
1345                 }
1346
1347                 if (errorpipeG[0] != -1) {
1348                     if ( GETBIT(exceptfds, errorpipeG[0]) ) {
1349                         /*
1350                          * Error pipe has gone bad.
1351                          */
1352                         close(errorpipeG[0]);
1353                         BITCLEAR(allactivefdsG, errorpipeG[0]);
1354                         errorpipeG[0] = -1;
1355                     }
1356                 }
1357             }
1358             else {
1359                 /*
1360                  * We have bad paremeters to select()
1361                  */
1362             }
1363             /*
1364              * So that select() errors cannot dominate, now behave as
1365              * though only a timeout had occured.
1366              */
1367             nfound = 0;
1368         }
1369
1370         if (nfound > 0) {
1371             /*
1372              * Have some input to process.  Figure out who.
1373              */
1374             if (ttfdG != -1) {
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);
1379
1380                     /*
1381                      * Tooltalk activity.
1382                      *
1383                      * Note that the input_handler parameters match
1384                      * an XtInputHandler() style callback in case Xt is
1385                      * ever used.
1386                      */
1387                     input_handler((char *) NULL, (int *) &junki,
1388                                         (unsigned long *) &junki);
1389                 }
1390             }
1391
1392             if (errorpipeG[0] != -1) {
1393                 if ( GETBIT(readfds, errorpipeG[0]) ) {
1394                     /*
1395                      * Stderr activity.
1396                      *
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.
1400                      */
1401                     errorBytes = 0;             /* what we have so far */
1402                     tmpBuffer  = NULL;
1403                     firstPass = 1;
1404
1405                     while (1) {
1406                         char buf;
1407                         nfound =select(MAXSOCKS, FD_SET_CAST(&readfds), FD_SET_CAST(NULL),
1408                              FD_SET_CAST(NULL), &timeoutShort);
1409                         if (nfound > 0) {
1410                             tmpi = read (errorpipeG[0], &buf, 1);
1411                         } else {
1412                             tmpi = 0;
1413                         }
1414
1415                         if ( tmpi > 0 ) {
1416                             /*
1417                              * Grow buffer to hold entire error stream.
1418                              */
1419                             firstPass = 0;
1420                             if (tmpBuffer == NULL)
1421                                 tmpBuffer = (char *) malloc(
1422                                                 tmpi + 1);
1423                             else
1424                                 tmpBuffer = (char *) realloc( tmpBuffer,
1425                                                 errorBytes + tmpi + 1);
1426                             /*
1427                              * Drain error pipe.
1428                              */
1429                             tmpBuffer[errorBytes] = buf;
1430                             errorBytes += tmpi;
1431                             tmpBuffer[errorBytes] = '\0';
1432
1433                             if (errorBytes < 65535) {
1434                                 /*
1435                                  * Pause a bit and wait for a continuation of
1436                                  * the error stream if there is more.
1437                                  */
1438                                 select(0, FD_SET_CAST(NULL),
1439                                           FD_SET_CAST(NULL),
1440                                           FD_SET_CAST(NULL), &timeoutShort);
1441                             }
1442                             else {
1443                                 /*
1444                                  * We have enough to do a dump now.
1445                                  */
1446                                 break;
1447                             }
1448                         }
1449                         else {
1450                             /*
1451                              * No more to read.
1452                              */
1453                             if (firstPass) {
1454                                 /*
1455                                  * On the first pass after select(), if we have 0 bytes,
1456                                  * it really means the pipe has gone down.
1457                                  */
1458                                 close(errorpipeG[0]);
1459                                 BITCLEAR(allactivefdsG, errorpipeG[0]);
1460                                 BITCLEAR(readfds, errorpipeG[0]);
1461                                 errorpipeG[0] = -1;
1462                             }
1463                             break;
1464                         }
1465                     }
1466
1467                     if (tmpBuffer) {
1468
1469                         if (!tmpProgName) {
1470                             tmpProgName = (char *) malloc (strlen (argv[0]) + 
1471                                                            strlen (cmdLine[0]) +
1472                                                            5);
1473                             if (!tmpProgName)
1474                                 tmpProgName = argv[0];
1475                             else {
1476                                 /*
1477                                  * To identify the process for this stderr,
1478                                  * use both argv[0] and the name of the
1479                                  * process that was execvp'd
1480                                  */
1481                                 (void) strcpy (tmpProgName, "(");
1482                                 (void) strcat (tmpProgName, argv[0]);
1483                                 (void) strcat (tmpProgName, ") ");
1484                                 (void) strcat (tmpProgName, cmdLine[0]);
1485                             }
1486                         }
1487                         DtMsgLogMessage( tmpProgName, DtMsgLogStderr, "%s", 
1488                                          tmpBuffer );
1489                         free( tmpBuffer );
1490                     }
1491
1492                     if (errorpipeG[0] != -1)
1493                         BITCLEAR(readfds, errorpipeG[0]);
1494                 }
1495             }
1496             /*
1497              * So that select() data cannot dominate, now behave as
1498              * though only a timeout had occured.
1499              */
1500             nfound = 0;
1501         }
1502
1503         if (nfound == 0) {
1504             /*
1505              * Timeout.  We are probably rediscovering and have entered
1506              * a shutdown phase.   The following rediscover handlers are
1507              * in priority order.
1508              *
1509              * Note that by way of timeouts and events, we will make
1510              * multiple passes through this block of code.
1511              */
1512
1513             if (rediscoverUrgentSigG) {
1514                 /*
1515                  * Handle urgent signal.
1516                  *
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.
1521                  */
1522
1523                 if (rediscoverSigCldG)
1524                     /*
1525                      * Rather than act on the Urgent Signal, defer to the
1526                      * SIGCLD Signal shutdown process.
1527                      */
1528                     rediscoverUrgentSigG = 0;
1529                 else
1530                     /*
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().
1534                      */
1535                     rediscoverUrgentSigG++;
1536
1537                 /*
1538                  * After 5 seconds (add select timeout too) waiting for
1539                  * a SIGCLD, give up and exit.
1540                  */
1541                 if (rediscoverUrgentSigG > ((1000/SHORT_SELECT_TIMEOUT)*5) ) {
1542 #if defined(__aix) || defined (__osf__) || defined(CSRG_BASED) || defined(linux)
1543                     PanicSignal(0);
1544 #else
1545                     PanicSignal();
1546 #endif
1547                 }
1548             }
1549
1550             if (rediscoverSigCldG) {
1551                 /*
1552                  * Handle SIGCLD signal.
1553                  *
1554                  * Under SIGCLD, we will make multiple passes through the
1555                  * following, implementing a phased shutdown.
1556                  */
1557                 if (shutdownPhaseG == SDP_DONE_STARTING) {
1558                     /*
1559                      * Send Done(Request) for starters.
1560                      */
1561                     if (dtSvcProcIdG) DoneRequest(_DtActCHILD_DONE);
1562
1563                     if (dtSvcProcIdG) {
1564                         /*
1565                          * Sit and wait for the Done Reply in select()
1566                          */
1567                         shutdownPhaseG = SDP_DONE_REPLY_WAIT;
1568                     }
1569                     else {
1570                         /*
1571                          * Unable to send Done Reply.  Assume we're on
1572                          * our own from now on.
1573                          */
1574                         shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1575                     }
1576                 }
1577
1578                 if (shutdownPhaseG == SDP_DONE_REPLY_WAIT) {
1579                     /*
1580                      * After 5 minutes of passing through REPLY_WAIT,
1581                      * assume the Done(Reply) will never come in and
1582                      * move on.
1583                      */
1584                     rediscoverSigCldG++;
1585                     if (rediscoverSigCldG > ((1000/SHORT_SELECT_TIMEOUT)*300)) {
1586
1587                         if (dtSvcProcIdG) {
1588                             /*
1589                              * Try to detatch from Tooltalk anyway.
1590                              */
1591                             DetachFromTooltalk(NULL);
1592                         }
1593
1594                         shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1595                     }
1596
1597                     /*
1598                      * See if the Tooltalk connection is still alive.   If
1599                      * not, then no reason to wait around.
1600                      */
1601                     else if (!dtSvcProcIdG) {
1602                         shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1603                     }
1604
1605                 }
1606
1607                 if (shutdownPhaseG == SDP_DONE_REPLIED) {
1608                     /*
1609                      * We have our Done(Reply), so proceed.
1610                      */
1611
1612                     if (dtSvcProcIdG)
1613                         shutdownPhaseG = SDP_FINAL_LINGER;
1614                     else
1615                         shutdownPhaseG = SDP_DONE_PANIC_CLEANUP;
1616                 }
1617
1618                 if (shutdownPhaseG == SDP_DONE_PANIC_CLEANUP) {
1619                     /*
1620                      * We cannot talk with caller, so do cleanup
1621                      * of tmp files.
1622                      */
1623                     for (i = 0; i < tmpFileCntG; i++ ) {
1624                         chmod( tmpFilesG[i], (S_IRUSR|S_IWUSR) );
1625                         unlink( tmpFilesG[i] );
1626                     }
1627
1628                     shutdownPhaseG = SDP_FINAL_LINGER;
1629                 }
1630
1631                 if (shutdownPhaseG == SDP_FINAL_LINGER) {
1632                     /*
1633                      * All done.
1634                      */
1635                     static int skipFirst = 1;
1636
1637                     if (skipFirst) {
1638                         /*
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.
1644                          */
1645                         skipFirst = 0;
1646                     }
1647                     else {
1648                         FinalLinger();
1649                     }
1650                 }
1651             }
1652         }
1653     }
1654 }
1655