xsm.c revision 1a650d1e
1/* $Xorg: xsm.c,v 1.7 2001/02/09 02:06:01 xorgcvs Exp $ */
2/******************************************************************************
3
4Copyright 1993, 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******************************************************************************/
26/* $XFree86: xc/programs/xsm/xsm.c,v 1.9 2001/12/14 20:02:27 dawes Exp $ */
27
28/*
29 * X Session Manager.
30 *
31 * Authors:
32 *	Ralph Mor, X Consortium
33 *      Jordan Brown, Quarterdeck Office Systems
34 */
35
36#include "xsm.h"
37#include "xtwatch.h"
38#include "prop.h"
39#include "choose.h"
40#include "mainwin.h"
41#include "info.h"
42#include "log.h"
43#include "save.h"
44#include "auth.h"
45#include "restart.h"
46#include "saveutil.h"
47#include "lock.h"
48
49#include <X11/Shell.h>
50#include <X11/Xatom.h>
51#include <X11/Xaw/List.h>
52
53int		Argc;
54char		**Argv;
55
56List		*RunningList;
57List		*PendingList;
58List		*RestartAnywayList;
59List		*RestartImmedList;
60
61List		*WaitForSaveDoneList;
62static List	*InitialSaveList;
63List		*FailedSaveList;
64List		*WaitForInteractList;
65List		*WaitForPhase2List;
66
67Bool		wantShutdown = False;
68Bool		shutdownInProgress = False;
69Bool		phase2InProgress = False;
70Bool		saveInProgress = False;
71Bool		shutdownCancelled = False;
72
73Bool		verbose = False;
74
75char		*sm_id = NULL;
76
77char		*networkIds = NULL;
78char		*session_name = NULL;
79
80IceAuthDataEntry *authDataEntries = NULL;
81int		numTransports = 0;
82
83Bool		client_info_visible = False;
84Bool		client_prop_visible = False;
85Bool		client_log_visible = False;
86
87String 		*clientListNames = NULL;
88ClientRec	**clientListRecs = NULL;
89int		numClientListNames = 0;
90
91int		current_client_selected;
92
93int		sessionNameCount = 0;
94String		*sessionNamesShort = NULL;
95String		*sessionNamesLong = NULL;
96Bool		*sessionsLocked = NULL;
97
98int		num_clients_in_last_session = -1;
99
100char		**non_session_aware_clients = NULL;
101int		non_session_aware_count = 0;
102
103char		*display_env = NULL, *non_local_display_env = NULL;
104char		*session_env = NULL, *non_local_session_env = NULL;
105char		*audio_env = NULL;
106
107Bool		need_to_name_session = False;
108
109Bool		remote_allowed;
110
111XtAppContext	appContext;
112Widget		topLevel;
113
114XtSignalId	sig_term_id, sig_usr1_id;
115
116static Atom wmStateAtom;
117static Atom wmDeleteAtom;
118static char *cmd_line_display = NULL;
119
120/*
121 * Forward declarations
122 */
123static void PropertyChangeXtHandler(Widget w, XtPointer closure,
124				    XEvent *event,
125				    Boolean *continue_to_dispatch);
126static void GetEnvironment(void);
127static Status RegisterClientProc(SmsConn smsConn, SmPointer managerData,
128				 char *previousId);
129static Bool OkToEnterInteractPhase(void);
130static void InteractRequestProc(SmsConn smsConn, SmPointer managerData,
131				int dialogType);
132static void InteractDoneProc(SmsConn smsConn, SmPointer managerData,
133			     Bool cancelShutdown);
134static void SaveYourselfReqProc(SmsConn smsConn, SmPointer managerData,
135				int saveType, Bool shutdown,
136				int interactStyle, Bool fast, Bool global);
137static Bool OkToEnterPhase2(void);
138static void SaveYourselfPhase2ReqProc(SmsConn smsConn, SmPointer managerData);
139static void SaveYourselfDoneProc(SmsConn smsConn, SmPointer managerData,
140				 Bool success);
141static void CloseConnectionProc(SmsConn smsConn, SmPointer managerData,
142				int count, char **reasonMsgs);
143static Status NewClientProc(SmsConn smsConn, SmPointer managerData,
144			    unsigned long *maskRet,
145			    SmsCallbacks *callbacksRet,
146			    char **failureReasonRet);
147static void NewConnectionXtProc(XtPointer client_data, int *source,
148				XtInputId *id);
149static void MyIoErrorHandler(IceConn ice_conn);
150static void InstallIOErrorHandler(void);
151static void CloseListeners(void);
152
153
154static IceListenObj *listenObjs;
155
156
157/*
158 * Main program
159 */
160int
161main(int argc, char *argv[])
162{
163    char	*p;
164    char 	errormsg[256];
165    static	char environment_name[] = "SESSION_MANAGER";
166    int		success, found_command_line_name, i;
167
168    Argc = argc;
169    Argv = argv;
170
171    for (i = 1; i < argc; i++)
172    {
173	if (argv[i][0] == '-')
174	{
175	    switch (argv[i][1])
176	    {
177	    case 'd':					/* -display */
178		if (++i >= argc) goto usage;
179		cmd_line_display = (char *) XtNewString (argv[i]);
180		continue;
181
182	    case 's':					/* -session */
183		if (++i >= argc) goto usage;
184		session_name = XtNewString (argv[i]);
185		continue;
186
187	    case 'v':					/* -verbose */
188		verbose = 1;
189		continue;
190	    }
191	}
192
193    usage:
194	fprintf (stderr,
195	 "usage: xsm [-display display] [-session session_name] [-verbose]\n");
196	exit (1);
197    }
198
199    topLevel = XtVaAppInitialize (&appContext, "XSm", NULL, 0,
200	&argc, argv, NULL,
201	XtNmappedWhenManaged, False,
202	XtNwindowRole, "xsm main window",
203	NULL);
204
205    wmStateAtom = XInternAtom (
206	XtDisplay (topLevel), "WM_STATE", False);
207    wmDeleteAtom = XInternAtom (
208	XtDisplay (topLevel), "WM_DELETE_WINDOW", False);
209
210    register_signals (appContext);
211
212
213    /*
214     * Install an IO error handler.  For an explanation,
215     * see the comments for InstallIOErrorHandler().
216     */
217
218    InstallIOErrorHandler ();
219
220
221    /*
222     * Init SM lib
223     */
224
225    if (!SmsInitialize ("SAMPLE-SM", "1.0",
226	NewClientProc, NULL,
227	HostBasedAuthProc, 256, errormsg))
228    {
229	fprintf (stderr, "%s\n", errormsg);
230	exit (1);
231    }
232
233    if (!IceListenForConnections (&numTransports, &listenObjs,
234	256, errormsg))
235    {
236	fprintf (stderr, "%s\n", errormsg);
237	exit (1);
238    }
239
240    atexit(CloseListeners);
241
242    if (!SetAuthentication (numTransports, listenObjs, &authDataEntries))
243    {
244	fprintf (stderr, "Could not set authorization\n");
245	exit (1);
246    }
247
248    InitWatchProcs (appContext);
249
250    for (i = 0; i < numTransports; i++)
251    {
252	XtAppAddInput (appContext,
253	    IceGetListenConnectionNumber (listenObjs[i]),
254	    (XtPointer) XtInputReadMask,
255	    NewConnectionXtProc, (XtPointer) listenObjs[i]);
256    }
257
258    /* the sizeof includes the \0, so we don't need to count the '=' */
259    networkIds = IceComposeNetworkIdList (numTransports, listenObjs);
260    p = (char *) XtMalloc((sizeof environment_name) + strlen(networkIds) + 1);
261    if(!p) nomem();
262    sprintf(p, "%s=%s", environment_name, networkIds);
263    putenv(p);
264
265    if (cmd_line_display)
266    {
267	/*
268	 * If a display was passed on the command line, set the DISPLAY
269	 * environment in this process so all applications started by
270	 * the session manager will run on the specified display.
271	 */
272
273	p = (char *) XtMalloc(8 + strlen(cmd_line_display) + 1);
274	sprintf(p, "DISPLAY=%s", cmd_line_display);
275	putenv(p);
276    }
277
278    if (verbose)
279	printf ("setenv %s %s\n", environment_name, networkIds);
280
281    create_choose_session_popup ();
282    create_main_window ();
283    create_client_info_popup ();
284    create_save_popup ();
285    create_log_popup ();
286
287
288    /*
289     * Initalize all lists
290     */
291
292    RunningList = ListInit();
293    if(!RunningList) nomem();
294
295    PendingList = ListInit();
296    if(!PendingList) nomem();
297
298    RestartAnywayList = ListInit();
299    if(!RestartAnywayList) nomem();
300
301    RestartImmedList = ListInit();
302    if(!RestartImmedList) nomem();
303
304    WaitForSaveDoneList = ListInit();
305    if (!WaitForSaveDoneList) nomem();
306
307    InitialSaveList = ListInit();
308    if (!InitialSaveList) nomem();
309
310    FailedSaveList = ListInit();
311    if (!FailedSaveList) nomem();
312
313    WaitForInteractList = ListInit();
314    if (!WaitForInteractList) nomem();
315
316    WaitForPhase2List = ListInit();
317    if (!WaitForPhase2List) nomem();
318
319
320    /*
321     * Get list of session names.  If a session name was found on the
322     * command line, and it is in the list of session names we got, then
323     * use that session name.  If there were no session names found, then
324     * use the default session name.  Otherwise, present a list of session
325     * names for the user to choose from.
326     */
327
328    success = GetSessionNames (&sessionNameCount,
329	&sessionNamesShort, &sessionNamesLong, &sessionsLocked);
330
331    found_command_line_name = 0;
332    if (success && session_name)
333    {
334	for (i = 0; i < sessionNameCount; i++)
335	    if (strcmp (session_name, sessionNamesShort[i]) == 0)
336	    {
337		found_command_line_name = 1;
338
339		if (sessionsLocked[i])
340		{
341		    fprintf (stderr, "Session '%s' is locked\n", session_name);
342		    exit (1);
343		}
344
345		break;
346	    }
347    }
348
349    if (!success || found_command_line_name)
350    {
351	FreeSessionNames (sessionNameCount,
352	    sessionNamesShort, sessionNamesLong, sessionsLocked);
353
354	if (!found_command_line_name)
355	    session_name = XtNewString (DEFAULT_SESSION_NAME);
356
357    	if (!StartSession (session_name, !found_command_line_name))
358	    UnableToLockSession (session_name);
359    }
360    else
361    {
362	ChooseSession ();
363    }
364
365
366    /*
367     * Main loop
368     */
369
370    XtAppMainLoop (appContext);
371    exit(0);
372}
373
374
375
376static void
377PropertyChangeXtHandler(Widget w, XtPointer closure, XEvent *event,
378			Boolean *continue_to_dispatch)
379{
380    if (w == topLevel && event->type == PropertyNotify &&
381	event->xproperty.atom == wmStateAtom)
382    {
383	XtRemoveEventHandler (topLevel, PropertyChangeMask, False,
384	    PropertyChangeXtHandler, NULL);
385
386	/*
387	 * Restart the rest of the session aware clients.
388	 */
389
390	Restart (RESTART_REST_OF_CLIENTS);
391
392
393	/*
394	 * Start apps that aren't session aware that were specified
395	 * by the user.
396	 */
397
398	StartNonSessionAwareApps ();
399    }
400}
401
402
403
404void
405SetWM_DELETE_WINDOW(Widget widget, String delAction)
406{
407    char translation[64];
408
409    snprintf (translation, sizeof(translation),
410	      "<Message>WM_PROTOCOLS: %s", delAction);
411    XtOverrideTranslations (widget, XtParseTranslationTable (translation));
412
413    XSetWMProtocols (XtDisplay(widget), XtWindow (widget),
414	&wmDeleteAtom, 1);
415}
416
417
418
419static void
420GetEnvironment(void)
421{
422    static char	envDISPLAY[]="DISPLAY";
423    static char	envSESSION_MANAGER[]="SESSION_MANAGER";
424    static char	envAUDIOSERVER[]="AUDIOSERVER";
425    char	*p, *temp;
426
427    remote_allowed = 1;
428
429    display_env = NULL;
430    if((p = cmd_line_display) || (p = (char *) getenv(envDISPLAY))) {
431	display_env = (char *) XtMalloc(strlen(envDISPLAY)+1+strlen(p)+1);
432	if(!display_env) nomem();
433	sprintf(display_env, "%s=%s", envDISPLAY, p);
434
435	/*
436	 * When we restart a remote client, we have to make sure the
437	 * display environment we give it has the SM's hostname.
438	 */
439
440	if ((temp = strchr (p, '/')) == NULL)
441	    temp = p;
442	else
443	    temp++;
444
445	if (*temp != ':')
446	{
447	    /* we have a host name */
448
449	    non_local_display_env = (char *) XtMalloc (
450		strlen (display_env) + 1);
451	    if (!non_local_display_env) nomem();
452
453	    strcpy (non_local_display_env, display_env);
454	}
455	else
456	{
457	    char hostnamebuf[256];
458
459	    gethostname (hostnamebuf, sizeof hostnamebuf);
460	    non_local_display_env = (char *) XtMalloc (
461		strlen (envDISPLAY) + 1 +
462		strlen (hostnamebuf) + strlen (temp) + 1);
463	    if (!non_local_display_env) nomem();
464	    sprintf(non_local_display_env, "%s=%s%s",
465		envDISPLAY, hostnamebuf, temp);
466	}
467    }
468
469    session_env = NULL;
470    if((p = (char *) getenv(envSESSION_MANAGER))) {
471	session_env = (char *) XtMalloc(
472	    strlen(envSESSION_MANAGER)+1+strlen(p)+1);
473	if(!session_env) nomem();
474	sprintf(session_env, "%s=%s", envSESSION_MANAGER, p);
475
476	/*
477	 * When we restart a remote client, we have to make sure the
478	 * session environment does not have the SM's local connection port.
479	 */
480
481	non_local_session_env = (char *) XtMalloc (strlen (session_env) + 1);
482	if (!non_local_session_env) nomem();
483	strcpy (non_local_session_env, session_env);
484
485	if ((temp = Strstr (non_local_session_env, "local/")) != NULL)
486	{
487	    char *delim = strchr (temp, ',');
488	    if (delim == NULL)
489	    {
490		if (temp == non_local_session_env +
491		    strlen (envSESSION_MANAGER) + 1)
492		{
493		    *temp = '\0';
494		    remote_allowed = 0;
495		}
496		else
497		    *(temp - 1) = '\0';
498	    }
499	    else
500	    {
501		int bytes = strlen (delim + 1);
502		memmove (temp, delim + 1, bytes);
503		*(temp + bytes) = '\0';
504	    }
505	}
506    }
507
508    audio_env = NULL;
509    if((p = (char *) getenv(envAUDIOSERVER))) {
510	audio_env = (char *) XtMalloc(strlen(envAUDIOSERVER)+1+strlen(p)+1);
511	if(!audio_env) nomem();
512	sprintf(audio_env, "%s=%s", envAUDIOSERVER, p);
513    }
514}
515
516
517
518Status
519StartSession(char *name, Bool use_default)
520{
521    int database_read = 0;
522    Dimension width;
523    char title[256];
524
525
526    /*
527     * If we're not using the default session, lock it.
528     * If using the default session, it will be locked as
529     * soon as the user assigns the session a name.
530     */
531
532    if (!use_default && !LockSession (name, True))
533	return (0);
534
535
536    /*
537     * Get important environment variables.
538     */
539
540    GetEnvironment ();
541
542
543    /*
544     * Set the main window's title to the session name.
545     */
546
547    snprintf (title, sizeof(title), "xsm: %s", name);
548
549    XtVaSetValues (topLevel,
550	XtNtitle, title,		/* session name */
551	NULL);
552
553    XtRealizeWidget (topLevel);
554
555
556    /*
557     * Set WM_DELETE_WINDOW support on main window.  If the user tries
558     * to delete the main window, the shutdown prompt will come up.
559     */
560
561    SetWM_DELETE_WINDOW (topLevel, "DelMainWinAction()");
562
563
564    /*
565     * Read the session save file.  Make sure the session manager
566     * has an SM_CLIENT_ID, so that other managers (like the WM) can
567     * identify it.
568     */
569
570    set_session_save_file_name (name);
571
572    if (use_default)
573	need_to_name_session = True;
574    else
575    {
576	database_read = ReadSave (name, &sm_id);
577	need_to_name_session = !database_read;
578    }
579
580    if (!sm_id)
581    {
582	sm_id = SmsGenerateClientID (NULL);
583	if (!sm_id) return (0);
584    }
585    XChangeProperty (XtDisplay (topLevel), XtWindow (topLevel),
586	XInternAtom (XtDisplay (topLevel), "SM_CLIENT_ID", False),
587	XA_STRING, 8, PropModeReplace,
588	(unsigned char *) sm_id, strlen (sm_id));
589
590
591    /*
592     * Adjust some label widths
593     */
594
595    XtVaGetValues (clientInfoButton,
596	XtNwidth, &width,
597	NULL);
598
599    XtVaSetValues (checkPointButton,
600	XtNwidth, width,
601	NULL);
602
603    XtVaGetValues (logButton,
604	XtNwidth, &width,
605	NULL);
606
607    XtVaSetValues (shutdownButton,
608	XtNwidth, width,
609	NULL);
610
611
612    XtMapWidget (topLevel);
613
614
615    if (!database_read)
616    {
617	/*
618	 * Start default apps (e.g. twm, smproxy)
619	 */
620
621	StartDefaultApps ();
622    }
623    else
624    {
625	/*
626	 * Restart window manager first.  When the session manager
627	 * gets a WM_STATE stored on its top level window, we know
628	 * the window manager is running.  At that time, we can start
629	 * the rest of the applications.
630	 */
631
632	XtAddEventHandler (topLevel, PropertyChangeMask, False,
633	    PropertyChangeXtHandler, NULL);
634
635	if (!Restart (RESTART_MANAGERS))
636	{
637	    XtRemoveEventHandler (topLevel, PropertyChangeMask, False,
638	        PropertyChangeXtHandler, NULL);
639
640	    /*
641	     * Restart the rest of the session aware clients.
642	     */
643
644	    Restart (RESTART_REST_OF_CLIENTS);
645
646	    /*
647	     * Start apps that aren't session aware that were specified
648	     * by the user.
649	     */
650
651	    StartNonSessionAwareApps ();
652	}
653    }
654
655    return (1);
656}
657
658
659
660void
661EndSession(int status)
662{
663    if (verbose)
664	printf ("\nSESSION MANAGER GOING AWAY!\n");
665
666    FreeAuthenticationData (numTransports, authDataEntries);
667
668    if (session_name)
669    {
670	UnlockSession (session_name);
671	XtFree (session_name);
672    }
673
674    if (display_env)
675	XtFree (display_env);
676    if (session_env)
677	XtFree (session_env);
678    if (cmd_line_display)
679	XtFree (cmd_line_display);
680    if (non_local_display_env)
681	XtFree (non_local_display_env);
682    if (non_local_session_env)
683	XtFree (non_local_session_env);
684    if (audio_env)
685	XtFree (audio_env);
686    if (networkIds)
687	free (networkIds);
688
689    exit (status);
690}
691
692
693
694void
695FreeClient(ClientRec *client, Bool freeProps)
696{
697    if (freeProps)
698    {
699	List *pl;
700
701	for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
702	    FreeProp ((Prop *) pl->thing);
703
704	ListFreeAll (client->props);
705    }
706
707    if (client->clientId)
708	free (client->clientId);		/* malloc'd by SMlib */
709    if (client->clientHostname)
710	free (client->clientHostname);		/* malloc'd by SMlib */
711
712    if (client->discardCommand)
713	XtFree (client->discardCommand);
714    if (client->saveDiscardCommand)
715	XtFree (client->saveDiscardCommand);
716
717    XtFree ((char *) client);
718}
719
720
721
722/*
723 * Session Manager callbacks
724 */
725
726static Status
727RegisterClientProc(SmsConn smsConn, SmPointer managerData, char *previousId)
728{
729    ClientRec	*client = (ClientRec *) managerData;
730    char 	*id;
731    List	*cl;
732    int		send_save;
733
734    if (verbose)
735    {
736	printf (
737	"On IceConn fd = %d, received REGISTER CLIENT [Previous Id = %s]\n",
738	IceConnectionNumber (client->ice_conn),
739	previousId ? previousId : "NULL");
740	printf ("\n");
741    }
742
743    if (!previousId)
744    {
745	id = SmsGenerateClientID (smsConn);
746	send_save = 1;
747    }
748    else
749    {
750	int found_match = 0;
751	send_save = 1;
752
753	for (cl = ListFirst (PendingList); cl; cl = ListNext (cl))
754	{
755	    PendingClient *pendClient = (PendingClient *) cl->thing;
756
757	    if (!strcmp (pendClient->clientId, previousId))
758	    {
759		SetInitialProperties (client, pendClient->props);
760		XtFree (pendClient->clientId);
761		XtFree (pendClient->clientHostname);
762		XtFree ((char *) pendClient);
763		ListFreeOne (cl);
764		found_match = 1;
765		send_save = 0;
766		break;
767	    }
768	}
769
770	if (!found_match)
771	{
772	    for (cl = ListFirst (RestartAnywayList); cl; cl = ListNext (cl))
773	    {
774		ClientRec *rClient = (ClientRec *) cl->thing;
775
776		if (!strcmp (rClient->clientId, previousId))
777		{
778		    SetInitialProperties (client, rClient->props);
779		    FreeClient (rClient, False /* don't free props */);
780		    ListFreeOne (cl);
781		    found_match = 1;
782		    send_save = 0;
783		    break;
784		}
785	    }
786	}
787
788	if (!found_match)
789	{
790	    for (cl = ListFirst (RestartImmedList); cl; cl = ListNext (cl))
791	    {
792		ClientRec *rClient = (ClientRec *) cl->thing;
793
794		if (!strcmp (rClient->clientId, previousId))
795		{
796		    SetInitialProperties (client, rClient->props);
797		    FreeClient (rClient, False /* don't free props */);
798		    ListFreeOne (cl);
799		    found_match = 1;
800		    send_save = 0;
801		    break;
802		}
803	    }
804	}
805
806	if (!found_match)
807	{
808	    /*
809	     * previous-id was bogus: return bad status and the client
810	     * should re-register with a NULL previous-id
811	     */
812
813	    free (previousId);
814	    return (0);
815	}
816	else
817	{
818	    id = previousId;
819	}
820    }
821
822    SmsRegisterClientReply (smsConn, id);
823
824    if (verbose)
825    {
826	printf (
827	"On IceConn fd = %d, sent REGISTER CLIENT REPLY [Client Id = %s]\n",
828	IceConnectionNumber (client->ice_conn), id);
829	printf ("\n");
830    }
831
832    client->clientId = id;
833    client->clientHostname = SmsClientHostName (smsConn);
834    client->restarted = (previousId != NULL);
835
836    if (send_save)
837    {
838	SmsSaveYourself (smsConn, SmSaveLocal,
839	    False, SmInteractStyleNone, False);
840
841	ListAddLast (InitialSaveList, (char *) client);
842    }
843    else if (client_info_visible)
844    {
845	/* We already have all required client info */
846
847	UpdateClientList ();
848	XawListHighlight (clientListWidget, current_client_selected);
849    }
850
851    return (1);
852}
853
854
855
856static Bool
857OkToEnterInteractPhase(void)
858{
859    return ((ListCount (WaitForInteractList) +
860	ListCount (WaitForPhase2List)) == ListCount (WaitForSaveDoneList));
861}
862
863
864
865static void
866InteractRequestProc(SmsConn smsConn, SmPointer managerData, int dialogType)
867{
868    ClientRec	*client = (ClientRec *) managerData;
869
870    if (verbose)
871    {
872	printf ("Client Id = %s, received INTERACT REQUEST [Dialog Type = ",
873		client->clientId);
874	if (dialogType == SmDialogError)
875	    printf ("Error]\n");
876	else if (dialogType == SmDialogNormal)
877	    printf ("Normal]\n");
878	else
879	    printf ("Error in SMlib: should have checked for bad value]\n");
880    }
881
882    ListAddLast (WaitForInteractList, (char *) client);
883
884    if (OkToEnterInteractPhase ())
885    {
886	LetClientInteract (ListFirst (WaitForInteractList));
887    }
888}
889
890
891
892static void
893InteractDoneProc(SmsConn smsConn, SmPointer managerData, Bool cancelShutdown)
894{
895    ClientRec	*client = (ClientRec *) managerData;
896    List	*cl;
897
898    if (verbose)
899    {
900	printf (
901	"Client Id = %s, received INTERACT DONE [Cancel Shutdown = %s]\n",
902	client->clientId, cancelShutdown ? "True" : "False");
903    }
904
905    if (cancelShutdown)
906    {
907	ListFreeAllButHead (WaitForInteractList);
908	ListFreeAllButHead (WaitForPhase2List);
909    }
910
911    if (cancelShutdown)
912    {
913	if (shutdownCancelled)
914	{
915	    /* Shutdown was already cancelled */
916	    return;
917	}
918
919	shutdownCancelled = True;
920
921	for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
922	{
923	    client = (ClientRec *) cl->thing;
924
925	    SmsShutdownCancelled (client->smsConn);
926
927	    if (verbose)
928	    {
929		printf ("Client Id = %s, sent SHUTDOWN CANCELLED\n",
930			client->clientId);
931	    }
932	}
933    }
934    else
935    {
936	if ((cl = ListFirst (WaitForInteractList)) != NULL)
937	{
938	    LetClientInteract (cl);
939	}
940	else
941	{
942	    if (verbose)
943	    {
944		printf ("\n");
945		printf ("Done interacting with all clients.\n");
946		printf ("\n");
947	    }
948
949	    if (ListCount (WaitForPhase2List) > 0)
950	    {
951		StartPhase2 ();
952	    }
953	}
954    }
955}
956
957
958
959static void
960SaveYourselfReqProc(SmsConn smsConn, SmPointer managerData, int saveType,
961    Bool shutdown, int interactStyle, Bool fast, Bool global)
962{
963    if (verbose)
964	printf("SAVE YOURSELF REQUEST not supported!\n");
965}
966
967
968
969static Bool
970OkToEnterPhase2(void)
971
972{
973    return (ListCount (WaitForPhase2List) == ListCount (WaitForSaveDoneList));
974}
975
976
977
978static void
979SaveYourselfPhase2ReqProc(SmsConn smsConn, SmPointer managerData)
980{
981    ClientRec	*client = (ClientRec *) managerData;
982
983    if (verbose)
984    {
985	printf ("Client Id = %s, received SAVE YOURSELF PHASE 2 REQUEST\n",
986	    client->clientId);
987    }
988
989    if (!saveInProgress)
990    {
991	/*
992	 * If we are not in the middle of a checkpoint (ie. we just
993	 * started the client and sent the initial save yourself), just
994	 * send the save yourself phase2 now.
995	 */
996
997	SmsSaveYourselfPhase2 (client->smsConn);
998    }
999    else
1000    {
1001	ListAddLast (WaitForPhase2List, (char *) client);
1002
1003	if (ListCount (WaitForInteractList) > 0 && OkToEnterInteractPhase ())
1004	{
1005	    LetClientInteract (ListFirst (WaitForInteractList));
1006	}
1007	else if (OkToEnterPhase2 ())
1008	{
1009	    StartPhase2 ();
1010	}
1011    }
1012}
1013
1014
1015
1016static void
1017SaveYourselfDoneProc(SmsConn smsConn, SmPointer managerData, Bool success)
1018{
1019    ClientRec	*client = (ClientRec *) managerData;
1020
1021    if (verbose)
1022    {
1023	printf("Client Id = %s, received SAVE YOURSELF DONE [Success = %s]\n",
1024	       client->clientId, success ? "True" : "False");
1025    }
1026
1027    if (!ListSearchAndFreeOne (WaitForSaveDoneList, (char *) client))
1028    {
1029	if (ListSearchAndFreeOne (InitialSaveList, (char *) client))
1030	    SmsSaveComplete (client->smsConn);
1031	return;
1032    }
1033
1034    if (!success)
1035    {
1036	ListAddLast (FailedSaveList, (char *) client);
1037    }
1038
1039    if (ListCount (WaitForSaveDoneList) == 0)
1040    {
1041	if (ListCount (FailedSaveList) > 0 && !checkpoint_from_signal)
1042	    PopupBadSave ();
1043	else
1044	    FinishUpSave ();
1045    }
1046    else if (ListCount (WaitForInteractList) > 0 && OkToEnterInteractPhase ())
1047    {
1048	LetClientInteract (ListFirst (WaitForInteractList));
1049    }
1050    else if (ListCount (WaitForPhase2List) > 0 && OkToEnterPhase2 ())
1051    {
1052	StartPhase2 ();
1053    }
1054}
1055
1056
1057
1058void
1059CloseDownClient(ClientRec *client)
1060{
1061    int index_deleted = 0;
1062
1063    if (verbose) {
1064	printf ("ICE Connection closed, IceConn fd = %d\n",
1065		IceConnectionNumber (client->ice_conn));
1066	printf ("\n");
1067    }
1068
1069    SmsCleanUp (client->smsConn);
1070    IceSetShutdownNegotiation (client->ice_conn, False);
1071    IceCloseConnection (client->ice_conn);
1072
1073    client->ice_conn = NULL;
1074    client->smsConn = NULL;
1075
1076    if (!shutdownInProgress && client_info_visible)
1077    {
1078	for (index_deleted = 0;
1079	    index_deleted < numClientListNames; index_deleted++)
1080	{
1081	    if (clientListRecs[index_deleted] == client)
1082		break;
1083	}
1084    }
1085
1086    ListSearchAndFreeOne (RunningList, (char *) client);
1087
1088    if (saveInProgress)
1089    {
1090	Status delStatus = ListSearchAndFreeOne (
1091	    WaitForSaveDoneList, (char *) client);
1092
1093	if (delStatus)
1094	{
1095	    ListAddLast (FailedSaveList, (char *) client);
1096	    client->freeAfterBadSavePopup = True;
1097	}
1098
1099	ListSearchAndFreeOne (WaitForInteractList, (char *) client);
1100	ListSearchAndFreeOne (WaitForPhase2List, (char *) client);
1101
1102	if (delStatus && ListCount (WaitForSaveDoneList) == 0)
1103	{
1104	    if (ListCount (FailedSaveList) > 0 && !checkpoint_from_signal)
1105		PopupBadSave ();
1106	    else
1107		FinishUpSave ();
1108	}
1109	else if (ListCount (WaitForInteractList) > 0 &&
1110	    OkToEnterInteractPhase ())
1111	{
1112	    LetClientInteract (ListFirst (WaitForInteractList));
1113	}
1114	else if (!phase2InProgress &&
1115	    ListCount (WaitForPhase2List) > 0 && OkToEnterPhase2 ())
1116	{
1117	    StartPhase2 ();
1118	}
1119    }
1120
1121    if (client->restartHint == SmRestartImmediately && !shutdownInProgress)
1122    {
1123	Clone (client, True /* use saved state */);
1124
1125	ListAddLast (RestartImmedList, (char *) client);
1126    }
1127    else if (client->restartHint == SmRestartAnyway)
1128    {
1129	ListAddLast (RestartAnywayList, (char *) client);
1130    }
1131    else if (!client->freeAfterBadSavePopup)
1132    {
1133	FreeClient (client, True /* free props */);
1134    }
1135
1136    if (shutdownInProgress)
1137    {
1138	if (ListCount (RunningList) == 0)
1139	    EndSession (0);
1140    }
1141    else if (client_info_visible)
1142    {
1143	UpdateClientList ();
1144
1145	if (current_client_selected == index_deleted)
1146	{
1147	    if (current_client_selected == numClientListNames)
1148		current_client_selected--;
1149
1150	    if (current_client_selected >= 0)
1151	    {
1152		XawListHighlight (clientListWidget, current_client_selected);
1153		ShowHint (clientListRecs[current_client_selected]);
1154		if (client_prop_visible)
1155		{
1156		    DisplayProps (clientListRecs[current_client_selected]);
1157		}
1158	    }
1159	}
1160	else
1161	{
1162	    if (index_deleted < current_client_selected)
1163		current_client_selected--;
1164	    XawListHighlight (clientListWidget, current_client_selected);
1165	}
1166    }
1167}
1168
1169
1170
1171
1172static void
1173CloseConnectionProc(SmsConn smsConn, SmPointer managerData,
1174		    int count, char **reasonMsgs)
1175{
1176    ClientRec	*client = (ClientRec *) managerData;
1177
1178    if (verbose)
1179    {
1180	int i;
1181
1182	printf ("Client Id = %s, received CONNECTION CLOSED\n",
1183	    client->clientId);
1184
1185	for (i = 0; i < count; i++)
1186	    printf ("   Reason string %d: %s\n", i + 1, reasonMsgs[i]);
1187	printf ("\n");
1188    }
1189
1190    SmFreeReasons (count, reasonMsgs);
1191
1192    CloseDownClient (client);
1193}
1194
1195
1196
1197static Status
1198NewClientProc(SmsConn smsConn, SmPointer managerData, unsigned long *maskRet,
1199	      SmsCallbacks *callbacksRet, char **failureReasonRet)
1200{
1201    ClientRec *newClient = (ClientRec *) XtMalloc (sizeof (ClientRec));
1202
1203    *maskRet = 0;
1204
1205    if (!newClient)
1206    {
1207	char *str = "Memory allocation failed";
1208
1209	if ((*failureReasonRet = (char *) XtMalloc (strlen (str) + 1)) != NULL)
1210	    strcpy (*failureReasonRet, str);
1211
1212	return (0);
1213    }
1214
1215    newClient->smsConn = smsConn;
1216    newClient->ice_conn = SmsGetIceConnection (smsConn);
1217    newClient->clientId = NULL;
1218    newClient->clientHostname = NULL;
1219    newClient->restarted = False; /* wait till RegisterClient for true value */
1220    newClient->userIssuedCheckpoint = False;
1221    newClient->receivedDiscardCommand = False;
1222    newClient->freeAfterBadSavePopup = False;
1223    newClient->props = ListInit ();
1224    newClient->discardCommand = NULL;
1225    newClient->saveDiscardCommand = NULL;
1226    newClient->restartHint = SmRestartIfRunning;
1227
1228    ListAddLast (RunningList, (char *) newClient);
1229
1230    if (verbose) {
1231	printf("On IceConn fd = %d, client set up session mngmt protocol\n\n",
1232	       IceConnectionNumber (newClient->ice_conn));
1233    }
1234
1235    /*
1236     * Set up session manager callbacks.
1237     */
1238
1239    *maskRet |= SmsRegisterClientProcMask;
1240    callbacksRet->register_client.callback 	= RegisterClientProc;
1241    callbacksRet->register_client.manager_data  = (SmPointer) newClient;
1242
1243    *maskRet |= SmsInteractRequestProcMask;
1244    callbacksRet->interact_request.callback 	= InteractRequestProc;
1245    callbacksRet->interact_request.manager_data = (SmPointer) newClient;
1246
1247    *maskRet |= SmsInteractDoneProcMask;
1248    callbacksRet->interact_done.callback	= InteractDoneProc;
1249    callbacksRet->interact_done.manager_data    = (SmPointer) newClient;
1250
1251    *maskRet |= SmsSaveYourselfRequestProcMask;
1252    callbacksRet->save_yourself_request.callback     = SaveYourselfReqProc;
1253    callbacksRet->save_yourself_request.manager_data = (SmPointer) newClient;
1254
1255    *maskRet |= SmsSaveYourselfP2RequestProcMask;
1256    callbacksRet->save_yourself_phase2_request.callback =
1257	SaveYourselfPhase2ReqProc;
1258    callbacksRet->save_yourself_phase2_request.manager_data =
1259	(SmPointer) newClient;
1260
1261    *maskRet |= SmsSaveYourselfDoneProcMask;
1262    callbacksRet->save_yourself_done.callback 	   = SaveYourselfDoneProc;
1263    callbacksRet->save_yourself_done.manager_data  = (SmPointer) newClient;
1264
1265    *maskRet |= SmsCloseConnectionProcMask;
1266    callbacksRet->close_connection.callback 	 = CloseConnectionProc;
1267    callbacksRet->close_connection.manager_data  = (SmPointer) newClient;
1268
1269    *maskRet |= SmsSetPropertiesProcMask;
1270    callbacksRet->set_properties.callback 	= SetPropertiesProc;
1271    callbacksRet->set_properties.manager_data   = (SmPointer) newClient;
1272
1273    *maskRet |= SmsDeletePropertiesProcMask;
1274    callbacksRet->delete_properties.callback	= DeletePropertiesProc;
1275    callbacksRet->delete_properties.manager_data   = (SmPointer) newClient;
1276
1277    *maskRet |= SmsGetPropertiesProcMask;
1278    callbacksRet->get_properties.callback	= GetPropertiesProc;
1279    callbacksRet->get_properties.manager_data   = (SmPointer) newClient;
1280
1281    return (1);
1282}
1283
1284
1285
1286/*
1287 * Xt callback invoked when a client attempts to connect.
1288 */
1289
1290static void
1291NewConnectionXtProc(XtPointer client_data, int *source, XtInputId *id)
1292{
1293    IceConn 	ice_conn;
1294    char	*connstr;
1295    IceAcceptStatus status;
1296
1297    if (shutdownInProgress)
1298    {
1299	/*
1300	 * Don't accept new connections if we are in the middle
1301	 * of a shutdown.
1302	 */
1303
1304	return;
1305    }
1306
1307    ice_conn = IceAcceptConnection((IceListenObj) client_data, &status);
1308    if (! ice_conn) {
1309	if (verbose)
1310	    printf ("IceAcceptConnection failed\n");
1311    } else {
1312	IceConnectStatus cstatus;
1313
1314	while ((cstatus = IceConnectionStatus (ice_conn))==IceConnectPending) {
1315	    XtAppProcessEvent (appContext, XtIMAll);
1316	}
1317
1318	if (cstatus == IceConnectAccepted) {
1319	    if (verbose) {
1320		printf ("ICE Connection opened by client, IceConn fd = %d, ",
1321			IceConnectionNumber (ice_conn));
1322		connstr = IceConnectionString (ice_conn);
1323		printf ("Accept at networkId %s\n", connstr);
1324		free (connstr);
1325		printf ("\n");
1326	    }
1327	} else {
1328	    if (verbose)
1329	    {
1330		if (cstatus == IceConnectIOError)
1331		    printf ("IO error opening ICE Connection!\n");
1332		else
1333		    printf ("ICE Connection rejected!\n");
1334	    }
1335
1336	    IceCloseConnection (ice_conn);
1337	}
1338    }
1339}
1340
1341
1342
1343void
1344SetAllSensitive(Bool on)
1345{
1346    XtSetSensitive (mainWindow, on);
1347    SetSaveSensitivity (on);
1348    XtSetSensitive (clientInfoPopup, on);
1349    XtSetSensitive (clientPropPopup, on);
1350
1351    if (on && current_client_selected >= 0)
1352	XawListHighlight (clientListWidget, current_client_selected);
1353}
1354
1355
1356
1357/*
1358 * The real way to handle IO errors is to check the return status
1359 * of IceProcessMessages.  xsm properly does this.
1360 *
1361 * Unfortunately, a design flaw exists in the ICE library in which
1362 * a default IO error handler is invoked if no IO error handler is
1363 * installed.  This default handler exits.  We must avoid this.
1364 *
1365 * To get around this problem, we install an IO error handler that
1366 * does a little magic.  Since a previous IO handler might have been
1367 * installed, when we install our IO error handler, we do a little
1368 * trick to get both the previous IO error handler and the default
1369 * IO error handler.  When our IO error handler is called, if the
1370 * previous handler is not the default handler, we call it.  This
1371 * way, everyone's IO error handler gets called except the stupid
1372 * default one which does an exit!
1373 */
1374
1375static IceIOErrorHandler prev_handler;
1376
1377static void
1378MyIoErrorHandler(IceConn ice_conn)
1379{
1380    if (prev_handler)
1381	(*prev_handler) (ice_conn);
1382}
1383
1384static void
1385InstallIOErrorHandler(void)
1386
1387{
1388    IceIOErrorHandler default_handler;
1389
1390    prev_handler = IceSetIOErrorHandler (NULL);
1391    default_handler = IceSetIOErrorHandler (MyIoErrorHandler);
1392    if (prev_handler == default_handler)
1393	prev_handler = NULL;
1394}
1395
1396static void
1397CloseListeners(void)
1398
1399{
1400    IceFreeListenObjs (numTransports, listenObjs);
1401}
1402
1403