Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:expandtab:shiftwidth=2:tabstop=8:
3 : */
4 : /* vim:set ts=8 sw=2 et cindent: */
5 : /* This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 :
9 : #include "mozilla/ArrayUtils.h"
10 : #include "mozilla/IntegerPrintfMacros.h"
11 : #include "mozilla/Sprintf.h"
12 : #include "XRemoteClient.h"
13 : #include "plstr.h"
14 : #include "prsystem.h"
15 : #include "mozilla/Logging.h"
16 : #include "prenv.h"
17 : #include "prdtoa.h"
18 : #include <stdlib.h>
19 : #include <unistd.h>
20 : #include <string.h>
21 : #include <strings.h>
22 : #include <sys/time.h>
23 : #include <sys/types.h>
24 : #include <unistd.h>
25 : #include <limits.h>
26 : #include <X11/Xatom.h>
27 :
28 : #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
29 : #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK"
30 : #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
31 : #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE"
32 : #define MOZILLA_USER_PROP "_MOZILLA_USER"
33 : #define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE"
34 : #define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM"
35 :
36 : #ifdef IS_BIG_ENDIAN
37 : #define TO_LITTLE_ENDIAN32(x) \
38 : ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
39 : (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
40 : #else
41 : #define TO_LITTLE_ENDIAN32(x) (x)
42 : #endif
43 :
44 : #ifndef MAX_PATH
45 : #ifdef PATH_MAX
46 : #define MAX_PATH PATH_MAX
47 : #else
48 : #define MAX_PATH 1024
49 : #endif
50 : #endif
51 :
52 : using mozilla::LogLevel;
53 :
54 : static mozilla::LazyLogModule sRemoteLm("XRemoteClient");
55 :
56 : static int (*sOldHandler)(Display *, XErrorEvent *);
57 : static bool sGotBadWindow;
58 :
59 0 : XRemoteClient::XRemoteClient()
60 : {
61 0 : mDisplay = 0;
62 0 : mInitialized = false;
63 0 : mMozVersionAtom = 0;
64 0 : mMozLockAtom = 0;
65 0 : mMozCommandLineAtom = 0;
66 0 : mMozResponseAtom = 0;
67 0 : mMozWMStateAtom = 0;
68 0 : mMozUserAtom = 0;
69 0 : mMozProfileAtom = 0;
70 0 : mMozProgramAtom = 0;
71 0 : mLockData = 0;
72 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::XRemoteClient"));
73 0 : }
74 :
75 0 : XRemoteClient::~XRemoteClient()
76 : {
77 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::~XRemoteClient"));
78 0 : if (mInitialized)
79 0 : Shutdown();
80 0 : }
81 :
82 : // Minimize the roundtrips to the X-server
83 : static const char *XAtomNames[] = {
84 : MOZILLA_VERSION_PROP,
85 : MOZILLA_LOCK_PROP,
86 : MOZILLA_RESPONSE_PROP,
87 : "WM_STATE",
88 : MOZILLA_USER_PROP,
89 : MOZILLA_PROFILE_PROP,
90 : MOZILLA_PROGRAM_PROP,
91 : MOZILLA_COMMANDLINE_PROP
92 : };
93 : static Atom XAtoms[MOZ_ARRAY_LENGTH(XAtomNames)];
94 :
95 : nsresult
96 0 : XRemoteClient::Init()
97 : {
98 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::Init"));
99 :
100 0 : if (mInitialized)
101 0 : return NS_OK;
102 :
103 : // try to open the display
104 0 : mDisplay = XOpenDisplay(0);
105 0 : if (!mDisplay)
106 0 : return NS_ERROR_FAILURE;
107 :
108 : // get our atoms
109 0 : XInternAtoms(mDisplay, const_cast<char**>(XAtomNames),
110 0 : MOZ_ARRAY_LENGTH(XAtomNames), False, XAtoms);
111 :
112 0 : int i = 0;
113 0 : mMozVersionAtom = XAtoms[i++];
114 0 : mMozLockAtom = XAtoms[i++];
115 0 : mMozResponseAtom = XAtoms[i++];
116 0 : mMozWMStateAtom = XAtoms[i++];
117 0 : mMozUserAtom = XAtoms[i++];
118 0 : mMozProfileAtom = XAtoms[i++];
119 0 : mMozProgramAtom = XAtoms[i++];
120 0 : mMozCommandLineAtom = XAtoms[i++];
121 :
122 0 : mInitialized = true;
123 :
124 0 : return NS_OK;
125 : }
126 :
127 : void
128 0 : XRemoteClient::Shutdown (void)
129 : {
130 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::Shutdown"));
131 :
132 0 : if (!mInitialized)
133 0 : return;
134 :
135 : // shut everything down
136 0 : XCloseDisplay(mDisplay);
137 0 : mDisplay = 0;
138 0 : mInitialized = false;
139 0 : if (mLockData) {
140 0 : free(mLockData);
141 0 : mLockData = 0;
142 : }
143 : }
144 :
145 : static int
146 0 : HandleBadWindow(Display *display, XErrorEvent *event)
147 : {
148 0 : if (event->error_code == BadWindow) {
149 0 : sGotBadWindow = true;
150 0 : return 0; // ignored
151 : }
152 :
153 0 : return (*sOldHandler)(display, event);
154 :
155 : }
156 :
157 : nsresult
158 0 : XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
159 : const char *aProfile,
160 : int32_t argc, char **argv,
161 : const char* aDesktopStartupID,
162 : char **aResponse, bool *aWindowFound)
163 : {
164 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::SendCommandLine"));
165 :
166 0 : *aWindowFound = false;
167 :
168 : // FindBestWindow() iterates down the window hierarchy, so catch X errors
169 : // when windows get destroyed before being accessed.
170 0 : sOldHandler = XSetErrorHandler(HandleBadWindow);
171 :
172 0 : Window w = FindBestWindow(aProgram, aUsername, aProfile);
173 :
174 0 : nsresult rv = NS_OK;
175 :
176 0 : if (w) {
177 : // ok, let the caller know that we at least found a window.
178 0 : *aWindowFound = true;
179 :
180 : // Ignore BadWindow errors up to this point. The last request from
181 : // FindBestWindow() was a synchronous XGetWindowProperty(), so no need to
182 : // Sync. Leave the error handler installed to detect if w gets destroyed.
183 0 : sGotBadWindow = false;
184 :
185 : // make sure we get the right events on that window
186 0 : XSelectInput(mDisplay, w,
187 0 : (PropertyChangeMask|StructureNotifyMask));
188 :
189 0 : bool destroyed = false;
190 :
191 : // get the lock on the window
192 0 : rv = GetLock(w, &destroyed);
193 :
194 0 : if (NS_SUCCEEDED(rv)) {
195 : // send our command
196 : rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse,
197 0 : &destroyed);
198 :
199 : // if the window was destroyed, don't bother trying to free the
200 : // lock.
201 0 : if (!destroyed)
202 0 : FreeLock(w); // doesn't really matter what this returns
203 :
204 : }
205 : }
206 :
207 0 : XSetErrorHandler(sOldHandler);
208 :
209 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("SendCommandInternal returning 0x%" PRIx32 "\n",
210 : static_cast<uint32_t>(rv)));
211 :
212 0 : return rv;
213 : }
214 :
215 : Window
216 0 : XRemoteClient::CheckWindow(Window aWindow)
217 : {
218 0 : Atom type = None;
219 : int format;
220 : unsigned long nitems, bytesafter;
221 : unsigned char *data;
222 : Window innerWindow;
223 :
224 0 : XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom,
225 : 0, 0, False, AnyPropertyType,
226 0 : &type, &format, &nitems, &bytesafter, &data);
227 :
228 0 : if (type) {
229 0 : XFree(data);
230 0 : return aWindow;
231 : }
232 :
233 : // didn't find it here so check the children of this window
234 0 : innerWindow = CheckChildren(aWindow);
235 :
236 0 : if (innerWindow)
237 0 : return innerWindow;
238 :
239 0 : return aWindow;
240 : }
241 :
242 : Window
243 0 : XRemoteClient::CheckChildren(Window aWindow)
244 : {
245 : Window root, parent;
246 : Window *children;
247 : unsigned int nchildren;
248 : unsigned int i;
249 0 : Atom type = None;
250 : int format;
251 : unsigned long nitems, after;
252 : unsigned char *data;
253 0 : Window retval = None;
254 :
255 0 : if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children,
256 : &nchildren))
257 0 : return None;
258 :
259 : // scan the list first before recursing into the list of windows
260 : // which can get quite deep.
261 0 : for (i=0; !retval && (i < nchildren); i++) {
262 0 : XGetWindowProperty(mDisplay, children[i], mMozWMStateAtom,
263 : 0, 0, False, AnyPropertyType, &type, &format,
264 0 : &nitems, &after, &data);
265 0 : if (type) {
266 0 : XFree(data);
267 0 : retval = children[i];
268 : }
269 : }
270 :
271 : // otherwise recurse into the list
272 0 : for (i=0; !retval && (i < nchildren); i++) {
273 0 : retval = CheckChildren(children[i]);
274 : }
275 :
276 0 : if (children)
277 0 : XFree((char *)children);
278 :
279 0 : return retval;
280 : }
281 :
282 : nsresult
283 0 : XRemoteClient::GetLock(Window aWindow, bool *aDestroyed)
284 : {
285 0 : bool locked = false;
286 0 : bool waited = false;
287 0 : *aDestroyed = false;
288 :
289 0 : nsresult rv = NS_OK;
290 :
291 0 : if (!mLockData) {
292 :
293 : char pidstr[32];
294 : char sysinfobuf[SYS_INFO_BUFFER_LENGTH];
295 0 : SprintfLiteral(pidstr, "pid%d@", getpid());
296 : PRStatus status;
297 : status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf,
298 0 : SYS_INFO_BUFFER_LENGTH);
299 0 : if (status != PR_SUCCESS) {
300 0 : return NS_ERROR_FAILURE;
301 : }
302 :
303 : // allocate enough space for the string plus the terminating
304 : // char
305 0 : mLockData = (char *)malloc(strlen(pidstr) + strlen(sysinfobuf) + 1);
306 0 : if (!mLockData)
307 0 : return NS_ERROR_OUT_OF_MEMORY;
308 :
309 0 : strcpy(mLockData, pidstr);
310 0 : if (!strcat(mLockData, sysinfobuf))
311 0 : return NS_ERROR_FAILURE;
312 : }
313 :
314 0 : do {
315 : int result;
316 : Atom actual_type;
317 : int actual_format;
318 : unsigned long nitems, bytes_after;
319 0 : unsigned char *data = 0;
320 :
321 0 : XGrabServer(mDisplay);
322 :
323 0 : result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom,
324 : 0, (65536 / sizeof (long)),
325 : False, /* don't delete */
326 : XA_STRING,
327 : &actual_type, &actual_format,
328 : &nitems, &bytes_after,
329 0 : &data);
330 :
331 : // aWindow may have been destroyed before XSelectInput was processed, in
332 : // which case there may not be any DestroyNotify event in the queue to
333 : // tell us. XGetWindowProperty() was synchronous so error responses have
334 : // now been processed, setting sGotBadWindow.
335 0 : if (sGotBadWindow) {
336 0 : *aDestroyed = true;
337 0 : rv = NS_ERROR_FAILURE;
338 : }
339 0 : else if (result != Success || actual_type == None) {
340 : /* It's not now locked - lock it. */
341 0 : XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8,
342 : PropModeReplace,
343 0 : (unsigned char *)mLockData,
344 0 : strlen(mLockData));
345 0 : locked = True;
346 : }
347 :
348 0 : XUngrabServer(mDisplay);
349 0 : XFlush(mDisplay); // ungrab now!
350 :
351 0 : if (!locked && !NS_FAILED(rv)) {
352 : /* We tried to grab the lock this time, and failed because someone
353 : else is holding it already. So, wait for a PropertyDelete event
354 : to come in, and try again. */
355 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
356 : ("window 0x%x is locked by %s; waiting...\n",
357 : (unsigned int) aWindow, data));
358 0 : waited = True;
359 : while (true) {
360 : XEvent event;
361 : int select_retval;
362 : fd_set select_set;
363 : struct timeval delay;
364 0 : delay.tv_sec = 10;
365 0 : delay.tv_usec = 0;
366 :
367 0 : FD_ZERO(&select_set);
368 : // add the x event queue to the select set
369 0 : FD_SET(ConnectionNumber(mDisplay), &select_set);
370 0 : select_retval = select(ConnectionNumber(mDisplay) + 1,
371 0 : &select_set, nullptr, nullptr, &delay);
372 : // did we time out?
373 0 : if (select_retval == 0) {
374 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("timed out waiting for window\n"));
375 0 : rv = NS_ERROR_FAILURE;
376 0 : break;
377 : }
378 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("xevent...\n"));
379 0 : XNextEvent (mDisplay, &event);
380 0 : if (event.xany.type == DestroyNotify &&
381 0 : event.xdestroywindow.window == aWindow) {
382 0 : *aDestroyed = true;
383 0 : rv = NS_ERROR_FAILURE;
384 0 : break;
385 : }
386 0 : if (event.xany.type == PropertyNotify &&
387 0 : event.xproperty.state == PropertyDelete &&
388 0 : event.xproperty.window == aWindow &&
389 0 : event.xproperty.atom == mMozLockAtom) {
390 : /* Ok! Someone deleted their lock, so now we can try
391 : again. */
392 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
393 : ("(0x%x unlocked, trying again...)\n",
394 : (unsigned int) aWindow));
395 0 : break;
396 : }
397 0 : }
398 : }
399 0 : if (data)
400 0 : XFree(data);
401 0 : } while (!locked && !NS_FAILED(rv));
402 :
403 0 : if (waited && locked) {
404 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("obtained lock.\n"));
405 0 : } else if (*aDestroyed) {
406 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
407 : ("window 0x%x unexpectedly destroyed.\n",
408 : (unsigned int) aWindow));
409 : }
410 :
411 0 : return rv;
412 : }
413 :
414 : Window
415 0 : XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
416 : const char *aProfile)
417 : {
418 0 : Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
419 0 : Window bestWindow = 0;
420 : Window root2, parent, *kids;
421 : unsigned int nkids;
422 :
423 : // Get a list of the children of the root window, walk the list
424 : // looking for the best window that fits the criteria.
425 0 : if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) {
426 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
427 : ("XQueryTree failed in XRemoteClient::FindBestWindow"));
428 0 : return 0;
429 : }
430 :
431 0 : if (!(kids && nkids)) {
432 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("root window has no children"));
433 0 : return 0;
434 : }
435 :
436 : // We'll walk the list of windows looking for a window that best
437 : // fits the criteria here.
438 :
439 0 : for (unsigned int i = 0; i < nkids; i++) {
440 : Atom type;
441 : int format;
442 : unsigned long nitems, bytesafter;
443 0 : unsigned char *data_return = 0;
444 : Window w;
445 0 : w = kids[i];
446 : // find the inner window with WM_STATE on it
447 0 : w = CheckWindow(w);
448 :
449 0 : int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom,
450 : 0, (65536 / sizeof (long)),
451 : False, XA_STRING,
452 : &type, &format, &nitems, &bytesafter,
453 0 : &data_return);
454 :
455 0 : if (!data_return)
456 0 : continue;
457 :
458 0 : double version = PR_strtod((char*) data_return, nullptr);
459 0 : XFree(data_return);
460 :
461 0 : if (!(version >= 5.1 && version < 6))
462 0 : continue;
463 :
464 0 : data_return = 0;
465 :
466 0 : if (status != Success || type == None)
467 0 : continue;
468 :
469 : // If someone passed in a program name, check it against this one
470 : // unless it's "any" in which case, we don't care. If someone did
471 : // pass in a program name and this window doesn't support that
472 : // protocol, we don't include it in our list.
473 0 : if (aProgram && strcmp(aProgram, "any")) {
474 0 : status = XGetWindowProperty(mDisplay, w, mMozProgramAtom,
475 : 0, (65536 / sizeof(long)),
476 : False, XA_STRING,
477 : &type, &format, &nitems, &bytesafter,
478 0 : &data_return);
479 :
480 : // If the return name is not the same as what someone passed in,
481 : // we don't want this window.
482 0 : if (data_return) {
483 0 : if (strcmp(aProgram, (const char *)data_return)) {
484 0 : XFree(data_return);
485 0 : continue;
486 : }
487 :
488 : // This is actually the success condition.
489 0 : XFree(data_return);
490 : }
491 : else {
492 : // Doesn't support the protocol, even though the user
493 : // requested it. So we're not going to use this window.
494 0 : continue;
495 : }
496 : }
497 :
498 : // Check to see if it has the user atom on that window. If there
499 : // is then we need to make sure that it matches what we have.
500 : const char *username;
501 0 : if (aUsername) {
502 0 : username = aUsername;
503 : }
504 : else {
505 0 : username = PR_GetEnv("LOGNAME");
506 : }
507 :
508 0 : if (username) {
509 0 : status = XGetWindowProperty(mDisplay, w, mMozUserAtom,
510 : 0, (65536 / sizeof(long)),
511 : False, XA_STRING,
512 : &type, &format, &nitems, &bytesafter,
513 0 : &data_return);
514 :
515 : // if there's a username compare it with what we have
516 0 : if (data_return) {
517 : // If the IDs aren't equal, we don't want this window.
518 0 : if (strcmp(username, (const char *)data_return)) {
519 0 : XFree(data_return);
520 0 : continue;
521 : }
522 :
523 0 : XFree(data_return);
524 : }
525 : }
526 :
527 : // Check to see if there's a profile name on this window. If
528 : // there is, then we need to make sure it matches what someone
529 : // passed in.
530 0 : if (aProfile) {
531 0 : status = XGetWindowProperty(mDisplay, w, mMozProfileAtom,
532 : 0, (65536 / sizeof(long)),
533 : False, XA_STRING,
534 : &type, &format, &nitems, &bytesafter,
535 0 : &data_return);
536 :
537 : // If there's a profile compare it with what we have
538 0 : if (data_return) {
539 : // If the profiles aren't equal, we don't want this window.
540 0 : if (strcmp(aProfile, (const char *)data_return)) {
541 0 : XFree(data_return);
542 0 : continue;
543 : }
544 :
545 0 : XFree(data_return);
546 : }
547 : }
548 :
549 : // Check to see if the window supports the new command-line passing
550 : // protocol, if that is requested.
551 :
552 : // If we got this far, this is the best window. It passed
553 : // all the tests.
554 0 : bestWindow = w;
555 0 : break;
556 : }
557 :
558 0 : if (kids)
559 0 : XFree((char *) kids);
560 :
561 0 : return bestWindow;
562 : }
563 :
564 : nsresult
565 0 : XRemoteClient::FreeLock(Window aWindow)
566 : {
567 : int result;
568 : Atom actual_type;
569 : int actual_format;
570 : unsigned long nitems, bytes_after;
571 0 : unsigned char *data = 0;
572 :
573 0 : result = XGetWindowProperty(mDisplay, aWindow, mMozLockAtom,
574 : 0, (65536 / sizeof(long)),
575 : True, /* atomic delete after */
576 : XA_STRING,
577 : &actual_type, &actual_format,
578 : &nitems, &bytes_after,
579 0 : &data);
580 0 : if (result != Success) {
581 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
582 : ("unable to read and delete " MOZILLA_LOCK_PROP
583 : " property\n"));
584 0 : return NS_ERROR_FAILURE;
585 : }
586 0 : if (!data || !*data){
587 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
588 : ("invalid data on " MOZILLA_LOCK_PROP
589 : " of window 0x%x.\n",
590 : (unsigned int) aWindow));
591 0 : return NS_ERROR_FAILURE;
592 : }
593 0 : else if (strcmp((char *)data, mLockData)) {
594 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
595 : (MOZILLA_LOCK_PROP " was stolen! Expected \"%s\", saw \"%s\"!\n",
596 : mLockData, data));
597 0 : return NS_ERROR_FAILURE;
598 : }
599 :
600 0 : if (data)
601 0 : XFree(data);
602 0 : return NS_OK;
603 : }
604 :
605 : /* like strcpy, but return the char after the final null */
606 : static char*
607 0 : estrcpy(const char* s, char* d)
608 : {
609 0 : while (*s)
610 0 : *d++ = *s++;
611 :
612 0 : *d++ = '\0';
613 0 : return d;
614 : }
615 :
616 : nsresult
617 0 : XRemoteClient::DoSendCommandLine(Window aWindow, int32_t argc, char **argv,
618 : const char* aDesktopStartupID,
619 : char **aResponse, bool *aDestroyed)
620 : {
621 0 : *aDestroyed = false;
622 :
623 : char cwdbuf[MAX_PATH];
624 0 : if (!getcwd(cwdbuf, MAX_PATH))
625 0 : return NS_ERROR_UNEXPECTED;
626 :
627 : // the commandline property is constructed as an array of int32_t
628 : // followed by a series of null-terminated strings:
629 : //
630 : // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
631 : // (offset is from the beginning of the buffer)
632 :
633 : static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID=";
634 :
635 0 : int32_t argvlen = strlen(cwdbuf);
636 0 : for (int i = 0; i < argc; ++i) {
637 0 : int32_t len = strlen(argv[i]);
638 0 : if (i == 0 && aDesktopStartupID) {
639 0 : len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
640 : }
641 0 : argvlen += len;
642 : }
643 :
644 0 : auto* buffer = (int32_t*) malloc(argvlen + argc + 1 +
645 0 : sizeof(int32_t) * (argc + 1));
646 0 : if (!buffer)
647 0 : return NS_ERROR_OUT_OF_MEMORY;
648 :
649 0 : buffer[0] = TO_LITTLE_ENDIAN32(argc);
650 :
651 0 : auto *bufend = (char*) (buffer + argc + 1);
652 :
653 0 : bufend = estrcpy(cwdbuf, bufend);
654 :
655 0 : for (int i = 0; i < argc; ++i) {
656 0 : buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer));
657 0 : bufend = estrcpy(argv[i], bufend);
658 0 : if (i == 0 && aDesktopStartupID) {
659 0 : bufend = estrcpy(desktopStartupPrefix, bufend - 1);
660 0 : bufend = estrcpy(aDesktopStartupID, bufend - 1);
661 : }
662 : }
663 :
664 : #ifdef DEBUG_bsmedberg
665 : int32_t debug_argc = TO_LITTLE_ENDIAN32(*buffer);
666 : char *debug_workingdir = (char*) (buffer + argc + 1);
667 :
668 : printf("Sending command line:\n"
669 : " working dir: %s\n"
670 : " argc:\t%i",
671 : debug_workingdir,
672 : debug_argc);
673 :
674 : int32_t *debug_offset = buffer + 1;
675 : for (int debug_i = 0; debug_i < debug_argc; ++debug_i)
676 : printf(" argv[%i]:\t%s\n", debug_i,
677 : ((char*) buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i]));
678 : #endif
679 :
680 0 : XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8,
681 : PropModeReplace, (unsigned char *) buffer,
682 0 : bufend - ((char*) buffer));
683 0 : free(buffer);
684 :
685 0 : if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom))
686 0 : return NS_ERROR_FAILURE;
687 :
688 0 : return NS_OK;
689 : }
690 :
691 : bool
692 0 : XRemoteClient::WaitForResponse(Window aWindow, char **aResponse,
693 : bool *aDestroyed, Atom aCommandAtom)
694 : {
695 0 : bool done = false;
696 0 : bool accepted = false;
697 :
698 0 : while (!done) {
699 : XEvent event;
700 0 : XNextEvent (mDisplay, &event);
701 0 : if (event.xany.type == DestroyNotify &&
702 0 : event.xdestroywindow.window == aWindow) {
703 : /* Print to warn user...*/
704 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
705 : ("window 0x%x was destroyed.\n",
706 : (unsigned int) aWindow));
707 0 : *aResponse = strdup("Window was destroyed while reading response.");
708 0 : *aDestroyed = true;
709 0 : return false;
710 : }
711 0 : if (event.xany.type == PropertyNotify &&
712 0 : event.xproperty.state == PropertyNewValue &&
713 0 : event.xproperty.window == aWindow &&
714 0 : event.xproperty.atom == mMozResponseAtom) {
715 : Atom actual_type;
716 : int actual_format;
717 : unsigned long nitems, bytes_after;
718 0 : unsigned char *data = 0;
719 : Bool result;
720 0 : result = XGetWindowProperty (mDisplay, aWindow, mMozResponseAtom,
721 : 0, (65536 / sizeof (long)),
722 : True, /* atomic delete after */
723 : XA_STRING,
724 : &actual_type, &actual_format,
725 : &nitems, &bytes_after,
726 0 : &data);
727 0 : if (result != Success) {
728 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
729 : ("failed reading " MOZILLA_RESPONSE_PROP
730 : " from window 0x%0x.\n",
731 : (unsigned int) aWindow));
732 0 : *aResponse = strdup("Internal error reading response from window.");
733 0 : done = true;
734 : }
735 0 : else if (!data || strlen((char *) data) < 5) {
736 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
737 : ("invalid data on " MOZILLA_RESPONSE_PROP
738 : " property of window 0x%0x.\n",
739 : (unsigned int) aWindow));
740 0 : *aResponse = strdup("Server returned invalid data in response.");
741 0 : done = true;
742 : }
743 0 : else if (*data == '1') { /* positive preliminary reply */
744 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
745 : /* keep going */
746 0 : done = false;
747 : }
748 :
749 0 : else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */
750 0 : *aResponse = strdup((char *)data);
751 0 : accepted = true;
752 0 : done = true;
753 : }
754 :
755 0 : else if (*data == '2') { /* positive completion */
756 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
757 0 : *aResponse = strdup((char *)data);
758 0 : accepted = true;
759 0 : done = true;
760 : }
761 :
762 0 : else if (*data == '3') { /* positive intermediate reply */
763 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
764 : ("internal error: "
765 : "server wants more information? (%s)\n",
766 : data));
767 0 : *aResponse = strdup((char *)data);
768 0 : done = true;
769 : }
770 :
771 0 : else if (*data == '4' || /* transient negative completion */
772 0 : *data == '5') { /* permanent negative completion */
773 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
774 0 : *aResponse = strdup((char *)data);
775 0 : done = true;
776 : }
777 :
778 : else {
779 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
780 : ("unrecognised " MOZILLA_RESPONSE_PROP
781 : " from window 0x%x: %s\n",
782 : (unsigned int) aWindow, data));
783 0 : *aResponse = strdup((char *)data);
784 0 : done = true;
785 : }
786 :
787 0 : if (data)
788 0 : XFree(data);
789 : }
790 :
791 0 : else if (event.xany.type == PropertyNotify &&
792 0 : event.xproperty.window == aWindow &&
793 0 : event.xproperty.state == PropertyDelete &&
794 0 : event.xproperty.atom == aCommandAtom) {
795 0 : MOZ_LOG(sRemoteLm, LogLevel::Debug,
796 : ("(server 0x%x has accepted "
797 : MOZILLA_COMMANDLINE_PROP ".)\n",
798 : (unsigned int) aWindow));
799 : }
800 :
801 : }
802 :
803 0 : return accepted;
804 : }
|