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