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