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