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