Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtpdmd / dtpdmd.c
1 /* $XConsortium: dtpdmd.c /main/9 1996/10/30 19:10:08 cde-hp $ */
2
3 /******************************************************************************
4  ******************************************************************************
5  **
6  ** File:         dtpdmd.c
7  **
8  ** Description:  main file for the implementation of the dtpdmd.
9  **
10  ** (c) Copyright 1995, 1996 Hewlett-Packard Company, all rights reserved.
11  **
12  ******************************************************************************
13  *****************************************************************************/
14
15 /******************************************************************************
16  *
17  * BASIC dtpdmd FLOW
18  *
19  * Other than setup, one event loop runs the show.
20  *
21  * Legend:
22  *
23  *    ->  means XtAppNextEvent breaks out with an event, and a
24  *        case statement figures out where the event should go.
25  *
26  *    =>  means XtAppNextEvent dispatches the handler for
27  *        the event directly.
28  *
29  * XtAppNextEvent()
30  *
31  *    -> dispatch_mgr
32  *          o find_rec              }
33  *          o mgr_initialize        }  mgr_launch_reply and delete_rec
34  *          o mgr_fetch_pdm         }  called as soon as any errors occur
35  *          o mgr_launch_pdm        }
36  *          o mgr_shutdown_scan     }
37  *
38  *    -> dispatch_mbox
39  *          o find_rec              }
40  *          o mbox_initialize       }  request for
41  *          o mbox_build            }  mailbox
42  *          o mbox_reply            }
43  *          o mgr_shutdown_scan     }
44  *       or
45  *          o find_rec_by_mbox_win  }  receive incoming
46  *          o mbox_receive          }  mail
47  *          o mgr_shutdown_scan     }
48  *
49  *    => xtkick_proc (SIGCLD write to pipe handler causes this)
50  *          o mgr_shutdown_scan
51  *
52  *    => message_pipe_handler
53  *          o child's stderr to pipe to malloc'ed buffer
54  *          o trap EOF on pipe
55  *          o mgr_shutdown_scan
56  *
57  *    ~> SIGCLD (not really dispatched)
58  *          o the handler notes which child, and exit status,
59  *            then writes a byte to tickle the xtkick_proc
60  *
61  * Note the final usage of:
62  *
63  *    mgr_shutdown_scan
64  *       o possibly pdm_launch_reply
65  *       o possibly pdm_shutdown_reply
66  *       o as appropriate, delete_rec
67  */
68
69 /******************************************************************************
70  *
71  * XIO Error Handling Strategy:
72  *
73  * XIO errors can occur from up to 3 display connections:
74  *
75  *    o sel_dpy (aka prop_dpy)
76  *         - always active.  If this display connection goes down,
77  *           then the dtpdmd will not be able to service any more
78  *           pdm requests.
79  *
80  *    o print_dpy (usually equal to sel_dpy)
81  *         - within the pdm, on a per client basis, only active
82  *           long enough to 1) fetch the dt-pdm-command attribute,
83  *           and to 2) send the final ClientMessage with OK/Cancel.
84  *
85  *           XIO strategy: wrap setjmp/longjmp around the usages
86  *           of XOpenDisplay on print_dpy.
87  *
88  *           XIO result: For case #1, set dt-pdm-command using
89  *           built in defaults, thus ignoring XpGetOneAttribute.
90  *
91  *           For case #2, the best that can be done is to log
92  *           a message to the errorlog.
93  *
94  *    o video_dpy
95  *         - within the pdm, on a per client basis, only active
96  *           long enough to 1) run a test connection to verify
97  *           authorization.
98  *
99  *           XIO strategy: wrap setjmp/longjmp around the one
100  *           XOpenDisplay of video_dpy.
101  *
102  *           XIO result: act as if authorization failed.
103  */
104 #define DTPDMD_DOT_C
105
106 #include "dtpdmdP.h"
107 #include "nlmsg.h"
108
109 /********************************************************************
110  *
111  * Globals.
112  */
113 XpPdmGlobals g;                 /* global to all modules */
114
115 static int xtkick_pipeG[2];     /* global to this module */
116
117 /********************************************************************
118  *
119  * pusage
120  */
121 static void pusage( char *prog_name )
122 {
123     fprintf(stderr, "\n");
124     fprintf( stderr, PDMD_MSG_1, prog_name, "PDM_MANAGER", DEFAULT_PDM_EXECUTABLE );
125     fprintf(stderr, "\n");
126     fprintf(stderr, "\n");
127 }
128
129
130 /******************************************************************************
131  *
132  * generic_error_handler
133  *
134  *****************************************************************************/
135
136 static int generic_error_handler( edpy, eevent )
137     Display     *edpy;
138     XErrorEvent *eevent;
139 {
140     g.xerrno  = eevent->error_code;
141     g.xerrreq = eevent->request_code;
142     g.xerrmin = eevent->minor_code;
143 }
144
145 /******************************************************************************
146  *
147  * handle_SIGCLD
148  *
149  *****************************************************************************/
150 static void
151 #if defined(__aix)
152 handle_SIGCLD(int sigNum)
153 #else
154 handle_SIGCLD(void)
155 #endif /* __aix */
156 {
157     int exitStatus, i;
158     pid_t pid;
159
160     /*
161      * Query why the SIGCLD happened - a true termination or just
162      * a stopage.  We only care about terminations.
163      */
164     pid = wait(&exitStatus);
165
166     if (pid == -1) {
167         /*
168          * No specific child found with wait() - punt.
169          */
170     }
171     else if (!WIFSTOPPED(exitStatus)) {
172         /*
173          * The SIGCLD was *not* the result of being stopped, so the child
174          * has indeed terminated.  Look for its tracking record and mark
175          * accordingly for later shutdown.
176          */
177         for ( i = 0; i < g.serviceRecNum; i++ ) {
178             if ( g.serviceRecs[i]->pid == pid ) {
179                 /*
180                  * Update tracking record.
181                  */
182                 g.serviceRecs[i]->exit_received = True;
183                 g.serviceRecs[i]->exit_code = (int) WEXITSTATUS(exitStatus);
184
185                 /*
186                  * Figure out who will deliver the "tickle" to XtAppNextEvent
187                  * that will allow this child's status to be rediscovered
188                  * so that a subsequent shutdown can be done.
189                  *
190                  *    - if the child's message_pipe is still up,
191                  *      let its upcoming disconnection be the tickle.
192                  *
193                  *    - if the child's message pipe is aleady down,
194                  *      then this is our last event related to the
195                  *      child, so tickle the "Xt Signal Pipe" we
196                  *      installed.
197                  *
198                  *    - if the Xt Signal Pipe is down, then we'll have
199                  *      to rely upon a 3rd party to deliver an event
200                  *      that will reactivate the Xt Main Loop.
201                  */
202                 if ( g.serviceRecs[i]->message_pipe[0] == -1 ) {
203                     /*
204                      * Tickle our "Xt Signal Pipe".
205                      */
206                     write( xtkick_pipeG[1], (char *) "", 1 );
207                 }
208
209                 break;
210             }
211         }
212     }
213 }
214
215 static void xtkick_proc( XtPointer w, int *source, XtInputId *id)
216 {
217     char buffer[128];
218
219     /*
220      * Drain off the kicker bytes.  Don't care how many, since
221      * we'll look at all client records.
222      */
223     if ( read(*source, buffer, sizeof (buffer) - 1) <= 0 ) {
224         close (*source);   /* aka xtkick_pipeG[0] */
225         XtRemoveInput (*id);
226
227         xtkick_pipeG[0] = -1;   /* mark the pipe as invalid */
228         xtkick_pipeG[1] = -1;
229     }
230
231     /*
232      * Scan client records and see who is ready to be
233      * shut down.
234      */
235     mgr_shutdown_scan();
236 }
237
238 /******************************************************************************
239  *
240  * main
241  */
242 main( argc, argv )
243     int argc;
244     char **argv;
245 {
246     int tscreen;
247     XtInputId xtid;
248     Window sel_window;
249     Display *sel_display;
250     char *display_str, *auth_file;
251     Bool security_flag;
252     XEvent report;
253     struct sigaction svec;
254
255
256     g.serviceRecNum = 0;
257     g.maxServiceRecNum = 0;
258
259     g.prog_name = argv[0];
260     g.alt_selection = "PDM_MANAGER";
261     g.default_pdm = DEFAULT_PDM_EXECUTABLE;
262     g.override_pdm = (char *) NULL;
263     display_str = getenv("DISPLAY");
264     auth_file = (char *) NULL;
265     g.log_file  = (char *) NULL;
266     security_flag  = False;
267
268     /*
269      * Parse command line arguments.
270      */
271     while (*++argv) {
272         if (!strcmp (*argv, "-a")) {
273             g.alt_selection = *++argv;
274         }
275         else if (!strncmp (*argv, "-d", 2)) {
276             display_str = *++argv;
277         }
278         else if (!strcmp (*argv, "-p")) {
279             g.default_pdm = *++argv;
280         }
281         else if (!strcmp (*argv, "-P")) {
282             g.override_pdm = *++argv;
283         }
284         else if (!strcmp (*argv, "-s")) {
285             security_flag = True;
286         }
287         else if (!strcmp (*argv, "-f")) {
288             auth_file = *++argv;
289         }
290         else if (!strcmp (*argv, "-l")) {
291             g.log_file = *++argv;
292         }
293         else if (!strncmp (*argv, "-h", 2)) {
294             pusage( g.prog_name );
295             exit(1);
296         }
297         else {
298             /*
299              * Ignore unknown options.
300              */
301         }
302     }
303
304
305     /*
306      * Open a connection to the X-Server.
307      */
308     XtToolkitInitialize ();
309     g.context = XtCreateApplicationContext();
310     if ( (sel_display = XtOpenDisplay( g.context, display_str,
311                                         "dtpdmd", "Dtpdmd",
312                                         0, 0, &argc, argv )) == NULL ) {
313         fprintf( stderr , "\n" );
314         fprintf( stderr , PDMD_MSG_2, g.prog_name, display_str );
315         fprintf( stderr , "\n" );
316         fprintf( stderr , "\n" );
317         exit(0);
318     }
319
320     /*
321      * Create master PDM window upon which selections
322      * will be created.
323      */
324     tscreen = DefaultScreen( sel_display );
325
326     sel_window = XCreateSimpleWindow( sel_display,
327                                    DefaultRootWindow( sel_display ),
328                                    0, 0, 1, 1, 1,
329                                    BlackPixel(sel_display, tscreen),
330                                    WhitePixel(sel_display, tscreen) );
331
332     /*
333      * Setup PDM_MANAGER selection
334      */
335     if ( ! _PdmMgrSetup( sel_display, sel_window, security_flag ) ) {
336         fprintf( stderr , "\n" );
337         fprintf( stderr , PDMD_MSG_3, g.prog_name , g.alt_selection );
338         fprintf( stderr , "\n" );
339         fprintf( stderr , "\n" );
340         exit(0);
341     }
342
343
344     /*
345      * Install the "Xt Signal Pipe" Kicker handler.
346      */
347     if ( pipe(xtkick_pipeG) == -1 ) {
348         fprintf( stderr , "\n" );
349         fprintf( stderr , PDMD_MSG_4, g.prog_name );
350         fprintf( stderr , "\n" );
351         fprintf( stderr , "\n" );
352         exit(0);
353     }
354     xtid = XtAppAddInput( g.context, xtkick_pipeG[0],
355                           (XtPointer) XtInputReadMask,
356                           xtkick_proc, (XtPointer) NULL );
357
358     /*
359      * Install signal handers.
360      */
361     sigemptyset(&svec.sa_mask);
362     svec.sa_flags   = 0;
363     svec.sa_handler = handle_SIGCLD;
364     (void) sigaction(SIGCLD, &svec, (struct sigaction *) NULL);
365
366     /*
367      * After this point, we need to trap all X and XIO errors.
368      *
369      * XIO trap handlers are installed at critical points, and
370      * the following generic X trap handler is used to set
371      * globals.
372      */
373     XSetErrorHandler( generic_error_handler );
374
375     /*
376      * MASTER EVENT LOOP
377      */
378     while (1) {
379         /*
380          * XtAppNextEvent breaks for several reasons:
381          *    - X event received
382          *
383          * XtAppNextEvent will dispatch for several reasons:
384          *    - Alt input occurs on a pdm message pipe
385          *    - Alt input occurs because a pdm message pipe was disconnected
386          *    - Alt input occurs on our "Xt Signal Pipe" and we need to
387          *      rediscover what happened as a result of that signal.
388          */
389         XtAppNextEvent( g.context, &report );
390         switch (report.type) {
391
392             case ClientMessage:
393                 if (report.xclient.message_type == g.pdm_mail) {
394                     dispatch_mbox( &report );
395                 }
396                 else {
397                     /* ignore/pitch the event */
398                 }
399                 break;
400
401             case SelectionRequest:
402                 if (report.xselectionrequest.selection == g.pdm_selection) {
403                     if (report.xselectionrequest.target == g.pdm_start) {
404                         dispatch_mgr( &report );
405                     }
406                     else if (report.xselectionrequest.target==g.pdm_targets) {
407                         dispatch_targets( &report );
408                     }
409                     else if (report.xselectionrequest.target==g.pdm_multiple){
410                         dispatch_multiple( &report );
411                     }
412                     else if (report.xselectionrequest.target==g.pdm_timestamp){
413                         dispatch_timestamp( &report );
414                     }
415                     else if (report.xselectionrequest.target==g.pdm_mbox) {
416                         dispatch_mbox( &report );
417                     }
418                     else {
419                         dispatch_not_supported( &report );
420                     }
421                 }
422                 else {
423                     /* ignore/pitch the event */
424                 }
425                 break;
426
427             case SelectionNotify:
428                 /* pitch the event */
429                 break;
430
431             case SelectionClear:
432                 /*
433                  * Someone is trying to tear away the selection.
434                  * REACT by trying to reclaim it and logging an
435                  * error.
436                  */
437                 break;
438
439             default:
440                 break;
441         }
442
443         /*
444          * Use the opportunity to check for freshly finished
445          * PDMs, and close them out.
446          */
447         mgr_shutdown_scan();
448     }
449
450     XDestroyWindow( sel_display, sel_window );
451
452     XtCloseDisplay( sel_display );
453
454     exit(0);
455 }
456