dtwm: basic multihead(xinerama only) support
[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 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 /* $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( edpy, eevent )
159     Display     *edpy;
160     XErrorEvent *eevent;
161 {
162     g.xerrno  = eevent->error_code;
163     g.xerrreq = eevent->request_code;
164     g.xerrmin = eevent->minor_code;
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( argc, argv )
266     int argc;
267     char **argv;
268 {
269     int tscreen;
270     XtInputId xtid;
271     Window sel_window;
272     Display *sel_display;
273     char *display_str, *auth_file;
274     Bool security_flag;
275     XEvent report;
276     struct sigaction svec;
277
278
279     g.serviceRecNum = 0;
280     g.maxServiceRecNum = 0;
281
282     g.prog_name = argv[0];
283     g.alt_selection = "PDM_MANAGER";
284     g.default_pdm = DEFAULT_PDM_EXECUTABLE;
285     g.override_pdm = (char *) NULL;
286     display_str = getenv("DISPLAY");
287     auth_file = (char *) NULL;
288     g.log_file  = (char *) NULL;
289     security_flag  = False;
290
291     /*
292      * Parse command line arguments.
293      */
294     while (*++argv) {
295         if (!strcmp (*argv, "-a")) {
296             g.alt_selection = *++argv;
297         }
298         else if (!strncmp (*argv, "-d", 2)) {
299             display_str = *++argv;
300         }
301         else if (!strcmp (*argv, "-p")) {
302             g.default_pdm = *++argv;
303         }
304         else if (!strcmp (*argv, "-P")) {
305             g.override_pdm = *++argv;
306         }
307         else if (!strcmp (*argv, "-s")) {
308             security_flag = True;
309         }
310         else if (!strcmp (*argv, "-f")) {
311             auth_file = *++argv;
312         }
313         else if (!strcmp (*argv, "-l")) {
314             g.log_file = *++argv;
315         }
316         else if (!strncmp (*argv, "-h", 2)) {
317             pusage( g.prog_name );
318             exit(1);
319         }
320         else {
321             /*
322              * Ignore unknown options.
323              */
324         }
325     }
326
327
328     /*
329      * Open a connection to the X-Server.
330      */
331     XtToolkitInitialize ();
332     g.context = XtCreateApplicationContext();
333     if ( (sel_display = XtOpenDisplay( g.context, display_str,
334                                         "dtpdmd", "Dtpdmd",
335                                         0, 0, &argc, argv )) == NULL ) {
336         fprintf( stderr , "\n" );
337         fprintf( stderr , PDMD_MSG_2, g.prog_name, display_str );
338         fprintf( stderr , "\n" );
339         fprintf( stderr , "\n" );
340         exit(0);
341     }
342
343     /*
344      * Create master PDM window upon which selections
345      * will be created.
346      */
347     tscreen = DefaultScreen( sel_display );
348
349     sel_window = XCreateSimpleWindow( sel_display,
350                                    DefaultRootWindow( sel_display ),
351                                    0, 0, 1, 1, 1,
352                                    BlackPixel(sel_display, tscreen),
353                                    WhitePixel(sel_display, tscreen) );
354
355     /*
356      * Setup PDM_MANAGER selection
357      */
358     if ( ! _PdmMgrSetup( sel_display, sel_window, security_flag ) ) {
359         fprintf( stderr , "\n" );
360         fprintf( stderr , PDMD_MSG_3, g.prog_name , g.alt_selection );
361         fprintf( stderr , "\n" );
362         fprintf( stderr , "\n" );
363         exit(0);
364     }
365
366
367     /*
368      * Install the "Xt Signal Pipe" Kicker handler.
369      */
370     if ( pipe(xtkick_pipeG) == -1 ) {
371         fprintf( stderr , "\n" );
372         fprintf( stderr , PDMD_MSG_4, g.prog_name );
373         fprintf( stderr , "\n" );
374         fprintf( stderr , "\n" );
375         exit(0);
376     }
377     xtid = XtAppAddInput( g.context, xtkick_pipeG[0],
378                           (XtPointer) XtInputReadMask,
379                           xtkick_proc, (XtPointer) NULL );
380
381     /*
382      * Install signal handers.
383      */
384     sigemptyset(&svec.sa_mask);
385     svec.sa_flags   = 0;
386     svec.sa_handler = handle_SIGCLD;
387     (void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL);
388
389     /*
390      * After this point, we need to trap all X and XIO errors.
391      *
392      * XIO trap handlers are installed at critical points, and
393      * the following generic X trap handler is used to set
394      * globals.
395      */
396     XSetErrorHandler( generic_error_handler );
397
398     /*
399      * MASTER EVENT LOOP
400      */
401     while (1) {
402         /*
403          * XtAppNextEvent breaks for several reasons:
404          *    - X event received
405          *
406          * XtAppNextEvent will dispatch for several reasons:
407          *    - Alt input occurs on a pdm message pipe
408          *    - Alt input occurs because a pdm message pipe was disconnected
409          *    - Alt input occurs on our "Xt Signal Pipe" and we need to
410          *      rediscover what happened as a result of that signal.
411          */
412         XtAppNextEvent( g.context, &report );
413         switch (report.type) {
414
415             case ClientMessage:
416                 if (report.xclient.message_type == g.pdm_mail) {
417                     dispatch_mbox( &report );
418                 }
419                 else {
420                     /* ignore/pitch the event */
421                 }
422                 break;
423
424             case SelectionRequest:
425                 if (report.xselectionrequest.selection == g.pdm_selection) {
426                     if (report.xselectionrequest.target == g.pdm_start) {
427                         dispatch_mgr( &report );
428                     }
429                     else if (report.xselectionrequest.target==g.pdm_targets) {
430                         dispatch_targets( &report );
431                     }
432                     else if (report.xselectionrequest.target==g.pdm_multiple){
433                         dispatch_multiple( &report );
434                     }
435                     else if (report.xselectionrequest.target==g.pdm_timestamp){
436                         dispatch_timestamp( &report );
437                     }
438                     else if (report.xselectionrequest.target==g.pdm_mbox) {
439                         dispatch_mbox( &report );
440                     }
441                     else {
442                         dispatch_not_supported( &report );
443                     }
444                 }
445                 else {
446                     /* ignore/pitch the event */
447                 }
448                 break;
449
450             case SelectionNotify:
451                 /* pitch the event */
452                 break;
453
454             case SelectionClear:
455                 /*
456                  * Someone is trying to tear away the selection.
457                  * REACT by trying to reclaim it and logging an
458                  * error.
459                  */
460                 break;
461
462             default:
463                 break;
464         }
465
466         /*
467          * Use the opportunity to check for freshly finished
468          * PDMs, and close them out.
469          */
470         mgr_shutdown_scan();
471     }
472
473     XDestroyWindow( sel_display, sel_window );
474
475     XtCloseDisplay( sel_display );
476
477     exit(0);
478 }
479