smproxy.c 27 KB

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