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