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