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