1/****************************************************************************** 2 3Copyright 1994, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 25Author: Ralph Mor, X Consortium 26******************************************************************************/ 27 28#include "smproxy.h" 29#include <unistd.h> 30#include <X11/Xmu/WinUtil.h> 31#include <limits.h> 32 33#ifndef HOST_NAME_MAX 34#define HOST_NAME_MAX 256 35#endif 36 37static XtAppContext appContext; 38static Display *disp; 39 40static Atom wmProtocolsAtom; 41static Atom wmSaveYourselfAtom; 42static Atom wmStateAtom; 43static Atom smClientIdAtom; 44static Atom wmClientLeaderAtom; 45 46static Bool debug = 0; 47 48static SmcConn proxy_smcConn; 49static XtInputId proxy_iceInputId; 50static char *proxy_clientId = NULL; 51 52WinInfo *win_head = NULL; 53 54static int proxy_count = 0; 55static int die_count = 0; 56 57static Bool ok_to_die = 0; 58 59static Bool caught_error = 0; 60 61static Bool sent_save_done = 0; 62 63static int Argc; 64static char **Argv; 65 66static Bool HasSaveYourself ( Window window ); 67static Bool HasXSMPsupport ( Window window ); 68static WinInfo * GetClientLeader ( WinInfo *winptr ); 69static char * CheckFullyQuantifiedName ( char *name, int *newstring ); 70static void FinishSaveYourself ( WinInfo *winInfo, Bool has_WM_SAVEYOURSELF ); 71static void SaveYourselfCB ( SmcConn smcConn, SmPointer clientData, int saveType, 72 Bool shutdown, int interactStyle, Bool fast ); 73static void DieCB ( SmcConn smcConn, SmPointer clientData ); 74static void SaveCompleteCB ( SmcConn smcConn, SmPointer clientData ); 75static void ShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData ); 76static void ProcessIceMsgProc ( XtPointer client_data, int *source, XtInputId *id ); 77static void NullIceErrorHandler ( IceConn iceConn, Bool swap, 78 int offendingMinorOpcode, 79 unsigned long offendingSequence, 80 int errorClass, int severity, IcePointer values ); 81static void ConnectClientToSM ( WinInfo *winInfo ); 82static int MyErrorHandler ( Display *display, XErrorEvent *event ); 83static Bool LookupWindow ( Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret ); 84static WinInfo * AddNewWindow ( Window window ); 85static void RemoveWindow ( WinInfo *winptr ); 86static void Got_WM_STATE ( WinInfo *winptr ); 87static void HandleCreate ( XCreateWindowEvent *event ); 88static void HandleDestroy ( XDestroyWindowEvent *event ); 89static void HandleUpdate ( XPropertyEvent *event ); 90static void ProxySaveYourselfPhase2CB ( SmcConn smcConn, SmPointer clientData ); 91static void ProxySaveYourselfCB ( SmcConn smcConn, SmPointer clientData, 92 int saveType, Bool shutdown, int interactStyle, 93 Bool fast ); 94static void ProxyDieCB ( SmcConn smcConn, SmPointer clientData ); 95static void ProxySaveCompleteCB ( SmcConn smcConn, SmPointer clientData ); 96static void ProxyShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData ); 97static Status ConnectProxyToSM ( char *previous_id ); 98static void CheckForExistingWindows ( Window root ); 99 100 101static Bool 102HasSaveYourself(Window window) 103{ 104 Atom *protocols; 105 int numProtocols; 106 int found; 107 108 protocols = NULL; 109 110 if (XGetWMProtocols (disp, window, &protocols, &numProtocols) != True) 111 return (False); 112 113 found = 0; 114 115 if (protocols != NULL) 116 { 117 for (int i = 0; i < numProtocols; i++) { 118 if (protocols[i] == wmSaveYourselfAtom) 119 found = 1; 120 } 121 122 XFree (protocols); 123 } 124 125 return (found); 126} 127 128 129 130static Bool 131HasXSMPsupport(Window window) 132{ 133 XTextProperty tp; 134 Bool hasIt = 0; 135 136 if (XGetTextProperty (disp, window, &tp, smClientIdAtom)) 137 { 138 if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) 139 hasIt = 1; 140 141 if (tp.value) 142 XFree ((char *) tp.value); 143 } 144 145 return (hasIt); 146} 147 148 149 150static WinInfo * 151GetClientLeader(WinInfo *winptr) 152{ 153 Atom actual_type; 154 int actual_format; 155 unsigned long nitems, bytesafter; 156 unsigned long *datap = NULL; 157 WinInfo *leader_winptr = NULL; 158 Bool failure = 0; 159 160 if (XGetWindowProperty (disp, winptr->window, wmClientLeaderAtom, 161 0L, 1L, False, AnyPropertyType, &actual_type, &actual_format, 162 &nitems, &bytesafter, (unsigned char **) &datap) == Success) 163 { 164 if (actual_type == XA_WINDOW && actual_format == 32 && 165 nitems == 1 && bytesafter == 0) 166 { 167 Window leader_win = *((Window *) datap); 168 169 if (!LookupWindow (leader_win, &leader_winptr, NULL)) 170 failure = 1; 171 } 172 173 if (datap) 174 XFree (datap); 175 } 176 177 if (failure) 178 { 179 /* The client leader was defined, but we couldn't find the window */ 180 181 return (NULL); 182 } 183 else if (leader_winptr) 184 { 185 /* We found the real client leader */ 186 187 return (leader_winptr); 188 } 189 else 190 { 191 /* There is no client leader defined, return this window */ 192 193 return (winptr); 194 } 195} 196 197 198 199static char * 200CheckFullyQuantifiedName(char *name, int *newstring) 201{ 202 /* 203 * Due to a bug in Xlib (for hpux in particular), some clients 204 * will have a WM_CLIENT_MACHINE that is not fully quantified. 205 * For example, we might get "excon" instead of "excon.x.org". 206 * This really stinks. The best we can do is tag on our own 207 * domain name, if we can - if not, the unquantified name is 208 * better than nothing. 209 */ 210 211 if (strchr (name, '.') != NULL) 212 { 213 *newstring = 0; 214 return (name); 215 } 216 else 217 { 218 char hostnamebuf[HOST_NAME_MAX + 1] = { 0 }; 219 char *firstDot; 220 221 if (gethostname (hostnamebuf, sizeof hostnamebuf) == 0) 222 firstDot = strchr (hostnamebuf, '.'); 223 else 224 firstDot = NULL; 225 226 if (!firstDot) 227 { 228 *newstring = 0; 229 return (name); 230 } 231 else 232 { 233 char *newptr; 234 235 if (asprintf (&newptr, "%s.%s", name, firstDot + 1) == -1) { 236 *newstring = 0; 237 return (name); 238 } 239 *newstring = 1; 240 return (newptr); 241 } 242 } 243} 244 245 246 247static void 248FinishSaveYourself(WinInfo *winInfo, Bool has_WM_SAVEYOURSELF) 249{ 250 SmProp prop1, prop2, prop3, *props[3]; 251 SmPropValue prop1val, prop2val, prop3val; 252 253 if (!winInfo->got_first_save_yourself) 254 { 255 char userId[20], restartService[80]; 256 char *fullyQuantifiedName; 257 int newstring; 258 259 prop1.name = SmProgram; 260 prop1.type = SmARRAY8; 261 prop1.num_vals = 1; 262 prop1.vals = &prop1val; 263 prop1val.value = (SmPointer) winInfo->wm_command[0]; 264 prop1val.length = strlen (winInfo->wm_command[0]); 265 266 snprintf (userId, sizeof(userId), "%ld", (long)getuid()); 267 prop2.name = SmUserID; 268 prop2.type = SmARRAY8; 269 prop2.num_vals = 1; 270 prop2.vals = &prop2val; 271 prop2val.value = (SmPointer) userId; 272 prop2val.length = strlen (userId); 273 274 fullyQuantifiedName = CheckFullyQuantifiedName ( 275 (char *) winInfo->wm_client_machine.value, &newstring); 276 snprintf (restartService, sizeof(restartService), 277 "rstart-rsh/%s", fullyQuantifiedName); 278 if (newstring) 279 free (fullyQuantifiedName); 280 281 prop3.name = "_XC_RestartService"; 282 prop3.type = SmLISTofARRAY8; 283 prop3.num_vals = 1; 284 prop3.vals = &prop3val; 285 prop3val.value = (SmPointer) restartService; 286 prop3val.length = strlen (restartService); 287 288 props[0] = &prop1; 289 props[1] = &prop2; 290 props[2] = &prop3; 291 292 SmcSetProperties (winInfo->smc_conn, 3, props); 293 294 winInfo->got_first_save_yourself = 1; 295 } 296 297 prop1.name = SmRestartCommand; 298 prop1.type = SmLISTofARRAY8; 299 prop1.num_vals = winInfo->wm_command_count; 300 301 prop1.vals = calloc (winInfo->wm_command_count, sizeof (SmPropValue)); 302 303 if (!prop1.vals) 304 { 305 SmcSaveYourselfDone (winInfo->smc_conn, False); 306 return; 307 } 308 309 for (int i = 0; i < winInfo->wm_command_count; i++) 310 { 311 prop1.vals[i].value = (SmPointer) winInfo->wm_command[i]; 312 prop1.vals[i].length = strlen (winInfo->wm_command[i]); 313 } 314 315 prop2.name = SmCloneCommand; 316 prop2.type = SmLISTofARRAY8; 317 prop2.num_vals = winInfo->wm_command_count; 318 prop2.vals = prop1.vals; 319 320 props[0] = &prop1; 321 props[1] = &prop2; 322 323 SmcSetProperties (winInfo->smc_conn, 2, props); 324 325 free (prop1.vals); 326 327 /* 328 * If the client doesn't support WM_SAVE_YOURSELF, we should 329 * return failure for the save, since we really don't know if 330 * the application needed to save state. 331 */ 332 333 SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF); 334} 335 336 337 338static void 339SaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType, 340 Bool shutdown, int interactStyle, Bool fast) 341{ 342 WinInfo *winInfo = (WinInfo *) clientData; 343 344 if (!winInfo->has_save_yourself) 345 { 346 FinishSaveYourself (winInfo, False); 347 } 348 else 349 { 350 XClientMessageEvent saveYourselfMessage; 351 352 353 /* Send WM_SAVE_YOURSELF */ 354 355 saveYourselfMessage.type = ClientMessage; 356 saveYourselfMessage.window = winInfo->window; 357 saveYourselfMessage.message_type = wmProtocolsAtom; 358 saveYourselfMessage.format = 32; 359 saveYourselfMessage.data.l[0] = wmSaveYourselfAtom; 360 saveYourselfMessage.data.l[1] = CurrentTime; 361 362 if (XSendEvent (disp, winInfo->window, False, NoEventMask, 363 (XEvent *) &saveYourselfMessage)) 364 { 365 winInfo->waiting_for_update = 1; 366 367 if (debug) 368 { 369 printf ("Sent SAVE YOURSELF to 0x%x\n", 370 (unsigned int)winInfo->window); 371 printf ("\n"); 372 } 373 } 374 else 375 { 376 if (debug) 377 { 378 printf ("Failed to send SAVE YOURSELF to 0x%x\n", 379 (unsigned int)winInfo->window); 380 printf ("\n"); 381 } 382 } 383 } 384} 385 386 387 388static void 389DieCB(SmcConn smcConn, SmPointer clientData) 390{ 391 WinInfo *winInfo = (WinInfo *) clientData; 392 393 SmcCloseConnection (winInfo->smc_conn, 0, NULL); 394 winInfo->smc_conn = NULL; 395 XtRemoveInput (winInfo->input_id); 396 397 /* Now tell the client to die */ 398 399 if (debug) 400 printf ("Trying to kill 0x%x\n", (unsigned int)winInfo->window); 401 402 XSync (disp, 0); 403 XKillClient (disp, winInfo->window); 404 XSync (disp, 0); 405 406 407 /* 408 * Proxy must exit when all clients die, and the proxy itself 409 * must have received a Die. 410 */ 411 412 die_count++; 413 414 if (die_count == proxy_count && ok_to_die) 415 { 416 exit (0); 417 } 418} 419 420 421 422static void 423SaveCompleteCB(SmcConn smcConn, SmPointer clientData) 424{ 425 /* 426 * Nothing to do here. 427 */ 428} 429 430 431 432static void 433ShutdownCancelledCB(SmcConn smcConn, SmPointer clientData) 434{ 435 /* 436 * Since we did not request to interact or request save yourself 437 * phase 2, we know we already sent the save yourself done, so 438 * there is nothing to do here. 439 */ 440} 441 442 443 444static void 445ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id) 446{ 447 IceConn ice_conn = (IceConn) client_data; 448 449 IceProcessMessages (ice_conn, NULL, NULL); 450} 451 452 453 454static void 455NullIceErrorHandler(IceConn iceConn, Bool swap, int offendingMinorOpcode, 456 unsigned long offendingSequence, int errorClass, 457 int severity, IcePointer values) 458{ 459 return; 460} 461 462 463static void 464ConnectClientToSM(WinInfo *winInfo) 465{ 466 char errorMsg[256]; 467 unsigned long mask; 468 SmcCallbacks callbacks; 469 IceConn ice_conn; 470 char *prevId; 471 472 mask = SmcSaveYourselfProcMask | SmcDieProcMask | 473 SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; 474 475 callbacks.save_yourself.callback = SaveYourselfCB; 476 callbacks.save_yourself.client_data = (SmPointer) winInfo; 477 478 callbacks.die.callback = DieCB; 479 callbacks.die.client_data = (SmPointer) winInfo; 480 481 callbacks.save_complete.callback = SaveCompleteCB; 482 callbacks.save_complete.client_data = (SmPointer) winInfo; 483 484 callbacks.shutdown_cancelled.callback = ShutdownCancelledCB; 485 callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo; 486 487 prevId = LookupClientID (winInfo); 488 489 /* 490 * In case a protocol error occurs when opening the connection, 491 * (e.g. an authentication error), we set a null error handler 492 * before the open, then restore the default handler after the open. 493 */ 494 495 IceSetErrorHandler (NullIceErrorHandler); 496 497 winInfo->smc_conn = SmcOpenConnection ( 498 NULL, /* use SESSION_MANAGER env */ 499 (SmPointer) winInfo, /* force a new connection */ 500 SmProtoMajor, 501 SmProtoMinor, 502 mask, 503 &callbacks, 504 prevId, 505 &winInfo->client_id, 506 256, errorMsg); 507 508 IceSetErrorHandler (NULL); 509 510 if (winInfo->smc_conn == NULL) 511 return; 512 513 ice_conn = SmcGetIceConnection (winInfo->smc_conn); 514 515 winInfo->input_id = XtAppAddInput ( 516 appContext, 517 IceConnectionNumber (ice_conn), 518 (XtPointer) XtInputReadMask, 519 ProcessIceMsgProc, 520 (XtPointer) ice_conn); 521 522 if (debug) 523 { 524 printf ("Connected to SM, window = 0x%x\n", 525 (unsigned int)winInfo->window); 526 printf ("\n"); 527 } 528 529 proxy_count++; 530} 531 532 533 534static int 535MyErrorHandler(Display *display, XErrorEvent *event) 536{ 537 caught_error = 1; 538 return 0; 539} 540 541 542 543static Bool 544LookupWindow(Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret) 545{ 546 WinInfo *ptr, *prev; 547 548 ptr = win_head; 549 prev = NULL; 550 551 while (ptr) 552 { 553 if (ptr->window == window) 554 break; 555 else 556 { 557 prev = ptr; 558 ptr = ptr->next; 559 } 560 } 561 562 if (ptr) 563 { 564 if (ptr_ret) 565 *ptr_ret = ptr; 566 if (prev_ptr_ret) 567 *prev_ptr_ret = prev; 568 return (1); 569 } 570 else 571 return (0); 572} 573 574 575 576static WinInfo * 577AddNewWindow(Window window) 578{ 579 WinInfo *newptr; 580 581 if (LookupWindow (window, NULL, NULL)) 582 return (NULL); 583 584 newptr = malloc (sizeof (WinInfo)); 585 586 if (newptr == NULL) 587 return (NULL); 588 589 newptr->next = win_head; 590 win_head = newptr; 591 592 newptr->window = window; 593 newptr->smc_conn = NULL; 594 newptr->tested_for_sm_client_id = 0; 595 newptr->client_id = NULL; 596 newptr->wm_command = NULL; 597 newptr->wm_command_count = 0; 598 newptr->class.res_name = NULL; 599 newptr->class.res_class = NULL; 600 newptr->wm_name = NULL; 601 newptr->wm_client_machine.value = NULL; 602 newptr->wm_client_machine.nitems = 0; 603 newptr->has_save_yourself = 0; 604 newptr->waiting_for_update = 0; 605 newptr->got_first_save_yourself = 0; 606 607 return (newptr); 608} 609 610 611 612static void 613RemoveWindow(WinInfo *winptr) 614{ 615 WinInfo *ptr, *prev; 616 617 if (LookupWindow (winptr->window, &ptr, &prev)) 618 { 619 if (prev == NULL) 620 win_head = ptr->next; 621 else 622 prev->next = ptr->next; 623 624 if (ptr->client_id) 625 free (ptr->client_id); 626 627 if (ptr->wm_command) 628 XFreeStringList (ptr->wm_command); 629 630 if (ptr->wm_name) 631 XFree (ptr->wm_name); 632 633 if (ptr->wm_client_machine.value) 634 XFree (ptr->wm_client_machine.value); 635 636 if (ptr->class.res_name) 637 XFree (ptr->class.res_name); 638 639 if (ptr->class.res_class) 640 XFree (ptr->class.res_class); 641 642 free ((char *) ptr); 643 } 644} 645 646 647 648static void 649Got_WM_STATE(WinInfo *winptr) 650{ 651 WinInfo *leader_winptr; 652 653 /* 654 * If we already got WM_STATE and tested for SM_CLIENT_ID, we 655 * shouldn't do it again. 656 */ 657 658 if (winptr->tested_for_sm_client_id) 659 { 660 return; 661 } 662 663 664 /* 665 * Set a null error handler, in case this window goes away 666 * behind our back. 667 */ 668 669 caught_error = 0; 670 XSetErrorHandler (MyErrorHandler); 671 672 673 /* 674 * Get the client leader window. 675 */ 676 677 leader_winptr = GetClientLeader (winptr); 678 679 if (caught_error) 680 { 681 caught_error = 0; 682 RemoveWindow (winptr); 683 XSetErrorHandler (NULL); 684 return; 685 } 686 687 688 /* 689 * If we already checked for SM_CLIENT_ID on the client leader 690 * window, don't do it again. 691 */ 692 693 if (!leader_winptr || leader_winptr->tested_for_sm_client_id) 694 { 695 caught_error = 0; 696 XSetErrorHandler (NULL); 697 return; 698 } 699 700 leader_winptr->tested_for_sm_client_id = 1; 701 702 if (!HasXSMPsupport (leader_winptr->window)) 703 { 704 XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name); 705 706 XGetCommand (disp, leader_winptr->window, 707 &leader_winptr->wm_command, 708 &leader_winptr->wm_command_count); 709 710 XGetClassHint (disp, leader_winptr->window, &leader_winptr->class); 711 712 XGetWMClientMachine (disp, leader_winptr->window, 713 &leader_winptr->wm_client_machine); 714 715 if (leader_winptr->wm_name != NULL && 716 leader_winptr->wm_command != NULL && 717 leader_winptr->wm_command_count > 0 && 718 leader_winptr->class.res_name != NULL && 719 leader_winptr->class.res_class != NULL && 720 leader_winptr->wm_client_machine.value != NULL && 721 leader_winptr->wm_client_machine.nitems != 0) 722 { 723 leader_winptr->has_save_yourself = 724 HasSaveYourself (leader_winptr->window); 725 726 ConnectClientToSM (leader_winptr); 727 } 728 } 729 730 XSync (disp, 0); 731 XSetErrorHandler (NULL); 732 733 if (caught_error) 734 { 735 caught_error = 0; 736 RemoveWindow (leader_winptr); 737 } 738} 739 740 741 742static void 743HandleCreate(XCreateWindowEvent *event) 744{ 745 Atom actual_type; 746 int actual_format; 747 unsigned long nitems, bytesafter; 748 unsigned long *datap = NULL; 749 WinInfo *winptr; 750 Bool got_wm_state = 0; 751 752 /* 753 * We are waiting for all proxy connections to close so we can die. 754 * Don't handle new connections. 755 */ 756 757 if (ok_to_die) 758 return; 759 760 761 /* 762 * Add the new window 763 */ 764 765 if ((winptr = AddNewWindow (event->window)) == NULL) 766 return; 767 768 769 /* 770 * Right after the window was created, it might have been destroyed, 771 * so the following Xlib calls might fail. Need to catch the error 772 * by installing an error handler. 773 */ 774 775 caught_error = 0; 776 XSetErrorHandler (MyErrorHandler); 777 778 779 /* 780 * Select for Property Notify on the window so we can determine 781 * when WM_STATE is defined. To avoid a race condition, we must 782 * do this _before_ we check for WM_STATE right now. 783 * 784 * Select for Substructure Notify so we can determine when the 785 * window is destroyed. 786 */ 787 788 XSelectInput (disp, event->window, 789 SubstructureNotifyMask | PropertyChangeMask); 790 791 792 /* 793 * WM_STATE may already be there. Check now. 794 */ 795 796 if (XGetWindowProperty (disp, event->window, wmStateAtom, 797 0L, 2L, False, AnyPropertyType, 798 &actual_type, &actual_format, &nitems, &bytesafter, 799 (unsigned char **) &datap) == Success && datap) 800 { 801 if (nitems > 0) 802 got_wm_state = 1; 803 804 if (datap) 805 XFree ((char *) datap); 806 } 807 808 XSync (disp, 0); 809 XSetErrorHandler (NULL); 810 811 if (caught_error) 812 { 813 caught_error = 0; 814 RemoveWindow (winptr); 815 } 816 else if (got_wm_state) 817 { 818 Got_WM_STATE (winptr); 819 } 820} 821 822 823 824static void 825HandleDestroy(XDestroyWindowEvent *event) 826{ 827 WinInfo *winptr; 828 829 if (LookupWindow (event->window, &winptr, NULL)) 830 { 831 if (winptr->smc_conn) 832 { 833 SmcCloseConnection (winptr->smc_conn, 0, NULL); 834 XtRemoveInput (winptr->input_id); 835 proxy_count--; 836 } 837 838 if (debug) 839 { 840 printf ("Removed window (window = 0x%x)\n", 841 (unsigned int)winptr->window); 842 printf ("\n"); 843 } 844 845 RemoveWindow (winptr); 846 } 847} 848 849 850 851static void 852HandleUpdate(XPropertyEvent *event) 853{ 854 Window window = event->window; 855 WinInfo *winptr; 856 857 if (!LookupWindow (window, &winptr, NULL)) 858 return; 859 860 if (event->atom == wmStateAtom) 861 { 862 Got_WM_STATE (winptr); 863 } 864 else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update) 865 { 866 /* Finish off the Save Yourself */ 867 868 if (winptr->wm_command) 869 { 870 XFreeStringList (winptr->wm_command); 871 winptr->wm_command = NULL; 872 winptr->wm_command_count = 0; 873 } 874 875 XGetCommand (disp, window, 876 &winptr->wm_command, 877 &winptr->wm_command_count); 878 879 winptr->waiting_for_update = 0; 880 FinishSaveYourself (winptr, True); 881 } 882} 883 884 885 886static void 887ProxySaveYourselfPhase2CB(SmcConn smcConn, SmPointer clientData) 888{ 889 char *filename; 890 Bool success = True; 891 SmProp prop1, prop2, prop3, *props[3]; 892 SmPropValue prop1val, prop2val, prop3val; 893 char *discardCommand; 894 int numVals; 895 static int first_time = 1; 896 897 if (first_time) 898 { 899 char userId[20]; 900 char hint = SmRestartIfRunning; 901 902 prop1.name = SmProgram; 903 prop1.type = SmARRAY8; 904 prop1.num_vals = 1; 905 prop1.vals = &prop1val; 906 prop1val.value = Argv[0]; 907 prop1val.length = strlen (Argv[0]); 908 909 snprintf (userId, sizeof(userId), "%ld", (long)getuid()); 910 prop2.name = SmUserID; 911 prop2.type = SmARRAY8; 912 prop2.num_vals = 1; 913 prop2.vals = &prop2val; 914 prop2val.value = (SmPointer) userId; 915 prop2val.length = strlen (userId); 916 917 prop3.name = SmRestartStyleHint; 918 prop3.type = SmCARD8; 919 prop3.num_vals = 1; 920 prop3.vals = &prop3val; 921 prop3val.value = (SmPointer) &hint; 922 prop3val.length = 1; 923 924 props[0] = &prop1; 925 props[1] = &prop2; 926 props[2] = &prop3; 927 928 SmcSetProperties (smcConn, 3, props); 929 930 first_time = 0; 931 } 932 933 if ((filename = WriteProxyFile ()) == NULL) 934 { 935 success = False; 936 goto finishUp; 937 } 938 939 prop1.name = SmRestartCommand; 940 prop1.type = SmLISTofARRAY8; 941 942 prop1.vals = calloc ((Argc + 4), sizeof (SmPropValue)); 943 944 if (!prop1.vals) 945 { 946 success = False; 947 goto finishUp; 948 } 949 950 numVals = 0; 951 952 for (int i = 0; i < Argc; i++) 953 { 954 if (strcmp (Argv[i], "-clientId") == 0 || 955 strcmp (Argv[i], "-restore") == 0) 956 { 957 i++; 958 } 959 else 960 { 961 prop1.vals[numVals].value = (SmPointer) Argv[i]; 962 prop1.vals[numVals++].length = strlen (Argv[i]); 963 } 964 } 965 966 prop1.vals[numVals].value = (SmPointer) "-clientId"; 967 prop1.vals[numVals++].length = 9; 968 969 prop1.vals[numVals].value = (SmPointer) proxy_clientId; 970 prop1.vals[numVals++].length = strlen (proxy_clientId); 971 972 prop1.vals[numVals].value = (SmPointer) "-restore"; 973 prop1.vals[numVals++].length = 8; 974 975 prop1.vals[numVals].value = (SmPointer) filename; 976 prop1.vals[numVals++].length = strlen (filename); 977 978 prop1.num_vals = numVals; 979 980 981 if (asprintf (&discardCommand, "rm %s", filename) == -1) { 982 success = False; 983 goto finishUp; 984 } 985 prop2.name = SmDiscardCommand; 986 prop2.type = SmARRAY8; 987 prop2.num_vals = 1; 988 prop2.vals = &prop2val; 989 prop2val.value = (SmPointer) discardCommand; 990 prop2val.length = strlen (discardCommand); 991 992 props[0] = &prop1; 993 props[1] = &prop2; 994 995 SmcSetProperties (smcConn, 2, props); 996 free (prop1.vals); 997 free (discardCommand); 998 999 finishUp: 1000 1001 SmcSaveYourselfDone (smcConn, success); 1002 sent_save_done = 1; 1003 1004 if (filename) 1005 free (filename); 1006} 1007 1008 1009 1010static void 1011ProxySaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType, 1012 Bool shutdown, int interactStyle, Bool fast) 1013{ 1014 /* 1015 * We want the proxy to respond to the Save Yourself after all 1016 * the regular XSMP clients have finished with the save (and possibly 1017 * interacted with the user). 1018 */ 1019 1020 if (!SmcRequestSaveYourselfPhase2 (smcConn, 1021 ProxySaveYourselfPhase2CB, NULL)) 1022 { 1023 SmcSaveYourselfDone (smcConn, False); 1024 sent_save_done = 1; 1025 } 1026 else 1027 sent_save_done = 0; 1028} 1029 1030 1031 1032static void 1033ProxyDieCB(SmcConn smcConn, SmPointer clientData) 1034{ 1035 SmcCloseConnection (proxy_smcConn, 0, NULL); 1036 XtRemoveInput (proxy_iceInputId); 1037 1038 if (die_count == proxy_count) 1039 exit (0); 1040 else 1041 ok_to_die = 1; 1042} 1043 1044 1045 1046static void 1047ProxySaveCompleteCB(SmcConn smcConn, SmPointer clientData) 1048{ 1049 ; 1050} 1051 1052 1053 1054static void 1055ProxyShutdownCancelledCB(SmcConn smcConn, SmPointer clientData) 1056{ 1057 if (!sent_save_done) 1058 { 1059 SmcSaveYourselfDone (smcConn, False); 1060 sent_save_done = 1; 1061 } 1062} 1063 1064 1065 1066static Status 1067ConnectProxyToSM(char *previous_id) 1068{ 1069 char errorMsg[256]; 1070 unsigned long mask; 1071 SmcCallbacks callbacks; 1072 IceConn iceConn; 1073 1074 mask = SmcSaveYourselfProcMask | SmcDieProcMask | 1075 SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; 1076 1077 callbacks.save_yourself.callback = ProxySaveYourselfCB; 1078 callbacks.save_yourself.client_data = (SmPointer) NULL; 1079 1080 callbacks.die.callback = ProxyDieCB; 1081 callbacks.die.client_data = (SmPointer) NULL; 1082 1083 callbacks.save_complete.callback = ProxySaveCompleteCB; 1084 callbacks.save_complete.client_data = (SmPointer) NULL; 1085 1086 callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB; 1087 callbacks.shutdown_cancelled.client_data = (SmPointer) NULL; 1088 1089 proxy_smcConn = SmcOpenConnection ( 1090 NULL, /* use SESSION_MANAGER env */ 1091 (SmPointer) appContext, 1092 SmProtoMajor, 1093 SmProtoMinor, 1094 mask, 1095 &callbacks, 1096 previous_id, 1097 &proxy_clientId, 1098 256, errorMsg); 1099 1100 if (proxy_smcConn == NULL) 1101 return (0); 1102 1103 iceConn = SmcGetIceConnection (proxy_smcConn); 1104 1105 proxy_iceInputId = XtAppAddInput ( 1106 appContext, 1107 IceConnectionNumber (iceConn), 1108 (XtPointer) XtInputReadMask, 1109 ProcessIceMsgProc, 1110 (XtPointer) iceConn); 1111 1112 return (1); 1113} 1114 1115 1116 1117static void 1118CheckForExistingWindows(Window root) 1119{ 1120 Window dontCare1, dontCare2, *children; 1121 unsigned int nchildren, i; 1122 1123 /* 1124 * We query the root tree for all windows created thus far. 1125 * Note that at any moment after XQueryTree is called, a 1126 * window may be deleted. So we must take extra care to make 1127 * sure a window really exists. 1128 */ 1129 1130 XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren); 1131 1132 for (i = 0; i < nchildren; i++) 1133 { 1134 Window client_window; 1135 XCreateWindowEvent event = { .window = children[i] }; 1136 1137 HandleCreate (&event); 1138 1139 caught_error = 0; 1140 XSetErrorHandler (MyErrorHandler); 1141 1142 client_window = XmuClientWindow (disp, children[i]); 1143 1144 XSetErrorHandler (NULL); 1145 1146 if (!caught_error && client_window != children[i]) 1147 { 1148 event.window = client_window; 1149 HandleCreate (&event); 1150 } 1151 } 1152} 1153 1154 1155 1156int 1157main (int argc, char *argv[]) 1158{ 1159 char *restore_filename = NULL; 1160 char *client_id = NULL; 1161 int i, zero = 0; 1162 const char * const usage = 1163 "usage: %s [-clientId id] [-restore file] [-debug] [-version]\n"; 1164 1165 Argc = argc; 1166 Argv = argv; 1167 1168 for (i = 1; i < argc; i++) 1169 { 1170 if (argv[i][0] == '-') 1171 { 1172 switch (argv[i][1]) 1173 { 1174 case 'd': /* -debug */ 1175 debug = 1; 1176 continue; 1177 1178 case 'c': /* -clientId */ 1179 if (++i >= argc) { 1180 fprintf (stderr, "%s: -clientId requires an argument\n", 1181 argv[0]); 1182 goto usage; 1183 } 1184 client_id = argv[i]; 1185 continue; 1186 1187 case 'r': /* -restore */ 1188 if (++i >= argc) { 1189 fprintf (stderr, "%s: -restore requires an argument\n", 1190 argv[0]); 1191 goto usage; 1192 } 1193 restore_filename = argv[i]; 1194 continue; 1195 1196 case 'v': 1197 puts (PACKAGE_STRING); 1198 exit (0); 1199 1200 case '-': 1201 if (strcmp(argv[i], "--help") == 0) { 1202 fprintf (stdout, usage, argv[0]); 1203 exit (0); 1204 } 1205 if (strcmp(argv[i], "--version") == 0) { 1206 puts (PACKAGE_STRING); 1207 exit (0); 1208 } 1209 break; 1210 } 1211 } 1212 1213 fprintf (stderr, "%s: unrecognized argument: %s\n", argv[0], argv[i]); 1214 1215 usage: 1216 1217 fprintf (stderr, usage, argv[0]); 1218 exit (1); 1219 } 1220 1221 1222 XtToolkitInitialize (); 1223 appContext = XtCreateApplicationContext (); 1224 1225 if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY", 1226 NULL, 0, &zero, NULL))) 1227 { 1228 fprintf (stderr, "smproxy: unable to open display\n"); 1229 exit (1); 1230 } 1231 1232 if (restore_filename) 1233 ReadProxyFile (restore_filename); 1234 1235 if (!ConnectProxyToSM (client_id)) 1236 { 1237 fprintf (stderr, "smproxy: unable to connect to session manager\n"); 1238 exit (1); 1239 } 1240 1241 wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False); 1242 wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False); 1243 wmStateAtom = XInternAtom (disp, "WM_STATE", False); 1244 smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False); 1245 wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False); 1246 1247 for (i = 0; i < ScreenCount (disp); i++) 1248 { 1249 Window root = RootWindow (disp, i); 1250 XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask); 1251 CheckForExistingWindows (root); 1252 } 1253 1254 while (1) 1255 { 1256 XEvent event; 1257 1258 XtAppNextEvent (appContext, &event); 1259 1260 switch (event.type) 1261 { 1262 case CreateNotify: 1263 HandleCreate (&event.xcreatewindow); 1264 break; 1265 1266 case DestroyNotify: 1267 HandleDestroy (&event.xdestroywindow); 1268 break; 1269 1270 case PropertyNotify: 1271 HandleUpdate (&event.xproperty); 1272 break; 1273 1274 default: 1275 XtDispatchEvent (&event); 1276 break; 1277 } 1278 } 1279 exit(0); 1280} 1281