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