1 /* $Xorg: xsm.c,v 1.7 2001/02/09 02:06:01 xorgcvs Exp $ */
2 /******************************************************************************
3 
4 Copyright 1993, 1998  The Open Group
5 
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose is hereby granted without fee, provided that
8 the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation.
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 Except as contained in this notice, the name of The Open Group shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in 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 
53 int		Argc;
54 char		**Argv;
55 
56 List		*RunningList;
57 List		*PendingList;
58 List		*RestartAnywayList;
59 List		*RestartImmedList;
60 
61 List		*WaitForSaveDoneList;
62 static List	*InitialSaveList;
63 List		*FailedSaveList;
64 List		*WaitForInteractList;
65 List		*WaitForPhase2List;
66 
67 Bool		wantShutdown = False;
68 Bool		shutdownInProgress = False;
69 Bool		phase2InProgress = False;
70 Bool		saveInProgress = False;
71 Bool		shutdownCancelled = False;
72 
73 Bool		verbose = False;
74 
75 char		*sm_id = NULL;
76 
77 char		*networkIds = NULL;
78 char		*session_name = NULL;
79 
80 IceAuthDataEntry *authDataEntries = NULL;
81 int		numTransports = 0;
82 
83 Bool		client_info_visible = False;
84 Bool		client_prop_visible = False;
85 Bool		client_log_visible = False;
86 
87 String 		*clientListNames = NULL;
88 ClientRec	**clientListRecs = NULL;
89 int		numClientListNames = 0;
90 
91 int		current_client_selected;
92 
93 int		sessionNameCount = 0;
94 String		*sessionNamesShort = NULL;
95 String		*sessionNamesLong = NULL;
96 Bool		*sessionsLocked = NULL;
97 
98 int		num_clients_in_last_session = -1;
99 
100 char		**non_session_aware_clients = NULL;
101 int		non_session_aware_count = 0;
102 
103 char		*display_env = NULL, *non_local_display_env = NULL;
104 char		*session_env = NULL, *non_local_session_env = NULL;
105 char		*audio_env = NULL;
106 
107 Bool		need_to_name_session = False;
108 
109 Bool		remote_allowed;
110 
111 XtAppContext	appContext;
112 Widget		topLevel;
113 
114 XtSignalId	sig_term_id, sig_usr1_id;
115 
116 static Atom wmStateAtom;
117 static Atom wmDeleteAtom;
118 static char *cmd_line_display = NULL;
119 
120 /*
121  * Forward declarations
122  */
123 static void PropertyChangeXtHandler(Widget w, XtPointer closure,
124 				    XEvent *event,
125 				    Boolean *continue_to_dispatch);
126 static void GetEnvironment(void);
127 static Status RegisterClientProc(SmsConn smsConn, SmPointer managerData,
128 				 char *previousId);
129 static Bool OkToEnterInteractPhase(void);
130 static void InteractRequestProc(SmsConn smsConn, SmPointer managerData,
131 				int dialogType);
132 static void InteractDoneProc(SmsConn smsConn, SmPointer managerData,
133 			     Bool cancelShutdown);
134 static void SaveYourselfReqProc(SmsConn smsConn, SmPointer managerData,
135 				int saveType, Bool shutdown,
136 				int interactStyle, Bool fast, Bool global);
137 static Bool OkToEnterPhase2(void);
138 static void SaveYourselfPhase2ReqProc(SmsConn smsConn, SmPointer managerData);
139 static void SaveYourselfDoneProc(SmsConn smsConn, SmPointer managerData,
140 				 Bool success);
141 static void CloseConnectionProc(SmsConn smsConn, SmPointer managerData,
142 				int count, char **reasonMsgs);
143 static Status NewClientProc(SmsConn smsConn, SmPointer managerData,
144 			    unsigned long *maskRet,
145 			    SmsCallbacks *callbacksRet,
146 			    char **failureReasonRet);
147 static void NewConnectionXtProc(XtPointer client_data, int *source,
148 				XtInputId *id);
149 static void MyIoErrorHandler(IceConn ice_conn);
150 static void InstallIOErrorHandler(void);
151 static void CloseListeners(void);
152 
153 
154 static IceListenObj *listenObjs;
155 
156 
157 /*
158  * Main program
159  */
160 int
main(int argc,char * argv[])161 main(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 
383 static void
PropertyChangeXtHandler(Widget w,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)384 PropertyChangeXtHandler(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 
411 void
SetWM_DELETE_WINDOW(Widget widget,const _XtString delAction)412 SetWM_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 
426 static void
GetEnvironment(void)427 GetEnvironment(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 
514 Status
StartSession(char * name,Bool use_default)515 StartSession(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 
656 void
EndSession(int status)657 EndSession(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 
690 void
FreeClient(ClientRec * client,Bool freeProps)691 FreeClient(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 
722 static Status
RegisterClientProc(SmsConn smsConn,SmPointer managerData,char * previousId)723 RegisterClientProc(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 
852 static Bool
OkToEnterInteractPhase(void)853 OkToEnterInteractPhase(void)
854 {
855     return ((ListCount (WaitForInteractList) +
856 	ListCount (WaitForPhase2List)) == ListCount (WaitForSaveDoneList));
857 }
858 
859 
860 
861 static void
InteractRequestProc(SmsConn smsConn,SmPointer managerData,int dialogType)862 InteractRequestProc(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 
888 static void
InteractDoneProc(SmsConn smsConn,SmPointer managerData,Bool cancelShutdown)889 InteractDoneProc(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 
955 static void
SaveYourselfReqProc(SmsConn smsConn,SmPointer managerData,int saveType,Bool shutdown,int interactStyle,Bool fast,Bool global)956 SaveYourselfReqProc(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 
965 static Bool
OkToEnterPhase2(void)966 OkToEnterPhase2(void)
967 
968 {
969     return (ListCount (WaitForPhase2List) == ListCount (WaitForSaveDoneList));
970 }
971 
972 
973 
974 static void
SaveYourselfPhase2ReqProc(SmsConn smsConn,SmPointer managerData)975 SaveYourselfPhase2ReqProc(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 
1012 static void
SaveYourselfDoneProc(SmsConn smsConn,SmPointer managerData,Bool success)1013 SaveYourselfDoneProc(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 
1054 void
CloseDownClient(ClientRec * client)1055 CloseDownClient(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 
1168 static void
CloseConnectionProc(SmsConn smsConn,SmPointer managerData,int count,char ** reasonMsgs)1169 CloseConnectionProc(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 
1193 static Status
NewClientProc(SmsConn smsConn,SmPointer managerData,unsigned long * maskRet,SmsCallbacks * callbacksRet,char ** failureReasonRet)1194 NewClientProc(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 
1286 static void
NewConnectionXtProc(XtPointer client_data,int * source,XtInputId * id)1287 NewConnectionXtProc(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 
1339 void
SetAllSensitive(Bool on)1340 SetAllSensitive(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 
1371 static IceIOErrorHandler prev_handler;
1372 
1373 static void
MyIoErrorHandler(IceConn ice_conn)1374 MyIoErrorHandler(IceConn ice_conn)
1375 {
1376     if (prev_handler)
1377 	(*prev_handler) (ice_conn);
1378 }
1379 
1380 static void
InstallIOErrorHandler(void)1381 InstallIOErrorHandler(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 
1392 static void
CloseListeners(void)1393 CloseListeners(void)
1394 
1395 {
1396     IceFreeListenObjs (numTransports, listenObjs);
1397 }
1398 
1399