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