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