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