1 /**
2 * This file is part of mozplugger a fork of plugger, for list of developers
3 * see the README file.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #define _GNU_SOURCE /* for getsid() */
25
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <sysexits.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35 #include <X11/X.h>
36 #include <X11/Xutil.h>
37 #include <X11/Xatom.h>
38 #include <sys/socket.h>
39 #include <sys/time.h>
40 #include <stdbool.h>
41
42 #include "npapi.h"
43 #include "cmd_flags.h"
44 #include "child.h"
45 #include "debug.h"
46 #include "pipe_msg.h"
47
48 /**
49 * Control use of semaphore in mozplugger-helper, define if one wants
50 * semaphores (rare cases doesnt work)
51 */
52
53 #define USE_MUTEX_LOCK
54
55 struct VictimDetails_s
56 {
57 int noWmRunning;
58 int mapped;
59 int reparented;
60 int reparentedAttemptCount;
61 Window window;
62 int borderWidth;
63 int x;
64 int y;
65 int idealWidth; /* The width the victim wants to be if unconstrained */
66 int idealHeight; /* The height the victim wants to be if unconstrained */
67 int currentWidth;
68 int currentHeight;
69 };
70
71 typedef struct VictimDetails_s VictimDetails_t;
72
73 struct ParentDetails_s
74 {
75 Window window;
76 int width;
77 int height;
78 };
79
80 typedef struct ParentDetails_s ParentDetails_t;
81
82 struct SwallowMutex_s
83 {
84 Atom xObj;
85 int taken;
86 Window win;
87 Display * dpy;
88 };
89
90 typedef struct SwallowMutex_s SwallowMutex_t;
91
92 struct SigGlobals_s
93 {
94 Display * dpy;
95 #ifdef USE_MUTEX_LOCK
96 SwallowMutex_t * mutex;
97 #endif
98 pid_t childPid;
99 };
100
101 typedef struct SigGlobals_s SigGlobals_t;
102
103 /**
104 * Global variables
105 */
106
107 static SigGlobals_t sig_globals;
108 static VictimDetails_t victimDetails;
109 static ParentDetails_t parentDetails;
110 static int pipe_fd;
111 static int flags;
112 static char * winname;
113 static char * file;
114
115 static XWindowAttributes wattr;
116
117 #define MAX_POSS_VICTIMS 100
118 static unsigned int possible_victim_count = 0;
119 static Window possible_victim_windows[MAX_POSS_VICTIMS];
120
121
122 /**
123 * Callback from X when there is an error. If debug defined error message is
124 * printed to debug log, otherise nothing is done
125 *
126 * @param[in] dpy The display pointer
127 * @param[in] err Pointer to the error event data
128 *
129 * @return Always returns zero
130 */
error_handler(Display * dpy,XErrorEvent * err)131 static int error_handler(Display *dpy, XErrorEvent *err)
132 {
133 char buffer[1024];
134 XGetErrorText(dpy, err->error_code, buffer, sizeof(buffer));
135 D("!!!ERROR_HANDLER!!!: %s\n", buffer);
136 return 0;
137 }
138
139 #ifdef USE_MUTEX_LOCK
140 /**
141 * Initialise semaphore (swallow mutex semaphore protection)
142 *
143 * @param[in] dpy The display
144 * @param[in] win The window
145 * @param[out] mutex The Mutex initialised
146 *
147 * @return none
148 */
initSwallowMutex(Display * dpy,Window win,SwallowMutex_t * mutex)149 static void initSwallowMutex(Display * dpy, Window win, SwallowMutex_t * mutex)
150 {
151 D("Initialising Swallow Mutex\n");
152 if(dpy == 0)
153 {
154 fprintf(stderr, "Display not set so cannot initialise semaphore!\n");
155 mutex->dpy = NULL;
156 }
157 else
158 {
159 mutex->dpy = dpy;
160 mutex->win = win;
161 mutex->xObj = XInternAtom(dpy, "MOZPLUGGER_SWALLOW_MUTEX", 0);
162 mutex->taken = 0;
163 }
164 }
165 #endif
166
167 /**
168 * Initialise the X atom used to mark windows as owned by mozplugger
169 *
170 * @param[in] dpy The display pointer
171 *
172 * @return None
173 */
getWindowOwnerMarker(Display * dpy)174 static Atom getWindowOwnerMarker(Display * dpy)
175 {
176 static Atom mark = 0;
177 if(mark == 0)
178 {
179 mark = XInternAtom(dpy, "MOZPLUGGER_OWNER", 0);
180 }
181 return mark;
182 }
183
184 /**
185 * Get Host ID - construct a host ID using host name as source of information
186 *
187 * @return The host ID
188 */
getHostId(void)189 static uint32_t getHostId(void)
190 {
191 char hostName[128];
192 uint32_t id;
193 int i;
194
195 memset(hostName, 0, sizeof(hostName));
196 gethostname(hostName, sizeof(hostName)-1);
197
198 D("Host Name = \"%s\"\n", hostName);
199
200 /* OK, Create a 32 bit hash value from the host name....
201 use this as a host ID, the possiblity of a collison of hash keys
202 effecting the swallow of victims is so infinitimisally small! */
203
204 id = 0;
205 for(i=0; i < (int)(sizeof(hostName)/sizeof(uint32_t)); i++)
206 {
207 id = ((id << 5) ^ (id >> 27)) ^ ((uint32_t *)hostName)[i];
208 }
209 return id;
210 }
211
212 /**
213 * Extract host Id and pid from window property. This is common code used in
214 * various places and is factored out due to complexity of 64 bit versus
215 * 32 bit machines.
216 *
217 * @param[in] dpy The display pointer
218 * @param[in] w The window to get property from
219 * @param[in] name The name of property to get
220 * @param[out] hostId The ID of the host currently owning the mutex
221 * @param[out] pid The process ID of the mutex
222 *
223 * @return 1(true) if owner found, 0 otherwise
224 */
getOwnerFromProperty(Display * dpy,Window w,Atom name,uint32_t * hostId,uint32_t * pid)225 static int getOwnerFromProperty(Display * dpy, Window w, Atom name, uint32_t * hostId, uint32_t * pid)
226 {
227 unsigned long nitems;
228 unsigned long bytes;
229 int fmt;
230 Atom type;
231 unsigned char * property = NULL;
232 int success = 0;
233
234 /* Get hold of the Host & PID that current holds the semaphore for
235 this display! - watch out for the bizarre 64-bit platform behaviour
236 where property returned is actually an array of 2 x 64bit ints with
237 the top 32bits set to zero! */
238 XGetWindowProperty(dpy, w, name,
239 0, 2, 0, XA_INTEGER,
240 &type, &fmt, &nitems, &bytes,
241 &property);
242
243 if(property)
244 {
245 D("XGetWindowProperty() passed\n");
246
247 /* Just check all is correct! */
248 if((type != XA_INTEGER) || (fmt!=32) || (nitems!=2))
249 {
250 fprintf(stderr, "XGetWindowProperty returned bad values "
251 "%ld,%d,%lu,%lu\n", (long) type, fmt, nitems,
252 bytes);
253 }
254 else
255 {
256 *hostId = (uint32_t)((unsigned long *)property)[0];
257 *pid = (uint32_t)((unsigned long *)property)[1];
258 success = 1;
259 }
260 XFree(property);
261 }
262 else
263 {
264 D("XGetWindowProperty() failed\n");
265 }
266 return success;
267 }
268
269 #ifdef USE_MUTEX_LOCK
270 /**
271 * Get owner of mutex semaphore. Returns the current owner of the semaphore
272 *
273 * @param[in] mutex The Swallow mutex structure
274 * @param[out] hostId The ID of the host currently owning the mutex
275 * @param[out] pid The process ID of the mutex
276 *
277 * @return 1(true) if owner found, 0 otherwise
278 */
getSwallowMutexOwner(const SwallowMutex_t * mutex,uint32_t * hostId,uint32_t * pid)279 static int getSwallowMutexOwner(const SwallowMutex_t * mutex,
280 uint32_t * hostId, uint32_t * pid)
281 {
282 return getOwnerFromProperty(mutex->dpy, mutex->win, mutex->xObj, hostId, pid);
283 }
284
285 /**
286 * Set owner of mutex semaphore
287 *
288 * @param[in,out] mutex The swallow mutex structure
289 * @param[in] hostId The ID of the new owner of the mutex
290 * @param[in] pid The process ID of the mutex
291 *
292 * @return none
293 */
setSwallowMutexOwner(SwallowMutex_t * mutex,uint32_t hostId,uint32_t pid)294 static void setSwallowMutexOwner(SwallowMutex_t * mutex,
295 uint32_t hostId, uint32_t pid)
296 {
297 unsigned long temp[2] = {hostId, pid};
298
299 D("Setting swallow mutex owner, hostId = 0x%08X, pid=%u\n",
300 (unsigned) hostId, (unsigned) pid);
301 XChangeProperty(mutex->dpy, mutex->win, mutex->xObj,
302 XA_INTEGER, 32, PropModeAppend,
303 (unsigned char*) (&temp), 2);
304 }
305
306 /**
307 * Take mutex semaphore ownership.
308 *
309 * @param[in,out] mutex The swallow mutex structure
310 *
311 * @return none
312 */
takeSwallowMutex(SwallowMutex_t * mutex)313 static void takeSwallowMutex(SwallowMutex_t * mutex)
314 {
315 int countDown;
316 const uint32_t ourPid = (uint32_t)getpid();
317 const uint32_t ourHostId = getHostId();
318 uint32_t otherPid;
319 uint32_t otherHostId;
320 uint32_t prevOtherPid = 0;
321
322 if(!mutex || (mutex->dpy == 0))
323 {
324 return;
325 }
326
327 D("Attempting to take Swallow Mutex\n");
328 /* Try up tp forty times ( 40 * 250ms = 10 seconds) to take
329 the semaphore... */
330
331 countDown = 40;
332 while(1)
333 {
334
335 /* While someone owns the semaphore */
336 while(getSwallowMutexOwner(mutex, &otherHostId, &otherPid))
337 {
338 if( otherHostId == ourHostId)
339 {
340 if(otherPid == ourPid)
341 {
342 /* Great we have either successfully taken the semaphore
343 OR (unlikely) we previously had the semaphore! Exit
344 the function...*/
345 mutex->taken = 1;
346 D("Taken Swallow Mutex\n");
347 return;
348 }
349
350 D("Semaphore currently taken by pid=%ld\n", (long) otherPid);
351 /* Check that the process that has the semaphore exists...
352 Bit of a hack, I cant find a function to directly check
353 if process exists. */
354 if( (getsid(otherPid) < 0 ) && (errno == ESRCH))
355 {
356 D("Strange other Pid(%lu) cannot be found\n",(unsigned long) otherPid);
357 XDeleteProperty(mutex->dpy, mutex->win, mutex->xObj);
358 break; /* OK force early exit of inner while loop */
359 }
360 }
361
362 /* Check if the owner of semaphore hasn't recently changed if it
363 has restart timer */
364 if(prevOtherPid != otherPid)
365 {
366 D("Looks like semaphore's owner has changed pid=%ld\n",
367 (long) otherPid);
368 countDown = 40;
369 prevOtherPid = otherPid;
370 }
371
372 /* Do one step of the timer... */
373 countDown--;
374 if(countDown <= 0)
375 {
376 D("Waited long enough for Pid(%lu)\n", (unsigned long) otherPid);
377 XDeleteProperty(mutex->dpy, mutex->win, mutex->xObj);
378 break;
379 }
380
381 usleep(250000); /* 250ms */
382 }
383
384 /* else no one has semaphore, timeout, or owner is dead -
385 Set us as the owner, but we need to check if we weren't
386 beaten to it so once more around the loop.
387 Note even doing the recheck does not work in 100% of all
388 cases due to task switching occuring at just the wrong moment
389 see Mozdev bug 20088 - the fix is done use the stillHaveMutex
390 function */
391
392 setSwallowMutexOwner(mutex, ourHostId, ourPid);
393 }
394 }
395
396 /**
397 * Check if we still have the mutex semaphore
398 *
399 * @param[in,out] mutex The swallow mutex structure
400 *
401 * @return true if we still have the Mutex semaphore
402 */
stillHaveMutex(SwallowMutex_t * mutex)403 static int stillHaveMutex(SwallowMutex_t * mutex)
404 {
405 uint32_t otherPid;
406 uint32_t otherHostId;
407
408 if(getSwallowMutexOwner(mutex, &otherHostId, &otherPid))
409 {
410 const uint32_t ourHostId = getHostId();
411 if( otherHostId == ourHostId)
412 {
413 const uint32_t ourPid = (uint32_t)getpid();
414 if(otherPid == ourPid)
415 {
416 return 1;
417 }
418 }
419 }
420 D("Race condition detected, semaphore pinched by Pid(%lu)\n",
421 (unsigned long) otherPid);
422 return 0;
423 }
424
425 /**
426 * Gives away ownership of the mutex semaphore
427 *
428 * @param[in,out] mutex The swallow mutex structure
429 *
430 * @return none
431 */
giveSwallowMutex(SwallowMutex_t * mutex)432 static void giveSwallowMutex(SwallowMutex_t * mutex)
433 {
434 if(!mutex || (mutex->dpy == 0))
435 {
436 return;
437 }
438 D("Giving Swallow Mutex\n");
439 XDeleteProperty(mutex->dpy, mutex->win, mutex->xObj);
440 mutex->taken = 0;
441 }
442 #endif
443
444 /**
445 * Mark the victim window with a property that indicates that this instance
446 * of mozplugger-helper has made a claim to this victim. This is used to
447 * guard against two instances of mozplugger-helper claiming the same victim.
448 *
449 * @param[in] dpy The display pointer
450 * @param[in] w The window
451 *
452 * @return True(1) if taken, False(0) if not
453 */
chkAndMarkVictimWindow(Display * dpy,Window w)454 static int chkAndMarkVictimWindow(Display * dpy, Window w)
455 {
456 unsigned long temp[2];
457 uint32_t ourPid;
458 uint32_t ourHostId;
459
460 uint32_t pid;
461 uint32_t hostId;
462 int gotIt = 0;
463
464 Atom windowOwnerMark = getWindowOwnerMarker(dpy);
465
466 /* See if some other instance of Mozplugger has already 'marked' this
467 * window */
468 if(getOwnerFromProperty(dpy, w, windowOwnerMark, &hostId, &pid))
469 {
470 return 0; /* Looks like someone else has marked this window */
471 }
472
473 /* OK lets claim it for ourselves... */
474 ourPid = (uint32_t)getpid();
475 ourHostId = getHostId();
476
477 temp[0] = ourHostId;
478 temp[1] = ourPid;
479
480 XChangeProperty(dpy, w, windowOwnerMark,
481 XA_INTEGER, 32, PropModeAppend,
482 (unsigned char*) (&temp), 2);
483
484 /* See if we got it */
485 if(getOwnerFromProperty(dpy, w, windowOwnerMark, &hostId, &pid))
486 {
487 if( (pid == ourPid) && (hostId == ourHostId) )
488 {
489 gotIt = 1;
490 }
491 }
492 else
493 {
494 D("Strange, couldn't set windowOwnerMark\n");
495 gotIt = 1;
496 }
497
498 return gotIt;
499 }
500
501 /**
502 * Calculate highest common factor
503 *
504 * @param[in] a First value
505 * @param[in] b Second value
506 *
507 * @return Scaling factor
508 */
gcd(int a,int b)509 static int gcd(int a, int b)
510 {
511 if (a < b)
512 {
513 return gcd(b,a);
514 }
515 if (b == 0)
516 {
517 return a;
518 }
519 return gcd(b, a % b);
520 }
521
522 /**
523 * Adjust window size
524 *
525 * @param[in] dpy The display pointer
526 *
527 * @return none
528 */
adjust_window_size(Display * dpy)529 static void adjust_window_size(Display * dpy)
530 {
531 int x = 0;
532 int y = 0;
533 int w = parentDetails.width;
534 int h = parentDetails.height;
535
536
537 if (flags & H_FILL)
538 {
539 D("Resizing window 0x%x with FILL\n", (unsigned) victimDetails.window);
540 }
541 else if (flags & H_MAXASPECT)
542 {
543 const int d = gcd(victimDetails.idealWidth, victimDetails.idealHeight);
544 int xaspect = victimDetails.idealWidth / d;
545 int yaspect = victimDetails.idealHeight / d;
546
547 if (xaspect && yaspect)
548 {
549 int tmpw, tmph;
550
551 D("Resizing window 0x%x with MAXASPECT\n",
552 (unsigned) victimDetails.window);
553 tmph = h / yaspect;
554 tmpw = w / xaspect;
555 if (tmpw < tmph)
556 {
557 tmph = tmpw;
558 }
559 tmpw = tmph * xaspect;
560 tmph = tmph * yaspect;
561
562 x = (w - tmpw) / 2;
563 y = (h - tmph) / 2;
564
565 w = tmpw;
566 h = tmph;
567 }
568 else
569 {
570 D("Not resizing window\n");
571 return;
572 }
573 }
574
575 /* Compensate for the Victims border width (usually set to zero anyway) */
576 w -= 2 * victimDetails.borderWidth;
577 h -= 2 * victimDetails.borderWidth;
578
579 D("New size: %dx%d+%d+%d\n", w, h, x, y);
580
581 if((victimDetails.x == x) && (victimDetails.y == y)
582 && (victimDetails.currentWidth == w) && (victimDetails.currentHeight == h))
583 {
584 XEvent event;
585 D("No change in window size so sending ConfigureNotify instead\n");
586 /* According to X11 ICCCM specification, a compliant window
587 * manager, (or proxy in this case) should sent a ConfigureNotify
588 * event to the client even if no size change has occurred!
589 * The code below is taken and adapted from the
590 * TWM window manager and fixed movdev bug #18298. */
591 event.type = ConfigureNotify;
592 event.xconfigure.display = dpy;
593 event.xconfigure.event = victimDetails.window;
594 event.xconfigure.window = victimDetails.window;
595 event.xconfigure.x = x;
596 event.xconfigure.y = y;
597 event.xconfigure.width = w;
598 event.xconfigure.height = h;
599 event.xconfigure.border_width = victimDetails.borderWidth;
600 event.xconfigure.above = Above;
601 event.xconfigure.override_redirect = False;
602
603 XSendEvent(dpy, victimDetails.window, False, StructureNotifyMask,
604 &event);
605 }
606
607 /* Always resize the window, even when the target size has not changed.
608 This is needed for gv. */
609 XMoveResizeWindow(dpy, victimDetails.window,
610 x, y, (unsigned)w, (unsigned)h);
611 victimDetails.x = x;
612 victimDetails.y = y;
613 victimDetails.currentWidth = w;
614 victimDetails.currentHeight = h;
615 }
616
617 /**
618 * Change the leader of the window. Peter - not sure why this code is needed
619 * as it does say in the ICCCM that only client applications should change
620 * client HINTS. Also all the window_group does is hint to the window manager
621 * that there is a group of windows that should be handled together.
622 *
623 * @param[in] dpy The display pointer
624 * @param[in] win The window ID
625 * @param[in] newLeader The new window leader
626 *
627 * @return none
628 */
change_leader(Display * dpy,Window win,Window newLeader)629 static void change_leader(Display * dpy, Window win, Window newLeader)
630 {
631 XWMHints *leader_change;
632 D("Changing leader of window 0x%x\n", (unsigned) win);
633
634 if ((leader_change = XGetWMHints(dpy, win)))
635 {
636 if((leader_change->flags & WindowGroupHint) != 0)
637 {
638 D("Old window leader was 0x%x\n",
639 (unsigned) leader_change->window_group);
640 }
641 if((leader_change->flags & InputHint) != 0)
642 {
643 D("Input hint = %i\n", leader_change->input);
644 }
645 if((leader_change->flags & StateHint) != 0)
646 {
647 D("InitialState hint = %i\n",
648 (unsigned) leader_change->initial_state);
649 }
650
651 leader_change->flags |= WindowGroupHint;
652 leader_change->window_group = newLeader;
653
654 D("New window leader is 0x%x\n",
655 (unsigned) leader_change->window_group);
656 XSetWMHints(dpy, win,leader_change);
657 XFree(leader_change);
658 }
659 else
660 {
661 D("XGetWMHints returned NULL\n");
662 }
663 }
664
665 /**
666 * Reparent the window, a count is kept of the number of attempts. If this
667 * exceeds 10, give up (i.e. avoid two instances of helper fighting over
668 * the same application). Originally this was done with a semaphore, but this
669 * caused more problems than it fixed. So alternative is to assume that it is
670 * such an unlikely occurance that two instances of helper will run at exactly
671 * the same time and try to get the same window, that we give up.
672 *
673 * @param[in] dpy The display pointer
674 *
675 * @return none
676 */
reparent_window(Display * dpy)677 static void reparent_window(Display * dpy)
678 {
679 if (!victimDetails.window)
680 {
681 D("reparent_window: No victim to reparent\n");
682 return;
683 }
684 if (!parentDetails.window)
685 {
686 D("reparent_window: No parent to reparent to\n");
687 return;
688 }
689
690 if(victimDetails.reparentedAttemptCount > 10)
691 {
692 D("Giving up reparenting to avoid - tried 10 times\n");
693 return;
694 }
695
696 victimDetails.reparentedAttemptCount++;
697
698
699 D("Reparenting window 0x%x into 0x%x\n", (unsigned)victimDetails.window,
700 (unsigned)parentDetails.window);
701
702 XReparentWindow(dpy, victimDetails.window, parentDetails.window, 0, 0);
703 }
704
705 /**
706 * Traditionally strcmp returns -1, 0 and +1 depending if name comes before
707 * or after windowname, this function also returns +1 if error
708 *
709 * @param[in] windowname The window name to compare against
710 * @param[in] name The name to compare against window name
711 *
712 * @return 0 if match, -1/+1 if different
713 */
my_strcmp(const char * windowname,const char * name)714 static int my_strcmp(const char *windowname, const char *name)
715 {
716 if (!name)
717 {
718 return 1;
719 }
720 if (!windowname)
721 {
722 return 1;
723 }
724
725 switch (name[0])
726 {
727 case '=':
728 return strcmp(name+1, windowname);
729 case '~':
730 return strcasecmp(name+1, windowname);
731 case '*':
732 return strncasecmp(name+1, windowname, strlen(name)-1);
733 default:
734 /* Return 0 for success so need to invert logic as strstr
735 returns pointer to the match or NULL if no match */
736 return !strstr(windowname, name);
737 }
738 }
739
740 /**
741 * Check name against the name of the passed window
742 *
743 * @param[in] dpy The display pointer
744 * @param[in] w The window to compare against
745 * @param[in] name The name to compare against window name
746 *
747 * @return 1 if match, 0 if not
748 */
check_window_name(Display * dpy,Window w,const char * name)749 static char check_window_name(Display * dpy, Window w, const char *name)
750 {
751 char * windowname;
752 XClassHint windowclass;
753
754 if (XFetchName(dpy, w, &windowname))
755 {
756 const char match = (my_strcmp(windowname, name) == 0);
757
758 D("XFetchName, checking window NAME 0x%x (%s %s %s)\n",
759 (unsigned)w, windowname, match ? "==" : "!=", name);
760 XFree(windowname);
761 if (match)
762 {
763 return 1;
764 }
765 }
766 else
767 {
768 D("XFetchName, window has no NAME\n");
769 }
770
771 if (XGetClassHint(dpy, w, &windowclass))
772 {
773 const char match = (my_strcmp(windowclass.res_name, name) == 0);
774
775 D("XGetClassHint, checking window CLASS 0x%x (%s %s %s)\n",
776 (unsigned)w, windowclass.res_name, match ? "==" : "!=", name);
777 XFree(windowclass.res_class);
778 XFree(windowclass.res_name);
779
780 return match;
781 }
782 else
783 {
784 D("XGetClassHint, window has no CLASS\n");
785 }
786 return 0;
787 }
788
789 /**
790 * Setup display ready for any reparenting of the application window. If the
791 * WINDOW is NULL (can happen see mozdev bug #18837), then return failed
792 * code from this function to indicate no window available. This then means
793 * no swallowing will occur (even if requested).
794 *
795 * @return The display pointer or NULL
796 */
setup_display(void)797 static Display * setup_display(void)
798 {
799 Display * dpy = NULL;
800 char * displayname;
801
802 if(parentDetails.window == 0) /* mozdev bug #18837 */
803 {
804 D("setup_display() WINDOW is Null - so nothing setup\n");
805 return NULL;
806 }
807
808 displayname = getenv("DISPLAY");
809 D("setup_display(%s)\n", displayname);
810
811 XSetErrorHandler(error_handler);
812
813 dpy = XOpenDisplay(displayname);
814 if(dpy == 0)
815 {
816 D("setup_display() failed cannot open display!!\n");
817 return 0;
818 }
819
820 if (!XGetWindowAttributes(dpy, parentDetails.window, &wattr))
821 {
822 D("setup_display() failed cannot get window attributes!!\n");
823 XCloseDisplay(dpy);
824 return NULL;
825 }
826 D("display=0x%x\n", (unsigned) dpy);
827 D("WINDOW =0x%x\n", (unsigned) parentDetails.window);
828 D("rootwin=0x%x\n", (unsigned) wattr.root);
829
830 D("setup_display() done\n");
831
832 return dpy;
833 }
834
835 /**
836 * Add to list of possible victim windows. Called whenever we get a
837 * CREATE_NOTIFY on the root window.
838 *
839 * @param[in] window The window
840 *
841 * @return none
842 */
add_possible_victim(Window window)843 static void add_possible_victim(Window window)
844 {
845 possible_victim_windows[possible_victim_count] = window;
846 if(possible_victim_count < MAX_POSS_VICTIMS)
847 {
848 possible_victim_count++;
849 }
850 }
851
852 /**
853 * Checks if the passed window is the victim.
854 *
855 * @param[in] dpy The display pointer
856 * @param[in] window The window ID
857 * @param[in] mutex The Swallow mutex
858 *
859 * @return True if found our victim for first time.
860 */
find_victim(Display * dpy,Window window,SwallowMutex_t * mutex)861 static int find_victim(Display * dpy, Window window, SwallowMutex_t * mutex)
862 {
863 if (!victimDetails.window)
864 {
865 int i;
866 Window found = 0;
867
868 D("Looking for victim... (%s)\n", winname);
869
870 /* New way, check through list of newly created windows */
871 for(i = 0; i < possible_victim_count; i++)
872 {
873 if(window == possible_victim_windows[i])
874 {
875 if (check_window_name(dpy, window, winname))
876 {
877 found = true;
878 break;
879 }
880 }
881 }
882
883 if (found)
884 {
885 XWindowAttributes ca;
886 if(XGetWindowAttributes(dpy, window, &ca))
887 {
888 /* See if some instance of mozplugger got the window
889 * before us, if not mark it as ours */
890 if(chkAndMarkVictimWindow(dpy, window))
891 {
892 victimDetails.window = window;
893 victimDetails.borderWidth = ca.border_width;
894 victimDetails.x = ca.x;
895 victimDetails.y = ca.y;
896 victimDetails.idealWidth = ca.width;
897 victimDetails.idealHeight = ca.height;
898 victimDetails.currentWidth = ca.width;
899 victimDetails.currentHeight = ca.height;
900
901 D("Found victim=0x%x, x=%i, y=%i, width=%i, height=%i, "
902 "border=%i\n",
903 (unsigned) victimDetails.window,
904 victimDetails.x,
905 victimDetails.y,
906 victimDetails.idealWidth,
907 victimDetails.idealHeight,
908 victimDetails.borderWidth);
909
910 /* To avoid losing events, enable monitoring events on
911 * victim at earlist opportunity */
912
913 XSelectInput(dpy, victimDetails.window,
914 StructureNotifyMask | FocusChangeMask
915 | EnterWindowMask | LeaveWindowMask);
916 XSync(dpy, False);
917
918 XSelectInput(dpy, wattr.root, 0);
919
920 #ifdef USE_MUTEX_LOCK
921 giveSwallowMutex(mutex);
922 #endif
923 return true;
924 }
925 else
926 {
927 D("Window 0x%x already claimed by another"
928 " instance of mozplugger\n", (unsigned) window);
929 }
930 }
931 else
932 {
933 D("XGetWindowAttributes failed for 0x%x\n",
934 (unsigned) window);
935 }
936 }
937 }
938 return false;
939 }
940
941 /**
942 * handle X event for the root window. Mozplugger in effect is acting like a
943 * window manager by intercepting root window events. The one event it would
944 * like to intercept, but cannot is the MapRequest. It cannot because only one
945 * client is allowed to intercept that event (i.e. the real window manager). When
946 * the real window manager receives the MapRequest, it will most likely try and
947 * reparent that window into a frame containing decorations (i.e. title bar,
948 * etc). The only way mozplugger knows this has happened is when it sees the
949 * ReparentNotify. Ideally Mozplugger needs to over-ride this reparenting, the
950 * current solution is to reparent that window again. Result is a fight with
951 * window manager. The alternative is to set the override_redirect attribute
952 * on the window (this stops the MapRequest event). But according to ICCCM this
953 * is bad practice, so mozplugger only does it as a last resort (see
954 * reparent_window function above).
955 *
956 * An alternative would be to reparent the window before the MapRequest i.e.
957 * when the window is invisible. Unfortunately some apps create many invisible
958 * root windows and it is difficult to know which window is the one to grab until
959 * the application makes it clear by Mapping that window.
960 *
961 * @param[in] dpy The display pointer
962 * @param[in] rootWin The window ID
963 * @param[in] ev the event
964 * @param[in] mutex The swallow mutex
965 *
966 * @return none
967 */
handle_rootWindow_event(Display * dpy,Window rootWin,const XEvent * const ev,SwallowMutex_t * mutex)968 static void handle_rootWindow_event(Display * dpy, Window rootWin, const XEvent * const ev,
969 SwallowMutex_t * mutex)
970 {
971 switch (ev->type)
972 {
973 case CreateNotify:
974 D("***CreateNotify for root, window=0x%x, "
975 "override_redirect=%i\n",
976 (unsigned) ev->xcreatewindow.window,
977 ev->xcreatewindow.override_redirect);
978 /* All toplevel new app windows will be created on desktop (root),
979 * so lets keep a list as they are created */
980 add_possible_victim(ev->xcreatewindow.window);
981 break;
982
983 case MapNotify:
984 D("***MapNotify for root, window=0x%x, override_redirect=%i\n",
985 (unsigned) ev->xmap.window,
986 ev->xmap.override_redirect);
987 /* A window has been mapped, if window manager is allowed to
988 * fiddle, lets see if this is the window */
989 if(!ev->xmap.override_redirect)
990 {
991 Window wnd = ev->xmap.window;
992 if(find_victim(dpy, wnd, mutex))
993 {
994 /* If we get MapNotify on root before ReparentNotify for our
995 * victim this means either:-
996 * (1) There is no reparenting window manager running
997 * (2) We are running over NX
998 * With NX, we get the MapNotify way too early! (window not
999 * mapped yet on the server), so need to be clever?
1000 * Set flag, this will be used later in the select loop */
1001 D("Looks like no reparenting WM running\n");
1002 change_leader(dpy, victimDetails.window, rootWin);
1003 victimDetails.noWmRunning = true;
1004 reparent_window(dpy);
1005 }
1006 }
1007 break;
1008
1009
1010 case ReparentNotify:
1011 D("***ReparentNotify for root, parent=0x%x, window=0x%x, "
1012 "x=%i, y=%i, override_redirect=%i\n",
1013 (unsigned) ev->xreparent.parent,
1014 (unsigned) ev->xreparent.window,
1015 ev->xreparent.x, ev->xreparent.y,
1016 ev->xreparent.override_redirect);
1017
1018 /* A window has been reparented, if window manager is allowed to
1019 * fiddle, lets see if this is the window we are looking for */
1020 if(!ev->xreparent.override_redirect)
1021 {
1022 Window wnd = ev->xreparent.window;
1023
1024 if(find_victim(dpy, wnd, mutex))
1025 {
1026 /* Avoid the fight with window manager to get reparent,
1027 * instead be kind and withdraw window and wait for WM
1028 * to reparent window back to root */
1029 D("Withdraw window 0x%x\n", (unsigned) wnd);
1030
1031 XWithdrawWindow(dpy, wnd, DefaultScreen(dpy));
1032 #if 0
1033 change_leader(dpy, victimDetails.window, rootWin);
1034 reparent_window(dpy);
1035 #endif
1036 }
1037 }
1038 break;
1039
1040
1041 case ConfigureNotify:
1042 D("***ConfigureNotify for root, window=0x%x, "
1043 "x=%i, y=%i, width=%i, height=%i, override_redirect=%i\n",
1044 (unsigned) ev->xconfigure.window,
1045 ev->xconfigure.x, ev->xconfigure.y,
1046 ev->xconfigure.width,
1047 ev->xconfigure.height,
1048 ev->xconfigure.override_redirect);
1049 break;
1050
1051
1052
1053 case UnmapNotify:
1054 D("***UnmapNotify for root, window=0x%x, from_configure=%i, "
1055 "send_event=%i\n", (unsigned) ev->xunmap.window,
1056 ev->xunmap.from_configure,
1057 ev->xunmap.send_event);
1058 break;
1059
1060
1061 case DestroyNotify:
1062 D("***DestroyNotify for root, window=0x%x\n",
1063 (unsigned) ev->xdestroywindow.window);
1064 break;
1065
1066 case ClientMessage:
1067 D("***ClientMessage for root\n");
1068 break;
1069
1070 default:
1071 D("!!Got unhandled event for root->%d\n", ev->type);
1072 break;
1073 }
1074 }
1075
1076 /**
1077 * handle X event for the victim window
1078 *
1079 * @param[in] dpy The display pointer
1080 * @param[in] ev the event
1081 *
1082 * @return none
1083 */
handle_victimWindow_event(Display * dpy,const XEvent * const ev)1084 static void handle_victimWindow_event(Display * dpy, const XEvent * const ev)
1085 {
1086 switch (ev->type)
1087 {
1088 case UnmapNotify:
1089 D("UNMAPNOTIFY for victim, window=0x%x, from_configure=%i, "
1090 "send_event=%i\n", (unsigned) ev->xunmap.window,
1091 ev->xunmap.from_configure,
1092 ev->xunmap.send_event);
1093 victimDetails.mapped = false;
1094 break;
1095
1096 case MapNotify:
1097 D("MAPNOTIFY for victim, window=0x%x, override_redirect=%i\n",
1098 (unsigned) ev->xmap.window,
1099 ev->xmap.override_redirect);
1100 victimDetails.mapped = true;
1101 if(victimDetails.reparented)
1102 {
1103 adjust_window_size(dpy);
1104 }
1105 break;
1106
1107 case ReparentNotify:
1108 {
1109 const XReparentEvent * evt = &ev->xreparent;
1110
1111 if (evt->parent == parentDetails.window)
1112 {
1113 D("REPARENT NOTIFY on victim to the right window, "
1114 "parent=0x%x, window=0x%x, "
1115 "x=%i, y=%i, override_redirect=%i\n",
1116 (unsigned) evt->parent,
1117 (unsigned) evt->window,
1118 evt->x, evt->y,
1119 evt->override_redirect);
1120 victimDetails.reparented = true;
1121 if(!victimDetails.mapped)
1122 {
1123 D("XMapWindow(0x%x)\n", (unsigned) victimDetails.window);
1124 XMapWindow(dpy, victimDetails.window);
1125 }
1126 else
1127 {
1128 adjust_window_size(dpy);
1129 }
1130 }
1131 else
1132 {
1133 D("REPARENT NOTIFY on victim to some other window! "
1134 "parent=0x%x, window=0x%x, "
1135 "x=%i, y=%i, override_redirect=%i\n",
1136 (unsigned) evt->parent,
1137 (unsigned) evt->window,
1138 evt->x, evt->y,
1139 evt->override_redirect);
1140 victimDetails.noWmRunning = false;
1141 victimDetails.reparented = false;
1142 reparent_window(dpy);
1143 }
1144 }
1145 break;
1146
1147 case ConfigureNotify:
1148 D("CONFIGURE NOTIFY for victim, window=0x%x,"
1149 " x=%d, y=%d, w=%d, h=%d, "
1150 "border_width=%d, override_redirect=%i\n",
1151 (unsigned) ev->xconfigure.window,
1152 ev->xconfigure.x, ev->xconfigure.y,
1153 ev->xconfigure.width,
1154 ev->xconfigure.height,
1155 ev->xconfigure.border_width,
1156 ev->xconfigure.override_redirect);
1157 break;
1158
1159 case ClientMessage:
1160 D("CLIENT MESSAGE for victim\n");
1161 /* I see this with evince! perhaps it needs to be handled? */
1162 break;
1163
1164 case DestroyNotify:
1165 D("DESTROY NOTIFY for victim, window=0x%x\n",
1166 (unsigned) ev->xdestroywindow.window);
1167 if(ev->xdestroywindow.window == victimDetails.window)
1168 {
1169 XSelectInput(dpy, victimDetails.window, 0);
1170 victimDetails.window = 0;
1171 }
1172 break;
1173
1174 case EnterNotify:
1175 D("ENTER NOTIFY for victim, window=0x%x\n", (unsigned) ev->xcrossing.window);
1176 if(ev->xcrossing.mode == NotifyGrab)
1177 {
1178 D("Pointer grabbed by child!!\n");
1179 }
1180 break;
1181
1182 default:
1183 {
1184 char * name = "";
1185 switch (ev->type)
1186 {
1187 case FocusIn: name="FOCUS IN"; break;
1188 case FocusOut: name="FOCUS OUT"; break;
1189 case LeaveNotify: name="LEAVE NOTIFY"; break;
1190 }
1191 D("!!Got unhandled event for victim->%s(%d)\n", name, ev->type);
1192 }
1193 break;
1194 }
1195 }
1196
1197 /**
1198 * handle X event for the parent window
1199 *
1200 * @param[in] dpy The display Pointer
1201 * @param[in] ev the event
1202 *
1203 * @return none
1204 */
handle_parentWindow_event(Display * dpy,const XEvent * const ev)1205 static void handle_parentWindow_event(Display * dpy, const XEvent * const ev)
1206 {
1207 unsigned long mask;
1208 switch (ev->type)
1209 {
1210 case ConfigureRequest:
1211 mask = ev->xconfigurerequest.value_mask;
1212 D("ConfigureRequest to WINDOW mask=0x%lx\n", mask);
1213 if(ev->xconfigurerequest.window != victimDetails.window)
1214 {
1215 D("- Strange Configure Request not for victim\n");
1216 }
1217 else
1218 {
1219 int adjustWindow = false;
1220
1221 if(mask & (CWHeight | CWWidth))
1222 {
1223 D(" - request to set width & height\n");
1224 victimDetails.idealWidth = ev->xconfigurerequest.width,
1225 victimDetails.idealHeight = ev->xconfigurerequest.height;
1226 adjustWindow = true;
1227 }
1228 if(mask & (CWBorderWidth))
1229 {
1230 const int w = ev->xconfigurerequest.border_width;
1231 D("- request to set border width=%d\n", w);
1232 if(victimDetails.borderWidth != w)
1233 {
1234 victimDetails.borderWidth = w;
1235 adjustWindow = true;
1236 }
1237 XSetWindowBorderWidth(dpy, victimDetails.window,
1238 (unsigned) w);
1239 }
1240 /* Only adjust if window has been mapped and reparented */
1241 if(adjustWindow && victimDetails.mapped
1242 && victimDetails.reparented)
1243 {
1244 adjust_window_size(dpy);
1245 }
1246 }
1247 break;
1248
1249 default:
1250 D("!!Got unhandled event for PARENT->%d\n", ev->type);
1251 break;
1252 }
1253 }
1254
1255 /**
1256 * Check events from X
1257 * Read in events from X and process, keep reading in events until no more
1258 * and then exit. It is important that all events have been processed and
1259 * not left pending.
1260 *
1261 * @param[in] dpy The display pointer
1262 * @param[in] mutex The swallow mutex
1263 *
1264 * @return none
1265 */
check_x_events(Display * dpy,SwallowMutex_t * mutex)1266 static void check_x_events(Display * dpy, SwallowMutex_t * mutex)
1267 {
1268 int numEvents = XPending(dpy);
1269
1270 /* While some events pending... Get and action */
1271 while (numEvents > 0)
1272 {
1273 XEvent ev;
1274
1275 XNextEvent(dpy, &ev);
1276
1277 if (ev.xany.window == wattr.root)
1278 {
1279 handle_rootWindow_event(dpy, wattr.root, &ev, mutex);
1280 }
1281 else if (victimDetails.window
1282 && ev.xany.window == victimDetails.window)
1283 {
1284 handle_victimWindow_event(dpy, &ev);
1285 }
1286 else if (parentDetails.window
1287 && (ev.xany.window == parentDetails.window))
1288 {
1289 handle_parentWindow_event(dpy, &ev);
1290 }
1291 else
1292 {
1293 D("!!Got unhandled event for unknown->%d\n", ev.type);
1294 }
1295
1296 /* If this is the last of this batch, check that more havent
1297 * been added in the meantime as a result of an action */
1298 numEvents--;
1299 if(numEvents == 0)
1300 {
1301 numEvents = XPending(dpy);
1302 }
1303 }
1304 }
1305
1306 /**
1307 * Terminate, called from signal handler or when SHUTDOWN_MSG received
1308 *
1309 * @param[in] mutex The swallow mutex
1310 * @param[in] pid The process ID of the mutex
1311 * @param[in] dpy The display pointer
1312 */
terminate(SwallowMutex_t * mutex,pid_t pid,Display * dpy)1313 static void terminate(SwallowMutex_t * mutex, pid_t pid, Display * dpy)
1314 {
1315 #ifdef USE_MUTEX_LOCK
1316 giveSwallowMutex(mutex);
1317 #endif
1318
1319 if(pid >= 0)
1320 {
1321 kill_app(pid);
1322 }
1323
1324 if(dpy)
1325 {
1326 XCloseDisplay(dpy);
1327 }
1328 }
1329
1330
1331 /**
1332 * Check events from pipe connected to mozplugger
1333 * Read in events from pipe connected to mozplugger and process
1334 *
1335 * @param[in] dpy The display pointer
1336 * @param[in] mutex The swallow mutex
1337 * @param[in] pid The process ID of the mutex
1338 *
1339 * @return none
1340 */
check_pipe_fd_events(Display * dpy,SwallowMutex_t * mutex)1341 static void check_pipe_fd_events(Display * dpy, SwallowMutex_t * mutex)
1342 {
1343 struct PipeMsg_s msg;
1344 int n;
1345
1346 Window oldwindow = parentDetails.window;
1347
1348 n = read(pipe_fd, ((char *)&msg), sizeof(msg));
1349 if((n == 0) || ((n < 0) && (errno != EINTR)))
1350 {
1351 D("Pipe read error, returned n=%i\n", n);
1352 terminate(mutex, sig_globals.childPid, dpy);
1353 exit(EX_UNAVAILABLE);
1354 }
1355
1356 if (n != sizeof(msg))
1357 {
1358 if(n > 0)
1359 {
1360 D("Pipe msg too short, size = %i\n", n);
1361 }
1362 return;
1363 }
1364
1365 switch(msg.msgType)
1366 {
1367 case WINDOW_MSG:
1368 parentDetails.window = (Window) msg.window_msg.window;
1369 parentDetails.width = (int) msg.window_msg.width;
1370 parentDetails.height = (int) msg.window_msg.height;
1371 D("Read Pipe, got WINDOW_MSG (win=0x%x - %i x %i)\n",
1372 (unsigned) parentDetails.window, parentDetails.width, parentDetails.height);
1373 break;
1374
1375 case SHUTDOWN_MSG:
1376 D("Read Pipe, got SHUTDOWN_MSG, terminating..\n");
1377 terminate(mutex, sig_globals.childPid, dpy);
1378 exit(EXIT_SUCCESS);
1379
1380 default:
1381 D("Read Pipe, got unknown msg\n");
1382 return;
1383 }
1384
1385 if (parentDetails.window && dpy )
1386 {
1387 if(parentDetails.window != oldwindow)
1388 {
1389 D("Switch parent window from 0x%x to 0x%x\n", (unsigned)oldwindow, (unsigned) parentDetails.window);
1390 victimDetails.reparented = false;
1391
1392 /* To avoid losing events, enable monitoring events on new parent
1393 * before disabling monitoring events on the old parent */
1394
1395 XSelectInput(dpy, parentDetails.window, SubstructureRedirectMask | FocusChangeMask);
1396 XSync(dpy, False);
1397 XSelectInput(dpy, oldwindow, 0);
1398
1399 if(victimDetails.window)
1400 {
1401 reparent_window(dpy);
1402 }
1403 else
1404 {
1405 D("Victim window not ready to be reparented\n");
1406 }
1407 }
1408
1409 if(victimDetails.window && victimDetails.mapped && victimDetails.reparented)
1410 {
1411 /* The window has been resized. */
1412 adjust_window_size(dpy);
1413 }
1414 else
1415 {
1416 D("Victim window not ready to be adjusted\n");
1417 }
1418 }
1419 }
1420
1421 /**
1422 * Check the app status
1423 *
1424 * @param[in] flags The command flags
1425 *
1426 * @return -2 if broken, -1
1427 */
check_app_status(unsigned flags)1428 static int check_app_status(unsigned flags)
1429 {
1430 int retVal = 1;
1431 int status = wait_child(sig_globals.childPid);
1432
1433 switch(status)
1434 {
1435 case -2: /* App crashed */
1436 sig_globals.childPid = -1;
1437 exit(EX_UNAVAILABLE);
1438 break;
1439
1440 case -1: /* App exited OK */
1441 sig_globals.childPid = -1;
1442 if (!(flags & H_IGNORE_ERRORS))
1443 {
1444 exit(WEXITSTATUS(status));
1445 }
1446 break;
1447
1448 case 0: /* App deattached */
1449 sig_globals.childPid = -1;
1450 if((flags & H_DAEMON) == 0)
1451 {
1452 retVal = -1;
1453 }
1454 else
1455 {
1456 D("Child process has detached, keep going\n");
1457 retVal = 0;
1458 }
1459 /* case 1: App still running */
1460 }
1461 return retVal;
1462 }
1463
1464 /**
1465 * Keep checking all events until application dies
1466 * Use select to check if any events need processing
1467 *
1468 * @param[in] dpy The display pointer
1469 * @param[in] mutex The swallow mutex
1470 *
1471 * @return none
1472 */
check_all_events(Display * dpy,SwallowMutex_t * mutex)1473 static void check_all_events(Display * dpy, SwallowMutex_t * mutex)
1474 {
1475 int sig_chld_fd = get_SIGCHLD_fd();
1476
1477 while(1)
1478 {
1479 int rd_chld_fd = get_chld_out_fd();
1480 int selectRetVal;
1481 int maxfd = 0;
1482
1483 struct timeval timeout;
1484 struct timeval * pTimeout = 0;
1485
1486 fd_set fds;
1487
1488 FD_ZERO(&fds);
1489 if (dpy)
1490 {
1491 check_x_events(dpy, mutex);
1492
1493 maxfd = ConnectionNumber(dpy);
1494 FD_SET(ConnectionNumber(dpy), &fds);
1495 /* If NX is is use and nxagent is configured as rootless, it can
1496 * appear that there is no WM running, but actually there is and its
1497 * actions (reparenting to add decorations) is invisible. In this
1498 * case just reparent 4 times in a row with 1 second gaps. If the
1499 * link latency > 4 seconds this may fail? */
1500 if((victimDetails.noWmRunning)
1501 && (victimDetails.reparentedAttemptCount < 4))
1502 {
1503 pTimeout = &timeout;
1504 pTimeout->tv_usec = 50000;
1505 pTimeout->tv_sec = 0;
1506 }
1507 }
1508 maxfd = pipe_fd > maxfd ? pipe_fd : maxfd;
1509 FD_SET(pipe_fd, &fds);
1510 if(sig_chld_fd >= 0)
1511 {
1512 maxfd = sig_chld_fd > maxfd ? sig_chld_fd : maxfd;
1513 FD_SET(sig_chld_fd, &fds);
1514 }
1515 if(rd_chld_fd >= 0)
1516 {
1517 maxfd = rd_chld_fd > maxfd ? rd_chld_fd : maxfd;
1518 FD_SET(rd_chld_fd, &fds);
1519 }
1520
1521 D("SELECT IN %s\n", pTimeout ? "with timeout" : "");
1522 selectRetVal = select(maxfd + 1, &fds, NULL, NULL, pTimeout);
1523 D("SELECT OUT\n");
1524
1525 if(selectRetVal > 0)
1526 {
1527 if((pipe_fd >= 0) && FD_ISSET(pipe_fd, &fds))
1528 {
1529 check_pipe_fd_events(dpy, mutex);
1530 }
1531 if((sig_chld_fd >= 0) && FD_ISSET(sig_chld_fd, &fds))
1532 {
1533 handle_SIGCHLD_event();
1534 }
1535 if((rd_chld_fd >= 0) && FD_ISSET(rd_chld_fd, &fds))
1536 {
1537 handle_chld_out_event(rd_chld_fd);
1538 }
1539 }
1540 else if((selectRetVal == 0) && pTimeout)
1541 {
1542 D("Select timeout and suspect invisible WM\n");
1543 reparent_window(dpy);
1544 }
1545 else
1546 {
1547 D("Select exited unexpected, errno = %i\n", errno);
1548 }
1549
1550 if(sig_globals.childPid >= 0)
1551 {
1552 int retVal = check_app_status(flags);
1553 if(retVal < 0)
1554 {
1555 return;
1556 }
1557 if(retVal == 0)
1558 {
1559 sig_globals.childPid = -1;
1560 }
1561 }
1562 }
1563 }
1564
1565 /**
1566 * Handle the application
1567 * Wait for application to finish and exit helper if a problem
1568 *
1569 * @param[in] dpy The display pointer
1570 * @param[in] mutex The swallow mutex
1571 *
1572 * @return none
1573 */
handle_app(Display * dpy,SwallowMutex_t * mutex)1574 static void handle_app(Display * dpy, SwallowMutex_t * mutex)
1575 {
1576 if (flags & H_SWALLOW)
1577 {
1578 /* Whilst waiting for the Application to complete, check X events */
1579 check_all_events(dpy, mutex);
1580
1581 #ifdef USE_MUTEX_LOCK
1582 /* Make sure the semaphore has been released */
1583 giveSwallowMutex(mutex);
1584 #endif
1585 }
1586 else if(flags & H_DAEMON)
1587 {
1588 /* If Daemon, then it is not supposed to exit, so we exit instead */
1589 D("App has become a daemon, so exit\n");
1590 terminate(mutex, -1, dpy);
1591 exit(EXIT_SUCCESS);
1592 }
1593 else
1594 {
1595 check_all_events(NULL, NULL);
1596 }
1597
1598 sig_globals.childPid = -1;
1599 D("Exited OK!\n");
1600 }
1601
1602 /**
1603 * Exit early due to problem. Prints a message to stderr and then exits
1604 * the application
1605 *
1606 * @return none
1607 */
exitEarly(void)1608 static void exitEarly(void)
1609 {
1610 fprintf(stderr,"MozPlugger version " VERSION " helper application.\n"
1611 "Please see 'man mozplugger' for details.\n");
1612 exit(EXIT_FAILURE);
1613 }
1614
1615 /**
1616 * Expands the winname if it contains %f or %p by replacing %f by the file
1617 * name part of the URL or %p by the whole of thr URL.
1618 *
1619 * @param[in,out] buffer The data buffer to use
1620 * @param[in] bufferLen Size of buffer
1621 * @param[in] winname The window name
1622 * @param[in] file The file name associated with child
1623 *
1624 * @return none
1625 */
expand_winname(char * buffer,int bufferLen,const char * winame,const char * file)1626 static void expand_winname(char * buffer, int bufferLen, const char * winame, const char * file)
1627 {
1628 const char * p = winame;
1629 char * q = buffer;
1630 char * end = &buffer[bufferLen-1];
1631
1632 D("winame before expansion is '%s'\n", winname);
1633
1634 while((*p != '\0') && (q < end))
1635 {
1636 if( *p != '%')
1637 {
1638 *q++ = *p++;
1639 }
1640 else
1641 {
1642 const char * r;
1643 switch(p[1])
1644 {
1645 case '%' :
1646 *q++ = '%';
1647 p += 2;
1648 break;
1649
1650 case 'f' : /* %f = insert just the filename no path */
1651 r = strrchr(file, '/'); /* Find the filename start */
1652 if(r == 0)
1653 {
1654 r = file;
1655 }
1656 else
1657 {
1658 r++; /* Skip the slash */
1659 }
1660 while((*r != '\0') && (q < end))
1661 {
1662 *q++ = *r++;
1663 }
1664 p += 2; /* Skip the %f */
1665 break;
1666
1667 case 'p' :
1668 r = file;
1669 while((*r != '\0') && (q < end))
1670 {
1671 *q++ = *r++;
1672 }
1673 p += 2;
1674 break;
1675
1676 default :
1677 *q++ = *p++;
1678 break;
1679 }
1680 }
1681 }
1682 *q = '\0';
1683
1684 D("winame after expansion is '%s'\n", buffer);
1685 }
1686
1687 /**
1688 * Handle the SIGTERM signal. Terminate the helper and child. Calling C-lib
1689 * functions in a signal handler is not good, so avoid if we can. Best if
1690 * mozplugger.so uses the SHUTDOWN_MSG.
1691 *
1692 * @return none
1693 */
sigTerm()1694 static void sigTerm()
1695 {
1696 D("SIGTERM received\n");
1697 terminate(sig_globals.mutex, sig_globals.childPid, sig_globals.dpy);
1698 _exit(EXIT_SUCCESS);
1699 }
1700
1701 /**
1702 * main() - normally called from the child process started by mozplugger.so
1703 *
1704 * @param[in] argc The number of arguments
1705 * @param[in] argv List of arguments
1706 *
1707 * @return Never returns (unless app exits)
1708 */
main(int argc,char ** argv)1709 int main(int argc, char **argv)
1710 {
1711 char buffer[100];
1712
1713 unsigned long temp = 0;
1714 int i;
1715 int repeats;
1716 Display * dpy;
1717 SwallowMutex_t mutex;
1718 char * command;
1719
1720 D("Helper started.....\n");
1721
1722 if (argc < 3)
1723 {
1724 exitEarly();
1725 }
1726
1727 memset(&victimDetails, 0, sizeof(victimDetails));
1728 memset(&parentDetails, 0, sizeof(parentDetails));
1729
1730 i = sscanf(argv[1],"%d,%d,%d,%lu,%d,%d",
1731 &flags,
1732 &repeats,
1733 &pipe_fd,
1734 &temp,
1735 &parentDetails.width,
1736 &parentDetails.height);
1737
1738 if(i < 6)
1739 {
1740 exitEarly();
1741 }
1742
1743 parentDetails.window = (Window)temp;
1744
1745 command = argv[2];
1746 winname = getenv("winname");
1747 file = getenv("file");
1748
1749 if(winname)
1750 {
1751 expand_winname(buffer, sizeof(buffer), winname, file);
1752 winname = buffer;
1753 }
1754 D("HELPER: %s %s %s %s\n",
1755 argv[0],
1756 argv[1],
1757 file,
1758 command);
1759
1760 redirect_SIGCHLD_to_fd();
1761
1762 /* Create handler for when terminating the helper */
1763
1764
1765 sig_globals.dpy = dpy = setup_display();
1766 sig_globals.mutex = NULL;
1767 sig_globals.childPid = -1;
1768
1769 signal(SIGTERM, sigTerm);
1770
1771 if (!dpy)
1772 {
1773 if( (flags & H_SWALLOW) != 0)
1774 {
1775 D("Failed to open X display - canceling swallow functionality\n");
1776 flags &=~ H_SWALLOW;
1777 }
1778 }
1779
1780 if (repeats < 1)
1781 {
1782 repeats = 1;
1783 }
1784
1785 while (repeats > 0)
1786 {
1787 int loops = 1;
1788 pid_t pid;
1789
1790 /* This application will use the $repeat variable */
1791 if (flags & H_REPEATCOUNT)
1792 {
1793 loops = repeats;
1794 }
1795
1796 /* Expecting the application to loop */
1797 if (flags & H_LOOP)
1798 {
1799 loops = INF_LOOPS;
1800 }
1801
1802 if (flags & H_SWALLOW)
1803 {
1804 #ifdef USE_MUTEX_LOCK
1805 /* If we are swallowing a victim window we need to guard
1806 against more than one instance of helper running in
1807 parallel, this is done using a global mutex semaphore.
1808 Although its not successful in all cases! */
1809 initSwallowMutex(dpy, wattr.root, &mutex);
1810 sig_globals.mutex = &mutex;
1811
1812 /* There is a race condition when taking the semaphore that
1813 * means occasionally we think we have it but we dont
1814 * This do-while loop checks for that case - this
1815 * is better than putting a wait in the take semaphore
1816 * rechecking loop fixes Mozdev bug 20088 */
1817 do
1818 {
1819 takeSwallowMutex(&mutex);
1820 }
1821 while(!stillHaveMutex(&mutex));
1822 #endif
1823 XSelectInput(dpy, parentDetails.window, SubstructureRedirectMask);
1824 XSelectInput(dpy, wattr.root, SubstructureNotifyMask);
1825 XSync(dpy, False);
1826 }
1827
1828 pid = spawn_app(command, flags);
1829 if(pid == -1)
1830 {
1831 terminate(&mutex, -1, dpy);
1832 exit(EX_UNAVAILABLE);
1833 }
1834 sig_globals.childPid = pid;
1835
1836 D("Waiting for pid=%d\n", pid);
1837
1838 handle_app(dpy, &mutex);
1839
1840 D("Wait done (repeats=%d, loops=%d)\n", repeats, loops);
1841 if (repeats < INF_LOOPS)
1842 {
1843 repeats -= loops;
1844 }
1845 }
1846
1847 D("All done\n");
1848 terminate(&mutex, -1, dpy);
1849 return EXIT_SUCCESS;
1850 }
1851