libDtSearch: Coverity 86579
[oweals/cde.git] / cde / programs / dtpdmd / manager.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 libraries 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  ******************************************************************************
25  **
26  ** File:         manager.c
27  ** RCS:          $XConsortium: manager.c /main/8 1996/10/30 19:10:15 cde-hp $
28  **
29  ** Description:
30  **
31  ** (c) Copyright 1995, Hewlett-Packard Company, all rights reserved.
32  **
33  ******************************************************************************
34  *****************************************************************************/
35
36 #define MANAGER_DOT_C
37
38 #define OPTIONAL_PXAUTH_PRETEST 1
39
40 #include "dtpdmdP.h"
41 #include "nlmsg.h"
42 #include <locale.h>
43 #include <unistd.h>
44
45
46
47 /******************************************************************************
48  ******************************************************************************
49  *
50  * The following PDM_MANAGER : PDM_START routines are registered
51  * per child, and dispatched out of XtAppNextEvent
52  */
53
54 /********************************************************************
55  *
56  * message_pipe_handler()
57  *
58  * Takes stderr from the child, via a pipe, and stores the output
59  * in the client tracking record.
60  * 
61  */
62 static void message_pipe_handler( XtPointer w, int *source, XtInputId *id)
63 {
64     int    i, inc;
65     struct stat statBuffer;
66     int    delt_with;
67     int    n, keepon;
68
69     /*
70      * Find out who is generating output.
71      */
72     delt_with = False;
73
74     for ( i = 0; i < g.serviceRecNum; i++ ) {
75         if ( g.serviceRecs[i]->message_pipe[0] == *source ) {
76             delt_with = True;
77             /*
78              * Fetch size and grow message_string to hold more.
79              */
80             if ( fstat(*source, &statBuffer) ) {
81                 /* unable to get size */
82                 statBuffer.st_size = 0;         /* bail out below */
83             }
84
85             if ( statBuffer.st_size > 0 ) {
86                 if ( g.serviceRecs[i]->message_string ) {
87                     inc = strlen( g.serviceRecs[i]->message_string );
88
89                     g.serviceRecs[i]->message_string = Xrealloc(
90                                 (char *) g.serviceRecs[i]->message_string,
91                                 statBuffer.st_size + inc + 1 );
92                 }
93                 else {
94                     inc = 0;
95
96                     g.serviceRecs[i]->message_string = Xmalloc(
97                                 statBuffer.st_size + 1 );
98                 }
99
100                 /*
101                  * Read off what we know is there.
102                  */
103                 keepon = True;
104                 while (keepon) {
105                     n = read( *source, &(g.serviceRecs[i]->message_string[inc]),
106                                 statBuffer.st_size );
107
108                     if ( n == statBuffer.st_size ) {
109                         /* read all there is (per the previous stat) */
110                         keepon = False;
111                     }
112                     else if (( n == -1 ) && ( errno == EINTR )) {
113                         /* an interrupt came in before the read could start */
114                         keepon = True;
115                     }
116                     else if (( n == -1 ) || ( n == 0 )) {
117                         /* problems - bail out */
118                         g.serviceRecs[i]->message_pipe[0] = -1;
119                         close(*source);
120                         XtRemoveInput(*id);
121                         keepon = False;
122                     }
123                     else {
124                         /* only a partial read, probably a sig, try for more */
125                         inc += n;
126                         statBuffer.st_size -= n;
127                         keepon = True;
128                     }
129                 }
130
131                 /*
132                  * NULL terminate what we have so far to make it look
133                  * like a string.
134                  */
135                 g.serviceRecs[i]->message_string[statBuffer.st_size+inc] = '\0';
136             }
137             else {
138                 /*
139                  * No more to read - this really means the pipe is
140                  * going down.
141                  */
142                 g.serviceRecs[i]->message_pipe[0] = -1;
143                 close (*source);
144                 XtRemoveInput(*id);
145             }
146         }
147     }
148
149     if (!delt_with) {
150         /*
151          * Some stray pipe that we no longer have information on.
152          */
153         close(*source);
154         XtRemoveInput(*id);
155     }
156
157     /*
158      * Scan client records and see who is ready to be
159      * shut down.
160      */
161     mgr_shutdown_scan();
162 }
163
164
165 /******************************************************************************
166  ******************************************************************************
167  *
168  * The following PDM_MANAGER : PDM_START routines are called by
169  * the dispatch routines to service selection requests and
170  * associated events.
171  */
172
173 /********************************************************************
174  *
175  * Setup a child service record per the selection request.
176  */
177 void mgr_initialize( XEvent *report, XpPdmServiceRec *rec )
178 {
179     Display *testdpy;
180     char    buf[1024];
181
182     Display *selection_display;
183     Window   requestor;
184     Atom     prop_atom;
185     unsigned long tafter;
186
187     XTextProperty  text_prop;
188     char           **list;
189     int            list_cnt;
190
191     /*
192      * Grab the PDM_CLIENT_PROP from which all the juicy
193      * information is retrieved.
194      */
195     selection_display = report->xselectionrequest.display;
196     requestor = report->xselectionrequest.requestor;
197     prop_atom = report->xselectionrequest.property;
198
199     if ( XGetWindowProperty( selection_display, requestor, prop_atom,
200                         0, 100000, True, AnyPropertyType,
201                         &text_prop.encoding,
202                         &text_prop.format,
203                         &text_prop.nitems,
204                         &tafter,
205                         &text_prop.value ) != Success ) {
206         /*
207          * Error
208          */
209         rec->pdm_exec_errorcode = g.pdm_start_error;
210
211         sprintf( buf, PDMD_MSG_5, g.prog_name );
212         rec->pdm_exec_errormessage = xpstrdup( buf );
213
214         return;
215     }
216
217     if ( text_prop.format != 8 ) {
218         /*
219          * Error
220          */
221         rec->pdm_exec_errorcode = g.pdm_start_error;
222
223         sprintf( buf, PDMD_MSG_6, g.prog_name );
224         rec->pdm_exec_errormessage = xpstrdup( buf );
225
226         return;
227     }
228
229     if ( XmbTextPropertyToTextList( selection_display, &text_prop,
230                                      &list, &list_cnt ) < 0 ) {
231         /*
232          * Error
233          */
234         rec->pdm_exec_errorcode = g.pdm_start_error;
235
236         sprintf( buf, PDMD_MSG_7, g.prog_name );
237         rec->pdm_exec_errormessage = xpstrdup( buf );
238
239         return;
240     }
241
242     /*
243      * Fill in the PDM_MANAGER portion of the client record.
244      */
245     rec->video_display_str  = xpstrdup( list[0] );
246     rec->video_window       = strtol(list[1], (char **)NULL, 16);
247     rec->print_display_str  = xpstrdup( list[2] );
248     rec->print_window       = strtol(list[3], (char **)NULL, 16);
249 #if 0 && defined(PRINTING_SUPPORTED)
250     rec->print_context      = strtol(list[4], (char **)NULL, 16);
251 #endif /* PRINTING_SUPPORTED */
252     rec->locale_hint        = xpstrdup( list[5] );
253     XFreeStringList( list );
254
255     rec->selection_display  = selection_display;
256     rec->requestor          = requestor;
257     rec->prop_atom          = prop_atom;
258     rec->selection          = report->xselectionrequest.selection;
259     rec->time               = report->xselectionrequest.time;
260
261     rec->mgr_flag           = True;     /* mgr portion of rec now valid */
262
263     /*
264      * Optimization.  The only live display connection, for which we
265      * need to trap XIO errors, is "selection display".  For the
266      * "video" and "print" displays, we have the display strings and
267      * can establish connections as we need them.  Since they are rarely
268      * used, and opening them up here would create XIO liability problems
269      * and a startup performance hit, we won't establish connections now.
270      *
271      * One optimization however is to see if the "print" display would
272      * just happen to be the same at the "selection display" currently
273      * open.
274      */
275     if ( !strcmp( XDisplayString(rec->selection_display),
276                   rec->print_display_str ) ) {
277         rec->seldpy_as_printdpy = True;
278     }
279     else {
280         rec->seldpy_as_printdpy = False;
281
282 #ifdef OPTIONAL_PXAUTH_PRETEST
283         /*
284          * Verify connectability to the Print Server.
285          *
286          * Note: once beyond the selection phase, all communication
287          *       will be by way of the Print Server.  If we cannot
288          *       connect later, then we will have no way to deliver
289          *       EXIT_PXAUTH, EXIT_VXAUTH, EXIT_ERROR, EXIT_OK or
290          *       EXIT_CANCEL.  Real bad news!
291          *
292          * It is better to discover now that we don't have
293          * connection authorization for the print display since
294          * we can still let the user know of PXAUTH problems
295          * via the selection display currently open.
296          *
297          * Unfortunately, this pre-test is a performance hit in the
298          * startup phase.
299          */
300         if ( ! (testdpy = XOpenDisplay(rec->print_display_str)) ) {
301             rec->pdm_exec_errorcode = g.pdm_start_pxauth;
302             return;
303         }
304         XCloseDisplay( testdpy );
305 #endif /* OPTIONAL_PXAUTH_PRETEST */
306     }
307
308 #ifdef OPTIONAL_VXAUTH_PRETEST
309     /*
310      * Verify connectability to the Video Server.
311      *
312      * It is better to discover now that we don't have
313      * connection authorization for the video display since
314      * we can still let the user know of VXAUTH problems
315      * via the selection display currently open.
316      *
317      * Unfortunately, this pre-test is a performance hit in the
318      * startup phase.
319      */
320     if ( ! (testdpy = XOpenDisplay(rec->video_display_str)) ) {
321         rec->pdm_exec_errorcode = g.pdm_start_vxauth;
322         return;
323     }
324     XCloseDisplay( testdpy );
325 #endif /* OPTIONAL_VXAUTH_PRETEST */
326
327 }
328 /********************************************************************
329  *
330  * fork/exec a child pdm after setting up a message pipe. 
331  */
332 void mgr_launch_pdm( XpPdmServiceRec *rec )
333 {
334     int       i;
335     struct sigaction svec;
336     char      buf[1024];
337     int       original_umask;
338     char      *existing_name;
339     FILE      *existing_file;
340     Xauth     *entry;
341     char      *envstr;
342
343
344     /*
345      * Setup message pipe.
346      */
347     if ( pipe(rec->message_pipe) == -1 ) {
348         rec->pdm_exec_errorcode = g.pdm_start_error;
349         sprintf( buf, PDMD_MSG_8, g.prog_name );
350         rec->pdm_exec_errormessage = xpstrdup( buf );
351         return;
352     }
353
354     rec->message_xtid = XtAppAddInput( g.context, rec->message_pipe[0],
355                           (XtPointer) XtInputReadMask,
356                           message_pipe_handler, (XtPointer) NULL );
357
358     /*
359      * See if a cookie file is needed.
360      */
361     if (rec->cookie_cnt) {
362         /*
363          * Create new .Xauthority file.
364          */
365         original_umask = umask (0077);      /* disallow non-owner access */
366         tmpnam( rec->auth_filename );
367         rec->auth_file = fopen( rec->auth_filename, "w" );
368
369         if (rec->auth_file) {
370             /*
371              * Copy existing .Xauthority entries.
372              */
373             existing_name = XauFileName ();
374
375             if (existing_name) {
376                 if (access (existing_name, R_OK) == 0) {     /* checks REAL id */
377                     existing_file = fopen (existing_name, "r");
378                     if (existing_file) {
379                         for (;;) {
380                             entry = XauReadAuth (existing_file);
381                             if (!entry)
382                                 break;
383
384                             XauWriteAuth( rec->auth_file, entry );
385                             XauDisposeAuth (entry);
386                         }
387                         fclose (existing_file);
388                     }
389                 }
390             }
391
392             /*
393              * Merge in cookies recently sent.
394              */
395             for ( i = 0; i < rec->cookie_cnt; i++ ) {
396                 XauWriteAuth( rec->auth_file, rec->cookies[i] );
397             }
398
399             fclose( rec->auth_file );
400         }
401         original_umask = umask (original_umask);
402     }
403
404
405     rec->pid = fork();
406
407     if ( rec->pid < 0 ) {
408         rec->pdm_exec_errorcode = g.pdm_start_error;
409         sprintf( buf, PDMD_MSG_9, g.prog_name );
410         rec->pdm_exec_errormessage = xpstrdup( buf );
411         return;
412     }
413     else if ( rec->pid == 0) {
414         /*
415          * Child process.
416          */
417
418         /*
419          * Hook stderr back to parent via message pipe.
420          */
421         dup2(rec->message_pipe[1], 2);
422         close(rec->message_pipe[0]);
423
424         /*
425          * The child should have default behavior for all signals.
426          */
427         sigemptyset(&svec.sa_mask);
428         svec.sa_flags   = 0;
429         svec.sa_handler = SIG_DFL;
430         (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL);
431
432         for (i=3; i < FOPEN_MAX; i++) {
433             if ((i != rec->message_pipe[1]) && 
434                 (rec->auth_file && (i != fileno(rec->auth_file))))
435             {
436                 (void) fcntl (i, F_SETFD, 1);
437             }
438         }
439
440         /*
441          * Set the new locale for the child.
442          *
443          * note: the locale hint will be of the form:
444          *
445          *    name_spec[;registry_spec[;ver_spec[;encoding_spec]]]
446          *
447          * for now, just pull out the name_spec (e.g. 'C')
448          * and use it.   With a little work, a more complex
449          * syntax could be understood and the appropriate
450          * actions taken here rather than just wedging
451          * name_spec into setlocale() and hoping.
452          */
453         if ( !(rec->locale_hint) ) {
454             /*
455              * Leave current locale alone.
456              */
457         }
458         else if ( strcmp( rec->locale_hint, "" ) ) {
459             /*
460              * Leave current locale alone.  Note that "" into
461              * setlocale says to go with default vs leave it alone.
462              */
463         }
464         else {
465             char *tptr1, *tptr2;
466
467             tptr1 = xpstrdup( rec->locale_hint );
468             tptr2 = strchr( tptr1, ';' );
469             if (tptr2) *tptr2 = '\0';
470         
471             setlocale( LC_ALL, tptr1 );
472             XFree( tptr1 );
473         }
474
475         /*
476          * Set XAUTHORITY env var if needed.
477          */
478         if ((rec->cookie_cnt) && (rec->auth_file)) {
479             envstr = Xmalloc( strlen(rec->auth_filename) + 12 );
480             sprintf( envstr, "XAUTHORITY=%s", rec->auth_filename );
481             putenv( envstr );
482         }
483
484         /*
485          * Start the child for real.
486          */
487         (void) execvp(rec->pdm_exec_argvs[0], rec->pdm_exec_argvs);
488
489         (void) fprintf (stderr, PDMD_MSG_10, g.prog_name, rec->pdm_exec_argvs[0]);
490
491         /*
492          * tomg - need to deal with failed child start.
493          */
494         exit(PDM_EXIT_ERROR);
495     }
496     else {
497         /*
498          * Parent process.
499          */
500
501         /*
502          * Close the write end of the pipe - only the child needs it.
503          */
504         close(rec->message_pipe[1]);
505         rec->message_pipe[1] = -1;
506     }
507 }
508
509
510 /********************************************************************
511  *
512  * Figure out which pdm executable to later fork/exec.
513  */
514 void mgr_fetch_pdm( XpPdmServiceRec *rec )
515 {
516     char tstr[1024], *tptr1 = NULL, *tptr2, *tptr3;
517     int  firstTime;
518     long now;
519     Display *tdpy;
520     int lxerrno;
521
522     if ( g.override_pdm ) {
523         /*
524          * Override all defaults and other possible settings.
525          */
526         tptr1 = xpstrdup(g.override_pdm);
527     }
528     else {
529         /*
530          * See if the print context says which pdm to run.
531          */
532         g.xerrno = 0;           /* Error Handler */
533         lxerrno = 0;            /* XIO Error Handler */
534
535         if ( setjmp( xio_quickie_jmp_buf ) == 0 ) {
536             XSetIOErrorHandler( xio_quickie_handler );
537
538 #if 0 && defined(PRINTING_SUPPORTED)
539             if ( rec->seldpy_as_printdpy ) {
540                 tptr1 = XpGetOneAttribute( rec->selection_display,
541                                    rec->print_context,
542                                    XPPrinterAttr, "dt-pdm-command" );
543             }
544             else {
545                 tdpy = XOpenDisplay( rec->print_display_str );
546                 if (tdpy) {
547                     tptr1 = XpGetOneAttribute( tdpy,
548                                    rec->print_context,
549                                    XPPrinterAttr, "dt-pdm-command" );
550                     XCloseDisplay( tdpy );
551                 }
552             }
553 #endif /* PRINTING_SUPPORTED */
554
555             XSetIOErrorHandler( NULL );
556         }
557         else {
558             lxerrno = 1;
559
560             XSetIOErrorHandler( NULL );
561         }
562
563         /*
564          * See if we got a useful pdm exec string.  Use
565          * default if not.
566          */
567         if ( g.xerrno || lxerrno ) {
568             rec->pdm_exec_errorcode = g.pdm_start_error;
569             return;
570         }
571         else if (!tptr1) {
572             tptr1 = xpstrdup(g.default_pdm);
573         }
574         else if (!tptr1[0]) {
575             tptr1 = xpstrdup(g.default_pdm);
576         }
577     }
578     
579     /*
580      * Convert pdm-command into argv[] style array.
581      *
582      * Note: this parsing code does NOT respect shell
583      * quotes and other items.   --tomg
584      */
585     rec->pdm_exec_argvs = (char **) NULL;
586
587     tptr2 = tptr1;      /* retain orig pointer for freeing */
588     firstTime = 1;
589
590     while (1) {
591         if (firstTime) {
592             tptr3 = xpstrtok( tptr2, " \n\t" );
593             firstTime = 0;
594
595             if (!tptr3) {
596                 /*
597                  * There were NO useful tokens to begin with, so
598                  * we'll have to fall back on the default.
599                  */
600                 xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup( g.default_pdm ));
601                 break;
602             }
603         }
604         else {
605             tptr3 = xpstrtok( (char *) NULL, " \n\t" );
606         }
607
608         if (tptr3) {
609             xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup( tptr3 ) );
610         }
611         else {
612             break;
613         }
614     }
615     Xfree(tptr1);
616
617     /*
618      * Add standard command line parameters.
619      */
620     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-display") );
621     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(rec->video_display_str) );
622
623     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-window") );
624     sprintf( tstr, "0x%lx", rec->video_window );
625     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(tstr) );
626
627     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-pdisplay") );
628     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(rec->print_display_str) );
629
630     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-pwindow") );
631     sprintf( tstr, "0x%lx", rec->print_window );
632     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(tstr) );
633
634 #if 0 && defined(PRINTING_SUPPORTED)
635     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-pcontext") );
636     sprintf( tstr, "0x%lx", rec->print_context );
637     xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(tstr) );
638 #endif /* PRINTING_SUPPORTED */
639 }
640
641 /********************************************************************
642  *
643  * Once a pdm has been lauched, reply to the SelectionRequest so that
644  * the requestors knows.
645  */
646 void mgr_launch_reply( XpPdmServiceRec *rec )
647 {
648     XEvent reply;
649     Status status;
650     FILE   *errlog;
651     time_t now;
652     char   *eec;
653
654     Atom    tmpa;
655
656
657     XChangeProperty( rec->selection_display, rec->requestor,
658                      rec->prop_atom, XA_ATOM,
659                      32, PropModeReplace,
660                      (unsigned char *) &(rec->pdm_exec_errorcode),
661                      1 );
662
663     /*
664      * Write optional error message to log file.
665      *
666      * Expected errors like PXAUTH and VXAUTH should not have
667      * textual descriptions too - they're obvious as is.  Only
668      * real nasty errors should have a message for the log file.
669      */
670     if ((rec->pdm_exec_errormessage) && (g.log_file)) {
671         if ((errlog = fopen(g.log_file, "a+")) != NULL) {
672             now = time((time_t)0);
673
674             if ( rec->pdm_exec_errorcode == g.pdm_start_ok )
675                 eec = "PDM_START_OK";
676             else if ( rec->pdm_exec_errorcode == g.pdm_start_vxauth )
677                 eec = "PDM_START_VXAUTH";
678             else if ( rec->pdm_exec_errorcode == g.pdm_start_pxauth )
679                 eec = "PDM_START_PXAUTH";
680             else if ( rec->pdm_exec_errorcode == g.pdm_start_error )
681                 eec = "PDM_START_ERROR";
682             else
683                 eec = "unknown error";
684                 
685             fprintf( errlog, PDMD_MSG_11, g.prog_name, ctime(&now),
686                         rec->pdm_exec_errormessage,
687                         eec,
688                         rec->pdm_exec_argvs[0],
689                         rec->print_display_str,
690                         rec->video_display_str );
691
692             fclose(errlog);
693         }
694     }
695
696
697     /*
698      * Send a SelectionNotify event, which will conclude the
699      * selection handshake.
700      */
701     reply.xselection.type      = SelectionNotify;
702     reply.xselection.requestor = rec->requestor;
703     reply.xselection.selection = rec->selection;
704     reply.xselection.target    = g.pdm_start;
705     reply.xselection.property  = rec->prop_atom;
706     reply.xselection.time      = rec->time;
707
708     status = XSendEvent( rec->selection_display, rec->requestor, True, 0, &reply );
709 }
710
711 /********************************************************************
712  *
713  * Send the final OK/CANCEL ClientMessage.
714  */
715 void mgr_shutdown_reply( XpPdmServiceRec *rec )
716 {
717     XEvent cme;
718     Display *pdpy;
719     char *mess;
720     int inc;
721     char buf[2048];
722     int lxerrno;
723
724
725     /*
726      * Setup client message event.
727      */
728     cme.xclient.type         = ClientMessage;
729     cme.xclient.window       = rec->print_window;
730     cme.xclient.message_type = g.pdm_reply;
731     cme.xclient.format       = 32;
732
733     /*
734      * Look at the current exit code.  Let 'mess' be both a message
735      * string, and a string we would XInternAtom on if we find out
736      * that the "Print" X-Server is differnent than the "Selection"
737      * X-Server.  Up till now, we've been using atom constants
738      * generated via the Selection X-Server.
739      */
740     switch (rec->exit_code) {
741         case PDM_EXIT_OK:
742                 cme.xclient.data.l[0] = (long) g.pdm_exit_ok;
743                 mess = "PDM_EXIT_OK";
744                 break;
745         case PDM_EXIT_CANCEL:
746                 cme.xclient.data.l[0] = (long) g.pdm_exit_cancel;
747                 mess = "PDM_EXIT_CANCEL";
748                 break;
749         case PDM_EXIT_VXAUTH:
750                 cme.xclient.data.l[0] = (long) g.pdm_exit_vxauth;
751                 mess = "PDM_EXIT_VXAUTH";
752                 break;
753         case PDM_EXIT_PXAUTH:
754                 cme.xclient.data.l[0] = (long) g.pdm_exit_pxauth;
755                 mess = "PDM_EXIT_PXAUTH";
756                 break;
757         case PDM_EXIT_ERROR:
758         default:
759                 cme.xclient.data.l[0] = (long) g.pdm_exit_error;
760                 mess = "PDM_EXIT_ERROR";
761                 break;
762     }
763
764     /*
765      * Try to send ClientMessage that will carry the reply.
766      */
767
768     g.xerrno = 0;  /* Error Handler */
769     lxerrno = 0;   /* XIO Error Handler */
770
771     if ( setjmp( xio_quickie_jmp_buf ) == 0 ) {
772         XSetIOErrorHandler( xio_quickie_handler );
773
774         if ( rec->seldpy_as_printdpy ) {
775             /*
776              * Since the "Print" X-Server is the same as the
777              * "Selection" X-Server, against which we have an
778              * active display connection and atom values, go
779              * ahead and use it.
780              */
781             XSendEvent( rec->selection_display, rec->print_window, False, 0L, &cme );
782         }
783         else {
784             pdpy = XOpenDisplay( rec->print_display_str );
785             if (pdpy) {
786                 /*
787                  * The "Print" X-Server is different than the
788                  * "Selection" X-Server.  Map values over.
789                  */
790                 cme.xclient.message_type =
791                                 XInternAtom( pdpy, "PDM_REPLY", False );
792                 cme.xclient.data.l[0] =
793                                 (long) XInternAtom( pdpy, mess, False );
794                 XSendEvent( pdpy, rec->print_window, False, 0L, &cme );
795                 XCloseDisplay( pdpy );
796             }
797         }
798
799         XSetIOErrorHandler( NULL );
800     }
801     else {
802         lxerrno = 1;
803
804         XSetIOErrorHandler( NULL );
805     }
806
807     if ( g.xerrno || lxerrno ) {
808         /*
809          * Problem - we can't get back to the requestor.
810          *
811          * This is really a PANIC situation, since the requesting
812          * client will hang around forever, waiting for this
813          * final reply.  The best we can do it log an error for
814          * the sys admin, and hope they can notice.
815          */
816         if (g.xerrno)
817             sprintf( buf, PDMD_MSG_12, g.prog_name, mess );
818         else
819             sprintf( buf, PDMD_MSG_13, g.prog_name, mess );
820
821         rec->message_string2 = Xmalloc( strlen( buf ) + 1 );
822         strcpy( rec->message_string2 , buf );
823     }
824 }
825
826
827 Bool has_exec_token( XpPdmServiceRec *rec )
828 {
829     char *s1, *s2, *s3;
830     int i1;
831
832     s1 = rec->message_string;
833
834     if (s1) {
835         /*
836          * Look for "PDM_START_*" tokens burried in the
837          * stderr output of the PDM.  If found, react as
838          * required, and eliminate the token from the
839          * output.
840          */
841         if ( s2 = strstr( s1, "PDM_START_OK") ) {
842             rec->pdm_exec_errorcode = g.pdm_start_ok;
843             i1 = 12;
844         }
845         else if ( s2 = strstr( s1, "PDM_START_VXAUTH") ) {
846             rec->pdm_exec_errorcode = g.pdm_start_vxauth;
847             i1 = 16;
848         }
849         else if ( s2 = strstr( s1, "PDM_START_PXAUTH") ) {
850             rec->pdm_exec_errorcode = g.pdm_start_pxauth;
851             i1 = 16;
852         }
853         else if ( s2 = strstr( s1, "PDM_START_ERROR") ) {
854             rec->pdm_exec_errorcode = g.pdm_start_error;
855             i1 = 15;
856         }
857
858         if (s2) {
859             /*
860              * Compress out the token.
861              */
862             s3 = s2 + i1;
863             while ( *s2++ = *s3++ );
864
865             if ( strlen(s1) == 0 ) {
866                 /*
867                  * The token was it - free the buffer now so that
868                  * it appears no stderr was ever generated. 
869                  */
870                 Xfree( rec->message_string );
871                 rec->message_string = (char *) NULL;
872             }
873             else if ((strlen(s1) == 1) && (s1[0] == '\n')) {
874                 /*
875                  * All but a \n remains - free the buffer.
876                  */
877                 Xfree( rec->message_string );
878                 rec->message_string = (char *) NULL;
879             }
880
881             return( True );
882         }
883         else {
884             return( False );
885         }
886     }
887     else {
888         return( False );
889     }
890 }
891
892 /********************************************************************
893  *
894  * Search through all the child tracking records and see if
895  * any can be shutdown.
896  */
897 void mgr_shutdown_scan(void)
898 {
899     int        i;
900     time_t     now;
901     FILE       *errlog;
902     static int errlog_problem_notice = 0;
903     Bool       shutdown_time;
904
905     for ( i = 0; i < g.serviceRecNum; i++ ) {
906
907         shutdown_time = False;
908
909         if ( (g.serviceRecs[i]->do_launch_reply) &&
910              (has_exec_token(g.serviceRecs[i]) )    ) {
911             /*
912              * Need to send our PDM_START fork/exec reply still.
913              *
914              * Note that if we send pdm_start_ok, we'll need to send
915              * a pdm_exit_* code later.  If we send pdm_start_vxauth,
916              * pdm_start_pxauth, or pdm_start_error, it indicates to
917              * the client a fatal condition, and they will NOT receive
918              * any more messages, including any pdm_exit_* codes.  The
919              * dtpdmd will hang around however, if for no other reason
920              * than to capture stderr and log it when the child finally
921              * goes down.
922              */
923             mgr_launch_reply( g.serviceRecs[i] );
924             g.serviceRecs[i]->do_launch_reply = False;  /* it has been done */
925         }
926
927         if ( (g.serviceRecs[i]->pdm_exec_errorcode == g.pdm_start_ok) &&
928              (g.serviceRecs[i]->exit_received) &&
929              (g.serviceRecs[i]->message_pipe[0] == -1) ) {
930             /*
931              * Child is down, and since we sent a pdm_start_ok,
932              * we need to send a PDM_REPLY pdm_exit_* code too.
933              */
934             mgr_shutdown_reply( g.serviceRecs[i] );
935         }
936
937         if ( (g.serviceRecs[i]->exit_received) &&
938              (g.serviceRecs[i]->message_pipe[0] == -1) ) {
939             /*
940              * Child is down.  Log any final error messages
941              * from the child, and from the dtpdmd on behalf of
942              * the child.
943              */
944             if ( ((g.serviceRecs[i]->message_string) ||
945                   (g.serviceRecs[i]->message_string2)) && (g.log_file) ) {
946                 if ((errlog = fopen(g.log_file, "a+")) != NULL) {
947                     now = time((time_t)0);
948
949                     fprintf (errlog, PDMD_MSG_14, g.prog_name, ctime(&now),
950                                         g.serviceRecs[i]->pdm_exec_argvs[0],
951                                         g.serviceRecs[i]->print_display_str,
952                                         g.serviceRecs[i]->video_display_str,
953                                         g.serviceRecs[i]->exit_code,
954                                         g.serviceRecs[i]->message_string);
955
956                     if (g.serviceRecs[i]->message_string2) {
957                         fprintf (errlog, PDMD_MSG_15, g.serviceRecs[i]->message_string2);
958                     }
959
960                     fprintf (errlog, "\n");
961                     fclose(errlog);
962                 }
963                 else if (!errlog_problem_notice) {
964                     fprintf (stderr, PDMD_MSG_16, g.prog_name, g.log_file );
965                     fflush (stderr);
966                     errlog_problem_notice = 1;
967                 }
968             }
969
970             /*
971              * The child is done and can be cleaned up.
972              */
973             delete_rec( g.serviceRecs[i] );
974         }
975     }
976 }
977