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