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