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