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