smproxy.c revision bf2eeab3
1/* $Xorg: smproxy.c,v 1.6 2001/02/09 02:05:35 xorgcvs Exp $ */
2/******************************************************************************
3
4Copyright 1994, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26Author:  Ralph Mor, X Consortium
27******************************************************************************/
28/* $XFree86: xc/programs/smproxy/smproxy.c,v 3.8 2001/10/28 03:34:25 tsi Exp $ */
29
30#include "smproxy.h"
31#include <unistd.h>
32#include <X11/Xmu/WinUtil.h>
33
34static XtAppContext appContext;
35static Display *disp;
36
37static Atom wmProtocolsAtom;
38static Atom wmSaveYourselfAtom;
39static Atom wmStateAtom;
40static Atom smClientIdAtom;
41static Atom wmClientLeaderAtom;
42
43static Bool debug = 0;
44
45static SmcConn proxy_smcConn;
46static XtInputId proxy_iceInputId;
47static char *proxy_clientId = NULL;
48
49WinInfo *win_head = NULL;
50
51static int proxy_count = 0;
52static int die_count = 0;
53
54static Bool ok_to_die = 0;
55
56static Bool caught_error = 0;
57
58static Bool sent_save_done = 0;
59
60static int Argc;
61static char **Argv;
62
63static Bool HasSaveYourself ( Window window );
64static Bool HasXSMPsupport ( Window window );
65static WinInfo * GetClientLeader ( WinInfo *winptr );
66static char * CheckFullyQuantifiedName ( char *name, int *newstring );
67static void FinishSaveYourself ( WinInfo *winInfo, Bool has_WM_SAVEYOURSELF );
68static void SaveYourselfCB ( SmcConn smcConn, SmPointer clientData, int saveType,
69			    Bool shutdown, int interactStyle, Bool fast );
70static void DieCB ( SmcConn smcConn, SmPointer clientData );
71static void SaveCompleteCB ( SmcConn smcConn, SmPointer clientData );
72static void ShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData );
73static void ProcessIceMsgProc ( XtPointer client_data, int *source, XtInputId *id );
74static void NullIceErrorHandler ( IceConn iceConn, Bool swap,
75			   int offendingMinorOpCode,
76			   unsigned long offendingSequence,
77			   int errorClass, int severity, IcePointer values );
78static void ConnectClientToSM ( WinInfo *winInfo );
79static int MyErrorHandler ( Display *display, XErrorEvent *event );
80static Bool LookupWindow ( Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret );
81static WinInfo * AddNewWindow ( Window window );
82static void RemoveWindow ( WinInfo *winptr );
83static void Got_WM_STATE ( WinInfo *winptr );
84static void HandleCreate ( XCreateWindowEvent *event );
85static void HandleDestroy ( XDestroyWindowEvent *event );
86static void HandleUpdate ( XPropertyEvent *event );
87static void ProxySaveYourselfPhase2CB ( SmcConn smcConn, SmPointer clientData );
88static void ProxySaveYourselfCB ( SmcConn smcConn, SmPointer clientData,
89			   int saveType, Bool shutdown, int interactStyle,
90			   Bool fast );
91static void ProxyDieCB ( SmcConn smcConn, SmPointer clientData );
92static void ProxySaveCompleteCB ( SmcConn smcConn, SmPointer clientData );
93static void ProxyShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData );
94static Status ConnectProxyToSM ( char *previous_id );
95static void CheckForExistingWindows ( Window root );
96
97
98static Bool
99HasSaveYourself(Window window)
100{
101    Atom *protocols;
102    int numProtocols;
103    int i, found;
104
105    protocols = NULL;
106
107    if (XGetWMProtocols (disp, window, &protocols, &numProtocols) != True)
108	return (False);
109
110    found = 0;
111
112    if (protocols != NULL)
113    {
114	for (i = 0; i < numProtocols; i++)
115	    if (protocols[i] == wmSaveYourselfAtom)
116		found = 1;
117
118	XFree (protocols);
119    }
120
121    return (found);
122}
123
124
125
126static Bool
127HasXSMPsupport(Window window)
128{
129    XTextProperty tp;
130    Bool hasIt = 0;
131
132    if (XGetTextProperty (disp, window, &tp, smClientIdAtom))
133    {
134	if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
135	    hasIt = 1;
136
137	if (tp.value)
138	    XFree ((char *) tp.value);
139    }
140
141    return (hasIt);
142}
143
144
145
146static WinInfo *
147GetClientLeader(WinInfo *winptr)
148{
149    Atom actual_type;
150    int actual_format;
151    unsigned long nitems, bytesafter;
152    unsigned long *datap = NULL;
153    WinInfo *leader_winptr = NULL;
154    Bool failure = 0;
155
156    if (XGetWindowProperty (disp, winptr->window, wmClientLeaderAtom,
157	0L, 1L, False, AnyPropertyType,	&actual_type, &actual_format,
158	&nitems, &bytesafter, (unsigned char **) &datap) == Success)
159    {
160	if (actual_type == XA_WINDOW && actual_format == 32 &&
161	    nitems == 1 && bytesafter == 0)
162	{
163	    Window leader_win = *((Window *) datap);
164
165	    if (!LookupWindow (leader_win, &leader_winptr, NULL))
166		failure = 1;
167	}
168
169	if (datap)
170	    XFree (datap);
171    }
172
173    if (failure)
174    {
175	/* The client leader was defined, but we couldn't find the window */
176
177	return (NULL);
178    }
179    else if (leader_winptr)
180    {
181	/* We found the real client leader */
182
183	return (leader_winptr);
184    }
185    else
186    {
187	/* There is no client leader defined, return this window */
188
189	return (winptr);
190    }
191}
192
193
194
195static char *
196CheckFullyQuantifiedName(char *name, int *newstring)
197{
198    /*
199     * Due to a bug in Xlib (for hpux in particular), some clients
200     * will have a WM_CLIENT_MACHINE that is not fully quantified.
201     * For example, we might get "excon" instead of "excon.x.org".
202     * This really stinks.  The best we can do is tag on our own
203     * domain name.
204     */
205
206    if (strchr (name, '.') != NULL)
207    {
208	*newstring = 0;
209	return (name);
210    }
211    else
212    {
213	char hostnamebuf[80];
214	char *firstDot;
215
216	gethostname (hostnamebuf, sizeof hostnamebuf);
217	firstDot = strchr (hostnamebuf, '.');
218
219	if (!firstDot)
220	{
221	    *newstring = 0;
222	    return (name);
223	}
224	else
225	{
226	    int bytes = strlen (name) + strlen (firstDot + 1) + 2;
227	    char *newptr;
228
229	    newptr = (char *) malloc (bytes);
230	    sprintf (newptr, "%s.%s", name, firstDot + 1);
231
232	    *newstring = 1;
233	    return (newptr);
234	}
235    }
236}
237
238
239
240static void
241FinishSaveYourself(WinInfo *winInfo, Bool has_WM_SAVEYOURSELF)
242{
243    SmProp prop1, prop2, prop3, *props[3];
244    SmPropValue prop1val, prop2val, prop3val;
245    int i;
246
247    if (!winInfo->got_first_save_yourself)
248    {
249	char userId[20], restartService[80];
250	char *fullyQuantifiedName;
251	int newstring;
252
253	prop1.name = SmProgram;
254	prop1.type = SmARRAY8;
255	prop1.num_vals = 1;
256	prop1.vals = &prop1val;
257	prop1val.value = (SmPointer) winInfo->wm_command[0];
258	prop1val.length = strlen (winInfo->wm_command[0]);
259
260	sprintf (userId, "%ld", (long)getuid());
261	prop2.name = SmUserID;
262	prop2.type = SmARRAY8;
263	prop2.num_vals = 1;
264	prop2.vals = &prop2val;
265	prop2val.value = (SmPointer) userId;
266	prop2val.length = strlen (userId);
267
268	fullyQuantifiedName = CheckFullyQuantifiedName (
269	    (char *) winInfo->wm_client_machine.value, &newstring);
270	sprintf (restartService, "rstart-rsh/%s", fullyQuantifiedName);
271	if (newstring)
272	    free (fullyQuantifiedName);
273
274	prop3.name = "_XC_RestartService";
275	prop3.type = SmLISTofARRAY8;
276	prop3.num_vals = 1;
277	prop3.vals = &prop3val;
278	prop3val.value = (SmPointer) restartService;
279	prop3val.length = strlen (restartService);
280
281	props[0] = &prop1;
282	props[1] = &prop2;
283	props[2] = &prop3;
284
285	SmcSetProperties (winInfo->smc_conn, 3, props);
286
287	winInfo->got_first_save_yourself = 1;
288    }
289
290    prop1.name = SmRestartCommand;
291    prop1.type = SmLISTofARRAY8;
292    prop1.num_vals = winInfo->wm_command_count;
293
294    prop1.vals = (SmPropValue *) malloc (
295	winInfo->wm_command_count * sizeof (SmPropValue));
296
297    if (!prop1.vals)
298    {
299	SmcSaveYourselfDone (winInfo->smc_conn, False);
300	return;
301    }
302
303    for (i = 0; i < winInfo->wm_command_count; i++)
304    {
305	prop1.vals[i].value = (SmPointer) winInfo->wm_command[i];
306	prop1.vals[i].length = strlen (winInfo->wm_command[i]);
307    }
308
309    prop2.name = SmCloneCommand;
310    prop2.type = SmLISTofARRAY8;
311    prop2.num_vals = winInfo->wm_command_count;
312    prop2.vals = prop1.vals;
313
314    props[0] = &prop1;
315    props[1] = &prop2;
316
317    SmcSetProperties (winInfo->smc_conn, 2, props);
318
319    free ((char *) prop1.vals);
320
321    /*
322     * If the client doesn't support WM_SAVE_YOURSELF, we should
323     * return failure for the save, since we really don't know if
324     * the application needed to save state.
325     */
326
327    SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF);
328}
329
330
331
332static void
333SaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
334	       Bool shutdown, int interactStyle, Bool fast)
335{
336    WinInfo *winInfo = (WinInfo *) clientData;
337
338    if (!winInfo->has_save_yourself)
339    {
340	FinishSaveYourself (winInfo, False);
341    }
342    else
343    {
344	XClientMessageEvent saveYourselfMessage;
345
346
347	/* Send WM_SAVE_YOURSELF */
348
349	saveYourselfMessage.type = ClientMessage;
350	saveYourselfMessage.window = winInfo->window;
351	saveYourselfMessage.message_type = wmProtocolsAtom;
352	saveYourselfMessage.format = 32;
353	saveYourselfMessage.data.l[0] = wmSaveYourselfAtom;
354	saveYourselfMessage.data.l[1] = CurrentTime;
355
356	if (XSendEvent (disp, winInfo->window, False, NoEventMask,
357	    (XEvent *) &saveYourselfMessage))
358	{
359	    winInfo->waiting_for_update = 1;
360
361	    if (debug)
362	    {
363		printf ("Sent SAVE YOURSELF to 0x%x\n",
364			(unsigned int)winInfo->window);
365		printf ("\n");
366	    }
367	}
368	else
369	{
370	    if (debug)
371	    {
372		printf ("Failed to send SAVE YOURSELF to 0x%x\n",
373		    (unsigned int)winInfo->window);
374		printf ("\n");
375	    }
376	}
377    }
378}
379
380
381
382static void
383DieCB(SmcConn smcConn, SmPointer clientData)
384{
385    WinInfo *winInfo = (WinInfo *) clientData;
386
387    SmcCloseConnection (winInfo->smc_conn, 0, NULL);
388    winInfo->smc_conn = NULL;
389    XtRemoveInput (winInfo->input_id);
390
391    /* Now tell the client to die */
392
393    if (debug)
394	printf ("Trying to kill 0x%x\n", (unsigned int)winInfo->window);
395
396    XSync (disp, 0);
397    XKillClient (disp, winInfo->window);
398    XSync (disp, 0);
399
400
401    /*
402     * Proxy must exit when all clients die, and the proxy itself
403     * must have received a Die.
404     */
405
406    die_count++;
407
408    if (die_count == proxy_count && ok_to_die)
409    {
410	exit (0);
411    }
412}
413
414
415
416static void
417SaveCompleteCB(SmcConn smcConn, SmPointer clientData)
418{
419    /*
420     * Nothing to do here.
421     */
422}
423
424
425
426static void
427ShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
428{
429    /*
430     * Since we did not request to interact or request save yourself
431     * phase 2, we know we already sent the save yourself done, so
432     * there is nothing to do here.
433     */
434}
435
436
437
438static void
439ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id)
440{
441    IceConn	ice_conn = (IceConn) client_data;
442
443    IceProcessMessages (ice_conn, NULL, NULL);
444}
445
446
447
448static void
449NullIceErrorHandler(IceConn iceConn, Bool swap, int offendingMinorOpcode,
450		    unsigned long offendingSequence, int errorClass,
451		    int severity, IcePointer values)
452{
453    return;
454}
455
456
457static void
458ConnectClientToSM(WinInfo *winInfo)
459{
460    char errorMsg[256];
461    unsigned long mask;
462    SmcCallbacks callbacks;
463    IceConn ice_conn;
464    char *prevId;
465
466    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
467	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
468
469    callbacks.save_yourself.callback = SaveYourselfCB;
470    callbacks.save_yourself.client_data = (SmPointer) winInfo;
471
472    callbacks.die.callback = DieCB;
473    callbacks.die.client_data = (SmPointer) winInfo;
474
475    callbacks.save_complete.callback = SaveCompleteCB;
476    callbacks.save_complete.client_data = (SmPointer) winInfo;
477
478    callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
479    callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo;
480
481    prevId = LookupClientID (winInfo);
482
483    /*
484     * In case a protocol error occurs when opening the connection,
485     * (e.g. an authentication error), we set a null error handler
486     * before the open, then restore the default handler after the open.
487     */
488
489    IceSetErrorHandler (NullIceErrorHandler);
490
491    winInfo->smc_conn = SmcOpenConnection (
492	NULL, 			/* use SESSION_MANAGER env */
493	(SmPointer) winInfo,	/* force a new connection */
494	SmProtoMajor,
495	SmProtoMinor,
496	mask,
497	&callbacks,
498	prevId,
499	&winInfo->client_id,
500	256, errorMsg);
501
502    IceSetErrorHandler (NULL);
503
504    if (winInfo->smc_conn == NULL)
505	return;
506
507    ice_conn = SmcGetIceConnection (winInfo->smc_conn);
508
509    winInfo->input_id = XtAppAddInput (
510	    appContext,
511	    IceConnectionNumber (ice_conn),
512            (XtPointer) XtInputReadMask,
513	    ProcessIceMsgProc,
514	    (XtPointer) ice_conn);
515
516    if (debug)
517    {
518	printf ("Connected to SM, window = 0x%x\n",
519		(unsigned int)winInfo->window);
520	printf ("\n");
521    }
522
523    proxy_count++;
524}
525
526
527
528static int
529MyErrorHandler(Display *display, XErrorEvent *event)
530{
531    caught_error = 1;
532    return 0;
533}
534
535
536
537static Bool
538LookupWindow(Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret)
539{
540    WinInfo *ptr, *prev;
541
542    ptr = win_head;
543    prev = NULL;
544
545    while (ptr)
546    {
547	if (ptr->window == window)
548	    break;
549	else
550	{
551	    prev = ptr;
552	    ptr = ptr->next;
553	}
554    }
555
556    if (ptr)
557    {
558	if (ptr_ret)
559	    *ptr_ret = ptr;
560	if (prev_ptr_ret)
561	    *prev_ptr_ret = prev;
562	return (1);
563    }
564    else
565	return (0);
566}
567
568
569
570static WinInfo *
571AddNewWindow(Window window)
572{
573    WinInfo *newptr;
574
575    if (LookupWindow (window, NULL, NULL))
576	return (NULL);
577
578    newptr = (WinInfo *) malloc (sizeof (WinInfo));
579
580    if (newptr == NULL)
581	return (NULL);
582
583    newptr->next = win_head;
584    win_head = newptr;
585
586    newptr->window = window;
587    newptr->smc_conn = NULL;
588    newptr->tested_for_sm_client_id = 0;
589    newptr->client_id = NULL;
590    newptr->wm_command = NULL;
591    newptr->wm_command_count = 0;
592    newptr->class.res_name = NULL;
593    newptr->class.res_class = NULL;
594    newptr->wm_name = NULL;
595    newptr->wm_client_machine.value = NULL;
596    newptr->wm_client_machine.nitems = 0;
597    newptr->has_save_yourself = 0;
598    newptr->waiting_for_update = 0;
599    newptr->got_first_save_yourself = 0;
600
601    return (newptr);
602}
603
604
605
606static void
607RemoveWindow(WinInfo *winptr)
608{
609    WinInfo *ptr, *prev;
610
611    if (LookupWindow (winptr->window, &ptr, &prev))
612    {
613	if (prev == NULL)
614	    win_head = ptr->next;
615	else
616	    prev->next = ptr->next;
617
618	if (ptr->client_id)
619	    free (ptr->client_id);
620
621	if (ptr->wm_command)
622	    XFreeStringList (ptr->wm_command);
623
624	if (ptr->wm_name)
625	    XFree (ptr->wm_name);
626
627	if (ptr->wm_client_machine.value)
628	    XFree (ptr->wm_client_machine.value);
629
630	if (ptr->class.res_name)
631	    XFree (ptr->class.res_name);
632
633	if (ptr->class.res_class)
634	    XFree (ptr->class.res_class);
635
636	free ((char *) ptr);
637    }
638}
639
640
641
642static void
643Got_WM_STATE(WinInfo *winptr)
644{
645    WinInfo *leader_winptr;
646
647    /*
648     * If we already got WM_STATE and tested for SM_CLIENT_ID, we
649     * shouldn't do it again.
650     */
651
652    if (winptr->tested_for_sm_client_id)
653    {
654	return;
655    }
656
657
658    /*
659     * Set a null error handler, in case this window goes away
660     * behind our back.
661     */
662
663    caught_error = 0;
664    XSetErrorHandler (MyErrorHandler);
665
666
667    /*
668     * Get the client leader window.
669     */
670
671    leader_winptr = GetClientLeader (winptr);
672
673    if (caught_error)
674    {
675	caught_error = 0;
676	RemoveWindow (winptr);
677	XSetErrorHandler (NULL);
678	return;
679    }
680
681
682    /*
683     * If we already checked for SM_CLIENT_ID on the client leader
684     * window, don't do it again.
685     */
686
687    if (!leader_winptr || leader_winptr->tested_for_sm_client_id)
688    {
689	caught_error = 0;
690	XSetErrorHandler (NULL);
691	return;
692    }
693
694    leader_winptr->tested_for_sm_client_id = 1;
695
696    if (!HasXSMPsupport (leader_winptr->window))
697    {
698	XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name);
699
700	XGetCommand (disp, leader_winptr->window,
701	    &leader_winptr->wm_command,
702	    &leader_winptr->wm_command_count);
703
704	XGetClassHint (disp, leader_winptr->window, &leader_winptr->class);
705
706	XGetWMClientMachine (disp, leader_winptr->window,
707	    &leader_winptr->wm_client_machine);
708
709	if (leader_winptr->wm_name != NULL &&
710	    leader_winptr->wm_command != NULL &&
711	    leader_winptr->wm_command_count > 0 &&
712	    leader_winptr->class.res_name != NULL &&
713	    leader_winptr->class.res_class != NULL &&
714	    leader_winptr->wm_client_machine.value != NULL &&
715	    leader_winptr->wm_client_machine.nitems != 0)
716	{
717	    leader_winptr->has_save_yourself =
718		HasSaveYourself (leader_winptr->window);
719
720	    ConnectClientToSM (leader_winptr);
721	}
722    }
723
724    XSync (disp, 0);
725    XSetErrorHandler (NULL);
726
727    if (caught_error)
728    {
729	caught_error = 0;
730	RemoveWindow (leader_winptr);
731    }
732}
733
734
735
736static void
737HandleCreate(XCreateWindowEvent *event)
738{
739    Atom actual_type;
740    int actual_format;
741    unsigned long nitems, bytesafter;
742    unsigned long *datap = NULL;
743    WinInfo *winptr;
744    Bool got_wm_state = 0;
745
746    /*
747     * We are waiting for all proxy connections to close so we can die.
748     * Don't handle new connections.
749     */
750
751    if (ok_to_die)
752	return;
753
754
755    /*
756     * Add the new window
757     */
758
759    if ((winptr = AddNewWindow (event->window)) == NULL)
760	return;
761
762
763    /*
764     * Right after the window was created, it might have been destroyed,
765     * so the following Xlib calls might fail.  Need to catch the error
766     * by installing an error handler.
767     */
768
769    caught_error = 0;
770    XSetErrorHandler (MyErrorHandler);
771
772
773    /*
774     * Select for Property Notify on the window so we can determine
775     * when WM_STATE is defined.  To avoid a race condition, we must
776     * do this _before_ we check for WM_STATE right now.
777     *
778     * Select for Substructure Notify so we can determine when the
779     * window is destroyed.
780     */
781
782    XSelectInput (disp, event->window,
783	SubstructureNotifyMask | PropertyChangeMask);
784
785
786    /*
787     * WM_STATE may already be there.  Check now.
788     */
789
790    if (XGetWindowProperty (disp, event->window, wmStateAtom,
791	0L, 2L, False, AnyPropertyType,
792	&actual_type, &actual_format, &nitems, &bytesafter,
793	(unsigned char **) &datap) == Success && datap)
794    {
795	if (nitems > 0)
796	    got_wm_state = 1;
797
798	if (datap)
799	    XFree ((char *) datap);
800    }
801
802    XSync (disp, 0);
803    XSetErrorHandler (NULL);
804
805    if (caught_error)
806    {
807	caught_error = 0;
808	RemoveWindow (winptr);
809    }
810    else if (got_wm_state)
811    {
812	Got_WM_STATE (winptr);
813    }
814}
815
816
817
818static void
819HandleDestroy(XDestroyWindowEvent *event)
820{
821    WinInfo *winptr;
822
823    if (LookupWindow (event->window, &winptr, NULL))
824    {
825	if (winptr->smc_conn)
826	{
827	    SmcCloseConnection (winptr->smc_conn, 0, NULL);
828	    XtRemoveInput (winptr->input_id);
829	    proxy_count--;
830	}
831
832	if (debug)
833	{
834	    printf ("Removed window (window = 0x%x)\n",
835		    (unsigned int)winptr->window);
836	    printf ("\n");
837	}
838
839	RemoveWindow (winptr);
840    }
841}
842
843
844
845static void
846HandleUpdate(XPropertyEvent *event)
847{
848    Window window = event->window;
849    WinInfo *winptr;
850
851    if (!LookupWindow (window, &winptr, NULL))
852	return;
853
854    if (event->atom == wmStateAtom)
855    {
856	Got_WM_STATE (winptr);
857    }
858    else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update)
859    {
860	/* Finish off the Save Yourself */
861
862	if (winptr->wm_command)
863	{
864	    XFreeStringList (winptr->wm_command);
865	    winptr->wm_command = NULL;
866	    winptr->wm_command_count = 0;
867	}
868
869	XGetCommand (disp, window,
870	    &winptr->wm_command,
871	    &winptr->wm_command_count);
872
873	winptr->waiting_for_update = 0;
874	FinishSaveYourself (winptr, True);
875    }
876}
877
878
879
880static void
881ProxySaveYourselfPhase2CB(SmcConn smcConn, SmPointer clientData)
882{
883    char *filename;
884    Bool success = True;
885    SmProp prop1, prop2, prop3, *props[3];
886    SmPropValue prop1val, prop2val, prop3val;
887    char discardCommand[80];
888    int numVals, i;
889    static int first_time = 1;
890
891    if (first_time)
892    {
893	char userId[20];
894	char hint = SmRestartIfRunning;
895
896	prop1.name = SmProgram;
897	prop1.type = SmARRAY8;
898	prop1.num_vals = 1;
899	prop1.vals = &prop1val;
900	prop1val.value = Argv[0];
901	prop1val.length = strlen (Argv[0]);
902
903	sprintf (userId, "%ld", (long)getuid());
904	prop2.name = SmUserID;
905	prop2.type = SmARRAY8;
906	prop2.num_vals = 1;
907	prop2.vals = &prop2val;
908	prop2val.value = (SmPointer) userId;
909	prop2val.length = strlen (userId);
910
911	prop3.name = SmRestartStyleHint;
912	prop3.type = SmCARD8;
913	prop3.num_vals = 1;
914	prop3.vals = &prop3val;
915	prop3val.value = (SmPointer) &hint;
916	prop3val.length = 1;
917
918	props[0] = &prop1;
919	props[1] = &prop2;
920	props[2] = &prop3;
921
922	SmcSetProperties (smcConn, 3, props);
923
924	first_time = 0;
925    }
926
927    if ((filename = WriteProxyFile ()) == NULL)
928    {
929	success = False;
930	goto finishUp;
931    }
932
933    prop1.name = SmRestartCommand;
934    prop1.type = SmLISTofARRAY8;
935
936    prop1.vals = (SmPropValue *) malloc (
937	(Argc + 4) * sizeof (SmPropValue));
938
939    if (!prop1.vals)
940    {
941	success = False;
942	goto finishUp;
943    }
944
945    numVals = 0;
946
947    for (i = 0; i < Argc; i++)
948    {
949	if (strcmp (Argv[i], "-clientId") == 0 ||
950	    strcmp (Argv[i], "-restore") == 0)
951	{
952	    i++;
953	}
954	else
955	{
956	    prop1.vals[numVals].value = (SmPointer) Argv[i];
957	    prop1.vals[numVals++].length = strlen (Argv[i]);
958	}
959    }
960
961    prop1.vals[numVals].value = (SmPointer) "-clientId";
962    prop1.vals[numVals++].length = 9;
963
964    prop1.vals[numVals].value = (SmPointer) proxy_clientId;
965    prop1.vals[numVals++].length = strlen (proxy_clientId);
966
967    prop1.vals[numVals].value = (SmPointer) "-restore";
968    prop1.vals[numVals++].length = 8;
969
970    prop1.vals[numVals].value = (SmPointer) filename;
971    prop1.vals[numVals++].length = strlen (filename);
972
973    prop1.num_vals = numVals;
974
975
976    sprintf (discardCommand, "rm %s", filename);
977    prop2.name = SmDiscardCommand;
978    prop2.type = SmARRAY8;
979    prop2.num_vals = 1;
980    prop2.vals = &prop2val;
981    prop2val.value = (SmPointer) discardCommand;
982    prop2val.length = strlen (discardCommand);
983
984    props[0] = &prop1;
985    props[1] = &prop2;
986
987    SmcSetProperties (smcConn, 2, props);
988    free ((char *) prop1.vals);
989
990 finishUp:
991
992    SmcSaveYourselfDone (smcConn, success);
993    sent_save_done = 1;
994
995    if (filename)
996	free (filename);
997}
998
999
1000
1001static void
1002ProxySaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
1003		    Bool shutdown, int interactStyle, Bool fast)
1004{
1005    /*
1006     * We want the proxy to respond to the Save Yourself after all
1007     * the regular XSMP clients have finished with the save (and possibly
1008     * interacted with the user).
1009     */
1010
1011    if (!SmcRequestSaveYourselfPhase2 (smcConn,
1012	ProxySaveYourselfPhase2CB, NULL))
1013    {
1014	SmcSaveYourselfDone (smcConn, False);
1015	sent_save_done = 1;
1016    }
1017    else
1018	sent_save_done = 0;
1019}
1020
1021
1022
1023static void
1024ProxyDieCB(SmcConn smcConn, SmPointer clientData)
1025{
1026    SmcCloseConnection (proxy_smcConn, 0, NULL);
1027    XtRemoveInput (proxy_iceInputId);
1028
1029    if (die_count == proxy_count)
1030	exit (0);
1031    else
1032	ok_to_die = 1;
1033}
1034
1035
1036
1037static void
1038ProxySaveCompleteCB(SmcConn smcConn, SmPointer clientData)
1039{
1040    ;
1041}
1042
1043
1044
1045static void
1046ProxyShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
1047{
1048    if (!sent_save_done)
1049    {
1050	SmcSaveYourselfDone (smcConn, False);
1051	sent_save_done = 1;
1052    }
1053}
1054
1055
1056
1057static Status
1058ConnectProxyToSM(char *previous_id)
1059{
1060    char errorMsg[256];
1061    unsigned long mask;
1062    SmcCallbacks callbacks;
1063    IceConn iceConn;
1064
1065    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
1066	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
1067
1068    callbacks.save_yourself.callback = ProxySaveYourselfCB;
1069    callbacks.save_yourself.client_data = (SmPointer) NULL;
1070
1071    callbacks.die.callback = ProxyDieCB;
1072    callbacks.die.client_data = (SmPointer) NULL;
1073
1074    callbacks.save_complete.callback = ProxySaveCompleteCB;
1075    callbacks.save_complete.client_data = (SmPointer) NULL;
1076
1077    callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB;
1078    callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
1079
1080    proxy_smcConn = SmcOpenConnection (
1081	NULL, 			/* use SESSION_MANAGER env */
1082	(SmPointer) appContext,
1083	SmProtoMajor,
1084	SmProtoMinor,
1085	mask,
1086	&callbacks,
1087	previous_id,
1088	&proxy_clientId,
1089	256, errorMsg);
1090
1091    if (proxy_smcConn == NULL)
1092	return (0);
1093
1094    iceConn = SmcGetIceConnection (proxy_smcConn);
1095
1096    proxy_iceInputId = XtAppAddInput (
1097	    appContext,
1098	    IceConnectionNumber (iceConn),
1099            (XtPointer) XtInputReadMask,
1100	    ProcessIceMsgProc,
1101	    (XtPointer) iceConn);
1102
1103    return (1);
1104}
1105
1106
1107
1108static void
1109CheckForExistingWindows(Window root)
1110{
1111    Window dontCare1, dontCare2, *children, client_window;
1112    unsigned int nchildren, i;
1113    XCreateWindowEvent event;
1114
1115    /*
1116     * We query the root tree for all windows created thus far.
1117     * Note that at any moment after XQueryTree is called, a
1118     * window may be deleted.  So we must take extra care to make
1119     * sure a window really exists.
1120     */
1121
1122    XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren);
1123
1124    for (i = 0; i < nchildren; i++)
1125    {
1126	event.window = children[i];
1127
1128	HandleCreate (&event);
1129
1130	caught_error = 0;
1131	XSetErrorHandler (MyErrorHandler);
1132
1133	client_window = XmuClientWindow (disp, children[i]);
1134
1135	XSetErrorHandler (NULL);
1136
1137	if (!caught_error && client_window != children[i])
1138	{
1139	    event.window = client_window;
1140	    HandleCreate (&event);
1141	}
1142    }
1143}
1144
1145
1146
1147int
1148main (int argc, char *argv[])
1149{
1150    char *restore_filename = NULL;
1151    char *client_id = NULL;
1152    int i, zero = 0;
1153
1154    Argc = argc;
1155    Argv = argv;
1156
1157    for (i = 1; i < argc; i++)
1158    {
1159	if (argv[i][0] == '-')
1160	{
1161	    switch (argv[i][1])
1162	    {
1163	      case 'd':				/* -debug */
1164		debug = 1;
1165		continue;
1166
1167	      case 'c':				/* -clientId */
1168		if (++i >= argc) goto usage;
1169		client_id = argv[i];
1170		continue;
1171
1172	      case 'r':				/* -restore */
1173		if (++i >= argc) goto usage;
1174		restore_filename = argv[i];
1175		continue;
1176	    }
1177	}
1178
1179    usage:
1180
1181	fprintf (stderr,
1182	    "usage:  %s [-clientId id] [-restore file] [-debug]\n", argv[0]);
1183	exit (1);
1184    }
1185
1186
1187    XtToolkitInitialize ();
1188    appContext = XtCreateApplicationContext ();
1189
1190    if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY",
1191	NULL, 0, &zero, NULL)))
1192    {
1193	fprintf (stderr, "smproxy: unable to open display\n");
1194	exit (1);
1195    }
1196
1197    if (restore_filename)
1198	ReadProxyFile (restore_filename);
1199
1200    if (!ConnectProxyToSM (client_id))
1201    {
1202	fprintf (stderr, "smproxy: unable to connect to session manager\n");
1203	exit (1);
1204    }
1205
1206    wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False);
1207    wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False);
1208    wmStateAtom = XInternAtom (disp, "WM_STATE", False);
1209    smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False);
1210    wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False);
1211
1212    for (i = 0; i < ScreenCount (disp); i++)
1213    {
1214	Window root = RootWindow (disp, i);
1215	XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask);
1216	CheckForExistingWindows (root);
1217    }
1218
1219    while (1)
1220    {
1221	XEvent event;
1222
1223	XtAppNextEvent (appContext, &event);
1224
1225	switch (event.type)
1226	{
1227	case CreateNotify:
1228	    HandleCreate (&event.xcreatewindow);
1229	    break;
1230
1231	case DestroyNotify:
1232	    HandleDestroy (&event.xdestroywindow);
1233	    break;
1234
1235	case PropertyNotify:
1236	    HandleUpdate (&event.xproperty);
1237	    break;
1238
1239	default:
1240	    XtDispatchEvent (&event);
1241	    break;
1242	}
1243    }
1244    exit(0);
1245}
1246