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