"Fossies" - the Fresh Open Source Software Archive

Member "mozplugger-2.1.6/mozplugger-helper.c" (17 Apr 2014, 55111 Bytes) of package /linux/www/old/mozplugger-2.1.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
 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  */
 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  */
 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  */
 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  */
 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  */
 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  */
 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  */
 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  */
 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  */
 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  */
 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  */
 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 }