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