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