smproxy.c revision 24047306
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	    char *newptr;
225
226	    if (asprintf (&newptr, "%s.%s", name, firstDot + 1) == -1) {
227                *newstring = 0;
228                return NULL;
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	snprintf (userId, sizeof(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	snprintf (restartService, sizeof(restartService),
269                  "rstart-rsh/%s", fullyQuantifiedName);
270	if (newstring)
271	    free (fullyQuantifiedName);
272
273	prop3.name = "_XC_RestartService";
274	prop3.type = SmLISTofARRAY8;
275	prop3.num_vals = 1;
276	prop3.vals = &prop3val;
277	prop3val.value = (SmPointer) restartService;
278	prop3val.length = strlen (restartService);
279
280	props[0] = &prop1;
281	props[1] = &prop2;
282	props[2] = &prop3;
283
284	SmcSetProperties (winInfo->smc_conn, 3, props);
285
286	winInfo->got_first_save_yourself = 1;
287    }
288
289    prop1.name = SmRestartCommand;
290    prop1.type = SmLISTofARRAY8;
291    prop1.num_vals = winInfo->wm_command_count;
292
293    prop1.vals = (SmPropValue *) malloc (
294	winInfo->wm_command_count * sizeof (SmPropValue));
295
296    if (!prop1.vals)
297    {
298	SmcSaveYourselfDone (winInfo->smc_conn, False);
299	return;
300    }
301
302    for (i = 0; i < winInfo->wm_command_count; i++)
303    {
304	prop1.vals[i].value = (SmPointer) winInfo->wm_command[i];
305	prop1.vals[i].length = strlen (winInfo->wm_command[i]);
306    }
307
308    prop2.name = SmCloneCommand;
309    prop2.type = SmLISTofARRAY8;
310    prop2.num_vals = winInfo->wm_command_count;
311    prop2.vals = prop1.vals;
312
313    props[0] = &prop1;
314    props[1] = &prop2;
315
316    SmcSetProperties (winInfo->smc_conn, 2, props);
317
318    free ((char *) prop1.vals);
319
320    /*
321     * If the client doesn't support WM_SAVE_YOURSELF, we should
322     * return failure for the save, since we really don't know if
323     * the application needed to save state.
324     */
325
326    SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF);
327}
328
329
330
331static void
332SaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
333	       Bool shutdown, int interactStyle, Bool fast)
334{
335    WinInfo *winInfo = (WinInfo *) clientData;
336
337    if (!winInfo->has_save_yourself)
338    {
339	FinishSaveYourself (winInfo, False);
340    }
341    else
342    {
343	XClientMessageEvent saveYourselfMessage;
344
345
346	/* Send WM_SAVE_YOURSELF */
347
348	saveYourselfMessage.type = ClientMessage;
349	saveYourselfMessage.window = winInfo->window;
350	saveYourselfMessage.message_type = wmProtocolsAtom;
351	saveYourselfMessage.format = 32;
352	saveYourselfMessage.data.l[0] = wmSaveYourselfAtom;
353	saveYourselfMessage.data.l[1] = CurrentTime;
354
355	if (XSendEvent (disp, winInfo->window, False, NoEventMask,
356	    (XEvent *) &saveYourselfMessage))
357	{
358	    winInfo->waiting_for_update = 1;
359
360	    if (debug)
361	    {
362		printf ("Sent SAVE YOURSELF to 0x%x\n",
363			(unsigned int)winInfo->window);
364		printf ("\n");
365	    }
366	}
367	else
368	{
369	    if (debug)
370	    {
371		printf ("Failed to send SAVE YOURSELF to 0x%x\n",
372		    (unsigned int)winInfo->window);
373		printf ("\n");
374	    }
375	}
376    }
377}
378
379
380
381static void
382DieCB(SmcConn smcConn, SmPointer clientData)
383{
384    WinInfo *winInfo = (WinInfo *) clientData;
385
386    SmcCloseConnection (winInfo->smc_conn, 0, NULL);
387    winInfo->smc_conn = NULL;
388    XtRemoveInput (winInfo->input_id);
389
390    /* Now tell the client to die */
391
392    if (debug)
393	printf ("Trying to kill 0x%x\n", (unsigned int)winInfo->window);
394
395    XSync (disp, 0);
396    XKillClient (disp, winInfo->window);
397    XSync (disp, 0);
398
399
400    /*
401     * Proxy must exit when all clients die, and the proxy itself
402     * must have received a Die.
403     */
404
405    die_count++;
406
407    if (die_count == proxy_count && ok_to_die)
408    {
409	exit (0);
410    }
411}
412
413
414
415static void
416SaveCompleteCB(SmcConn smcConn, SmPointer clientData)
417{
418    /*
419     * Nothing to do here.
420     */
421}
422
423
424
425static void
426ShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
427{
428    /*
429     * Since we did not request to interact or request save yourself
430     * phase 2, we know we already sent the save yourself done, so
431     * there is nothing to do here.
432     */
433}
434
435
436
437static void
438ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id)
439{
440    IceConn	ice_conn = (IceConn) client_data;
441
442    IceProcessMessages (ice_conn, NULL, NULL);
443}
444
445
446
447static void
448NullIceErrorHandler(IceConn iceConn, Bool swap, int offendingMinorOpcode,
449		    unsigned long offendingSequence, int errorClass,
450		    int severity, IcePointer values)
451{
452    return;
453}
454
455
456static void
457ConnectClientToSM(WinInfo *winInfo)
458{
459    char errorMsg[256];
460    unsigned long mask;
461    SmcCallbacks callbacks;
462    IceConn ice_conn;
463    char *prevId;
464
465    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
466	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
467
468    callbacks.save_yourself.callback = SaveYourselfCB;
469    callbacks.save_yourself.client_data = (SmPointer) winInfo;
470
471    callbacks.die.callback = DieCB;
472    callbacks.die.client_data = (SmPointer) winInfo;
473
474    callbacks.save_complete.callback = SaveCompleteCB;
475    callbacks.save_complete.client_data = (SmPointer) winInfo;
476
477    callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
478    callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo;
479
480    prevId = LookupClientID (winInfo);
481
482    /*
483     * In case a protocol error occurs when opening the connection,
484     * (e.g. an authentication error), we set a null error handler
485     * before the open, then restore the default handler after the open.
486     */
487
488    IceSetErrorHandler (NullIceErrorHandler);
489
490    winInfo->smc_conn = SmcOpenConnection (
491	NULL, 			/* use SESSION_MANAGER env */
492	(SmPointer) winInfo,	/* force a new connection */
493	SmProtoMajor,
494	SmProtoMinor,
495	mask,
496	&callbacks,
497	prevId,
498	&winInfo->client_id,
499	256, errorMsg);
500
501    IceSetErrorHandler (NULL);
502
503    if (winInfo->smc_conn == NULL)
504	return;
505
506    ice_conn = SmcGetIceConnection (winInfo->smc_conn);
507
508    winInfo->input_id = XtAppAddInput (
509	    appContext,
510	    IceConnectionNumber (ice_conn),
511            (XtPointer) XtInputReadMask,
512	    ProcessIceMsgProc,
513	    (XtPointer) ice_conn);
514
515    if (debug)
516    {
517	printf ("Connected to SM, window = 0x%x\n",
518		(unsigned int)winInfo->window);
519	printf ("\n");
520    }
521
522    proxy_count++;
523}
524
525
526
527static int
528MyErrorHandler(Display *display, XErrorEvent *event)
529{
530    caught_error = 1;
531    return 0;
532}
533
534
535
536static Bool
537LookupWindow(Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret)
538{
539    WinInfo *ptr, *prev;
540
541    ptr = win_head;
542    prev = NULL;
543
544    while (ptr)
545    {
546	if (ptr->window == window)
547	    break;
548	else
549	{
550	    prev = ptr;
551	    ptr = ptr->next;
552	}
553    }
554
555    if (ptr)
556    {
557	if (ptr_ret)
558	    *ptr_ret = ptr;
559	if (prev_ptr_ret)
560	    *prev_ptr_ret = prev;
561	return (1);
562    }
563    else
564	return (0);
565}
566
567
568
569static WinInfo *
570AddNewWindow(Window window)
571{
572    WinInfo *newptr;
573
574    if (LookupWindow (window, NULL, NULL))
575	return (NULL);
576
577    newptr = (WinInfo *) malloc (sizeof (WinInfo));
578
579    if (newptr == NULL)
580	return (NULL);
581
582    newptr->next = win_head;
583    win_head = newptr;
584
585    newptr->window = window;
586    newptr->smc_conn = NULL;
587    newptr->tested_for_sm_client_id = 0;
588    newptr->client_id = NULL;
589    newptr->wm_command = NULL;
590    newptr->wm_command_count = 0;
591    newptr->class.res_name = NULL;
592    newptr->class.res_class = NULL;
593    newptr->wm_name = NULL;
594    newptr->wm_client_machine.value = NULL;
595    newptr->wm_client_machine.nitems = 0;
596    newptr->has_save_yourself = 0;
597    newptr->waiting_for_update = 0;
598    newptr->got_first_save_yourself = 0;
599
600    return (newptr);
601}
602
603
604
605static void
606RemoveWindow(WinInfo *winptr)
607{
608    WinInfo *ptr, *prev;
609
610    if (LookupWindow (winptr->window, &ptr, &prev))
611    {
612	if (prev == NULL)
613	    win_head = ptr->next;
614	else
615	    prev->next = ptr->next;
616
617	if (ptr->client_id)
618	    free (ptr->client_id);
619
620	if (ptr->wm_command)
621	    XFreeStringList (ptr->wm_command);
622
623	if (ptr->wm_name)
624	    XFree (ptr->wm_name);
625
626	if (ptr->wm_client_machine.value)
627	    XFree (ptr->wm_client_machine.value);
628
629	if (ptr->class.res_name)
630	    XFree (ptr->class.res_name);
631
632	if (ptr->class.res_class)
633	    XFree (ptr->class.res_class);
634
635	free ((char *) ptr);
636    }
637}
638
639
640
641static void
642Got_WM_STATE(WinInfo *winptr)
643{
644    WinInfo *leader_winptr;
645
646    /*
647     * If we already got WM_STATE and tested for SM_CLIENT_ID, we
648     * shouldn't do it again.
649     */
650
651    if (winptr->tested_for_sm_client_id)
652    {
653	return;
654    }
655
656
657    /*
658     * Set a null error handler, in case this window goes away
659     * behind our back.
660     */
661
662    caught_error = 0;
663    XSetErrorHandler (MyErrorHandler);
664
665
666    /*
667     * Get the client leader window.
668     */
669
670    leader_winptr = GetClientLeader (winptr);
671
672    if (caught_error)
673    {
674	caught_error = 0;
675	RemoveWindow (winptr);
676	XSetErrorHandler (NULL);
677	return;
678    }
679
680
681    /*
682     * If we already checked for SM_CLIENT_ID on the client leader
683     * window, don't do it again.
684     */
685
686    if (!leader_winptr || leader_winptr->tested_for_sm_client_id)
687    {
688	caught_error = 0;
689	XSetErrorHandler (NULL);
690	return;
691    }
692
693    leader_winptr->tested_for_sm_client_id = 1;
694
695    if (!HasXSMPsupport (leader_winptr->window))
696    {
697	XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name);
698
699	XGetCommand (disp, leader_winptr->window,
700	    &leader_winptr->wm_command,
701	    &leader_winptr->wm_command_count);
702
703	XGetClassHint (disp, leader_winptr->window, &leader_winptr->class);
704
705	XGetWMClientMachine (disp, leader_winptr->window,
706	    &leader_winptr->wm_client_machine);
707
708	if (leader_winptr->wm_name != NULL &&
709	    leader_winptr->wm_command != NULL &&
710	    leader_winptr->wm_command_count > 0 &&
711	    leader_winptr->class.res_name != NULL &&
712	    leader_winptr->class.res_class != NULL &&
713	    leader_winptr->wm_client_machine.value != NULL &&
714	    leader_winptr->wm_client_machine.nitems != 0)
715	{
716	    leader_winptr->has_save_yourself =
717		HasSaveYourself (leader_winptr->window);
718
719	    ConnectClientToSM (leader_winptr);
720	}
721    }
722
723    XSync (disp, 0);
724    XSetErrorHandler (NULL);
725
726    if (caught_error)
727    {
728	caught_error = 0;
729	RemoveWindow (leader_winptr);
730    }
731}
732
733
734
735static void
736HandleCreate(XCreateWindowEvent *event)
737{
738    Atom actual_type;
739    int actual_format;
740    unsigned long nitems, bytesafter;
741    unsigned long *datap = NULL;
742    WinInfo *winptr;
743    Bool got_wm_state = 0;
744
745    /*
746     * We are waiting for all proxy connections to close so we can die.
747     * Don't handle new connections.
748     */
749
750    if (ok_to_die)
751	return;
752
753
754    /*
755     * Add the new window
756     */
757
758    if ((winptr = AddNewWindow (event->window)) == NULL)
759	return;
760
761
762    /*
763     * Right after the window was created, it might have been destroyed,
764     * so the following Xlib calls might fail.  Need to catch the error
765     * by installing an error handler.
766     */
767
768    caught_error = 0;
769    XSetErrorHandler (MyErrorHandler);
770
771
772    /*
773     * Select for Property Notify on the window so we can determine
774     * when WM_STATE is defined.  To avoid a race condition, we must
775     * do this _before_ we check for WM_STATE right now.
776     *
777     * Select for Substructure Notify so we can determine when the
778     * window is destroyed.
779     */
780
781    XSelectInput (disp, event->window,
782	SubstructureNotifyMask | PropertyChangeMask);
783
784
785    /*
786     * WM_STATE may already be there.  Check now.
787     */
788
789    if (XGetWindowProperty (disp, event->window, wmStateAtom,
790	0L, 2L, False, AnyPropertyType,
791	&actual_type, &actual_format, &nitems, &bytesafter,
792	(unsigned char **) &datap) == Success && datap)
793    {
794	if (nitems > 0)
795	    got_wm_state = 1;
796
797	if (datap)
798	    XFree ((char *) datap);
799    }
800
801    XSync (disp, 0);
802    XSetErrorHandler (NULL);
803
804    if (caught_error)
805    {
806	caught_error = 0;
807	RemoveWindow (winptr);
808    }
809    else if (got_wm_state)
810    {
811	Got_WM_STATE (winptr);
812    }
813}
814
815
816
817static void
818HandleDestroy(XDestroyWindowEvent *event)
819{
820    WinInfo *winptr;
821
822    if (LookupWindow (event->window, &winptr, NULL))
823    {
824	if (winptr->smc_conn)
825	{
826	    SmcCloseConnection (winptr->smc_conn, 0, NULL);
827	    XtRemoveInput (winptr->input_id);
828	    proxy_count--;
829	}
830
831	if (debug)
832	{
833	    printf ("Removed window (window = 0x%x)\n",
834		    (unsigned int)winptr->window);
835	    printf ("\n");
836	}
837
838	RemoveWindow (winptr);
839    }
840}
841
842
843
844static void
845HandleUpdate(XPropertyEvent *event)
846{
847    Window window = event->window;
848    WinInfo *winptr;
849
850    if (!LookupWindow (window, &winptr, NULL))
851	return;
852
853    if (event->atom == wmStateAtom)
854    {
855	Got_WM_STATE (winptr);
856    }
857    else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update)
858    {
859	/* Finish off the Save Yourself */
860
861	if (winptr->wm_command)
862	{
863	    XFreeStringList (winptr->wm_command);
864	    winptr->wm_command = NULL;
865	    winptr->wm_command_count = 0;
866	}
867
868	XGetCommand (disp, window,
869	    &winptr->wm_command,
870	    &winptr->wm_command_count);
871
872	winptr->waiting_for_update = 0;
873	FinishSaveYourself (winptr, True);
874    }
875}
876
877
878
879static void
880ProxySaveYourselfPhase2CB(SmcConn smcConn, SmPointer clientData)
881{
882    char *filename;
883    Bool success = True;
884    SmProp prop1, prop2, prop3, *props[3];
885    SmPropValue prop1val, prop2val, prop3val;
886    char *discardCommand;
887    int numVals, i;
888    static int first_time = 1;
889
890    if (first_time)
891    {
892	char userId[20];
893	char hint = SmRestartIfRunning;
894
895	prop1.name = SmProgram;
896	prop1.type = SmARRAY8;
897	prop1.num_vals = 1;
898	prop1.vals = &prop1val;
899	prop1val.value = Argv[0];
900	prop1val.length = strlen (Argv[0]);
901
902	snprintf (userId, sizeof(userId), "%ld", (long)getuid());
903	prop2.name = SmUserID;
904	prop2.type = SmARRAY8;
905	prop2.num_vals = 1;
906	prop2.vals = &prop2val;
907	prop2val.value = (SmPointer) userId;
908	prop2val.length = strlen (userId);
909
910	prop3.name = SmRestartStyleHint;
911	prop3.type = SmCARD8;
912	prop3.num_vals = 1;
913	prop3.vals = &prop3val;
914	prop3val.value = (SmPointer) &hint;
915	prop3val.length = 1;
916
917	props[0] = &prop1;
918	props[1] = &prop2;
919	props[2] = &prop3;
920
921	SmcSetProperties (smcConn, 3, props);
922
923	first_time = 0;
924    }
925
926    if ((filename = WriteProxyFile ()) == NULL)
927    {
928	success = False;
929	goto finishUp;
930    }
931
932    prop1.name = SmRestartCommand;
933    prop1.type = SmLISTofARRAY8;
934
935    prop1.vals = (SmPropValue *) malloc (
936	(Argc + 4) * sizeof (SmPropValue));
937
938    if (!prop1.vals)
939    {
940	success = False;
941	goto finishUp;
942    }
943
944    numVals = 0;
945
946    for (i = 0; i < Argc; i++)
947    {
948	if (strcmp (Argv[i], "-clientId") == 0 ||
949	    strcmp (Argv[i], "-restore") == 0)
950	{
951	    i++;
952	}
953	else
954	{
955	    prop1.vals[numVals].value = (SmPointer) Argv[i];
956	    prop1.vals[numVals++].length = strlen (Argv[i]);
957	}
958    }
959
960    prop1.vals[numVals].value = (SmPointer) "-clientId";
961    prop1.vals[numVals++].length = 9;
962
963    prop1.vals[numVals].value = (SmPointer) proxy_clientId;
964    prop1.vals[numVals++].length = strlen (proxy_clientId);
965
966    prop1.vals[numVals].value = (SmPointer) "-restore";
967    prop1.vals[numVals++].length = 8;
968
969    prop1.vals[numVals].value = (SmPointer) filename;
970    prop1.vals[numVals++].length = strlen (filename);
971
972    prop1.num_vals = numVals;
973
974
975    if (asprintf (&discardCommand, "rm %s", filename) == -1) {
976	success = False;
977	goto finishUp;
978    }
979    prop2.name = SmDiscardCommand;
980    prop2.type = SmARRAY8;
981    prop2.num_vals = 1;
982    prop2.vals = &prop2val;
983    prop2val.value = (SmPointer) discardCommand;
984    prop2val.length = strlen (discardCommand);
985
986    props[0] = &prop1;
987    props[1] = &prop2;
988
989    SmcSetProperties (smcConn, 2, props);
990    free ((char *) prop1.vals);
991    free (discardCommand);
992
993 finishUp:
994
995    SmcSaveYourselfDone (smcConn, success);
996    sent_save_done = 1;
997
998    if (filename)
999	free (filename);
1000}
1001
1002
1003
1004static void
1005ProxySaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
1006		    Bool shutdown, int interactStyle, Bool fast)
1007{
1008    /*
1009     * We want the proxy to respond to the Save Yourself after all
1010     * the regular XSMP clients have finished with the save (and possibly
1011     * interacted with the user).
1012     */
1013
1014    if (!SmcRequestSaveYourselfPhase2 (smcConn,
1015	ProxySaveYourselfPhase2CB, NULL))
1016    {
1017	SmcSaveYourselfDone (smcConn, False);
1018	sent_save_done = 1;
1019    }
1020    else
1021	sent_save_done = 0;
1022}
1023
1024
1025
1026static void
1027ProxyDieCB(SmcConn smcConn, SmPointer clientData)
1028{
1029    SmcCloseConnection (proxy_smcConn, 0, NULL);
1030    XtRemoveInput (proxy_iceInputId);
1031
1032    if (die_count == proxy_count)
1033	exit (0);
1034    else
1035	ok_to_die = 1;
1036}
1037
1038
1039
1040static void
1041ProxySaveCompleteCB(SmcConn smcConn, SmPointer clientData)
1042{
1043    ;
1044}
1045
1046
1047
1048static void
1049ProxyShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
1050{
1051    if (!sent_save_done)
1052    {
1053	SmcSaveYourselfDone (smcConn, False);
1054	sent_save_done = 1;
1055    }
1056}
1057
1058
1059
1060static Status
1061ConnectProxyToSM(char *previous_id)
1062{
1063    char errorMsg[256];
1064    unsigned long mask;
1065    SmcCallbacks callbacks;
1066    IceConn iceConn;
1067
1068    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
1069	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
1070
1071    callbacks.save_yourself.callback = ProxySaveYourselfCB;
1072    callbacks.save_yourself.client_data = (SmPointer) NULL;
1073
1074    callbacks.die.callback = ProxyDieCB;
1075    callbacks.die.client_data = (SmPointer) NULL;
1076
1077    callbacks.save_complete.callback = ProxySaveCompleteCB;
1078    callbacks.save_complete.client_data = (SmPointer) NULL;
1079
1080    callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB;
1081    callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
1082
1083    proxy_smcConn = SmcOpenConnection (
1084	NULL, 			/* use SESSION_MANAGER env */
1085	(SmPointer) appContext,
1086	SmProtoMajor,
1087	SmProtoMinor,
1088	mask,
1089	&callbacks,
1090	previous_id,
1091	&proxy_clientId,
1092	256, errorMsg);
1093
1094    if (proxy_smcConn == NULL)
1095	return (0);
1096
1097    iceConn = SmcGetIceConnection (proxy_smcConn);
1098
1099    proxy_iceInputId = XtAppAddInput (
1100	    appContext,
1101	    IceConnectionNumber (iceConn),
1102            (XtPointer) XtInputReadMask,
1103	    ProcessIceMsgProc,
1104	    (XtPointer) iceConn);
1105
1106    return (1);
1107}
1108
1109
1110
1111static void
1112CheckForExistingWindows(Window root)
1113{
1114    Window dontCare1, dontCare2, *children, client_window;
1115    unsigned int nchildren, i;
1116    XCreateWindowEvent event;
1117
1118    /*
1119     * We query the root tree for all windows created thus far.
1120     * Note that at any moment after XQueryTree is called, a
1121     * window may be deleted.  So we must take extra care to make
1122     * sure a window really exists.
1123     */
1124
1125    XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren);
1126
1127    for (i = 0; i < nchildren; i++)
1128    {
1129	event.window = children[i];
1130
1131	HandleCreate (&event);
1132
1133	caught_error = 0;
1134	XSetErrorHandler (MyErrorHandler);
1135
1136	client_window = XmuClientWindow (disp, children[i]);
1137
1138	XSetErrorHandler (NULL);
1139
1140	if (!caught_error && client_window != children[i])
1141	{
1142	    event.window = client_window;
1143	    HandleCreate (&event);
1144	}
1145    }
1146}
1147
1148
1149
1150int
1151main (int argc, char *argv[])
1152{
1153    char *restore_filename = NULL;
1154    char *client_id = NULL;
1155    int i, zero = 0;
1156
1157    Argc = argc;
1158    Argv = argv;
1159
1160    for (i = 1; i < argc; i++)
1161    {
1162	if (argv[i][0] == '-')
1163	{
1164	    switch (argv[i][1])
1165	    {
1166	      case 'd':				/* -debug */
1167		debug = 1;
1168		continue;
1169
1170	      case 'c':				/* -clientId */
1171		if (++i >= argc) {
1172		    fprintf (stderr, "%s: -clientId requires an argument\n",
1173			     argv[0]);
1174		    goto usage;
1175		}
1176		client_id = argv[i];
1177		continue;
1178
1179	      case 'r':				/* -restore */
1180		if (++i >= argc) {
1181		    fprintf (stderr, "%s: -restore requires an argument\n",
1182			     argv[0]);
1183		    goto usage;
1184		}
1185		restore_filename = argv[i];
1186		continue;
1187
1188	      case 'v':
1189		puts (PACKAGE_STRING);
1190		exit (0);
1191	    }
1192	}
1193
1194	fprintf (stderr, "%s: unrecognized argument: %s\n", argv[0], argv[i]);
1195
1196    usage:
1197
1198	fprintf (stderr,
1199	    "usage:  %s [-clientId id] [-restore file] [-debug] [-version]\n",
1200		 argv[0]);
1201	exit (1);
1202    }
1203
1204
1205    XtToolkitInitialize ();
1206    appContext = XtCreateApplicationContext ();
1207
1208    if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY",
1209	NULL, 0, &zero, NULL)))
1210    {
1211	fprintf (stderr, "smproxy: unable to open display\n");
1212	exit (1);
1213    }
1214
1215    if (restore_filename)
1216	ReadProxyFile (restore_filename);
1217
1218    if (!ConnectProxyToSM (client_id))
1219    {
1220	fprintf (stderr, "smproxy: unable to connect to session manager\n");
1221	exit (1);
1222    }
1223
1224    wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False);
1225    wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False);
1226    wmStateAtom = XInternAtom (disp, "WM_STATE", False);
1227    smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False);
1228    wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False);
1229
1230    for (i = 0; i < ScreenCount (disp); i++)
1231    {
1232	Window root = RootWindow (disp, i);
1233	XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask);
1234	CheckForExistingWindows (root);
1235    }
1236
1237    while (1)
1238    {
1239	XEvent event;
1240
1241	XtAppNextEvent (appContext, &event);
1242
1243	switch (event.type)
1244	{
1245	case CreateNotify:
1246	    HandleCreate (&event.xcreatewindow);
1247	    break;
1248
1249	case DestroyNotify:
1250	    HandleDestroy (&event.xdestroywindow);
1251	    break;
1252
1253	case PropertyNotify:
1254	    HandleUpdate (&event.xproperty);
1255	    break;
1256
1257	default:
1258	    XtDispatchEvent (&event);
1259	    break;
1260	}
1261    }
1262    exit(0);
1263}
1264