libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

extractor_ipc_w32.c (23329B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2002, 2003, 2004, 2005, 2006, 2009, 2012 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "platform.h"
     22 #include "plibc.h"
     23 #include "extractor.h"
     24 #include "extractor_datasource.h"
     25 #include "extractor_plugin_main.h"
     26 #include "extractor_plugins.h"
     27 #include "extractor_ipc.h"
     28 #include "extractor_logging.h"
     29 
     30 /**
     31  */
     32 struct EXTRACTOR_SharedMemory
     33 {
     34 
     35   /**
     36    * W32 handle of the shm into which data is uncompressed
     37    */
     38   HANDLE map;
     39 
     40   /**
     41    * Name of the shm
     42    */
     43   char shm_name[MAX_SHM_NAME + 1];
     44 
     45   /**
     46    * Pointer to the mapped region of the shm (covers the whole shm)
     47    */
     48   void *ptr;
     49 
     50   /**
     51    * Position within shm
     52    */
     53   int64_t pos;
     54 
     55   /**
     56    * Allocated size of the shm
     57    */
     58   int64_t shm_size;
     59 
     60   /**
     61    * Number of bytes in shm (<= shm_size)
     62    */
     63   size_t shm_buf_size;
     64 
     65   size_t shm_map_size;
     66 
     67   /**
     68    * Reference counter describing how many references share this SHM.
     69    */
     70   unsigned int rc;
     71 };
     72 
     73 
     74 /**
     75  * Definition of an IPC communication channel with
     76  * some plugin.
     77  */
     78 struct EXTRACTOR_Channel
     79 {
     80 
     81   /**
     82    * Process ID of the child process for this plugin. 0 for none.
     83    */
     84   HANDLE hProcess;
     85 
     86   /**
     87    * Pipe used to communicate information to the plugin child process.
     88    * NULL if not initialized.
     89    */
     90   HANDLE cpipe_in;
     91 
     92   /**
     93    * Handle of the shm object
     94    */
     95   HANDLE map_handle;
     96 
     97   /**
     98    * Pipe used to read information about extracted meta data from
     99    * the plugin child process.  -1 if not initialized.
    100    */
    101   HANDLE cpipe_out;
    102 
    103   /**
    104    * A structure for overlapped reads on W32.
    105    */
    106   OVERLAPPED ov_read;
    107 
    108   /**
    109    * A structure for overlapped writes on W32.
    110    */
    111   OVERLAPPED ov_write;
    112 
    113   /**
    114    * A write buffer for overlapped writes on W32
    115    */
    116   unsigned char *ov_write_buffer;
    117 
    118   /**
    119    * The plugin this channel is to communicate with.
    120    */
    121   struct EXTRACTOR_PluginList *plugin;
    122 
    123   /**
    124    * Memory segment shared with this process.
    125    */
    126   struct EXTRACTOR_SharedMemory *shm;
    127 
    128   void *old_buf;
    129 
    130   /**
    131    * Buffer for reading data from the plugin.
    132    */
    133   char *mdata;
    134 
    135   /**
    136    * Size of the 'mdata' buffer.
    137    */
    138   size_t mdata_size;
    139 
    140   /**
    141    * Number of valid bytes in the channel's buffer.
    142    */
    143   size_t size;
    144 };
    145 
    146 
    147 /**
    148  * Create a shared memory area.
    149  *
    150  * @param size size of the shared area
    151  * @return NULL on error
    152  */
    153 struct EXTRACTOR_SharedMemory *
    154 EXTRACTOR_IPC_shared_memory_create_ (size_t size)
    155 {
    156   struct EXTRACTOR_SharedMemory *shm;
    157   const char *tpath = "Local\\";
    158 
    159   if (NULL == (shm = malloc (sizeof (struct EXTRACTOR_SharedMemory))))
    160     return NULL;
    161 
    162   snprintf (shm->shm_name, MAX_SHM_NAME,
    163             "%slibextractor-shm-%u-%u",
    164             tpath, getpid (),
    165             (unsigned int) random ());
    166   shm->map = CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
    167                                 size, shm->shm_name);
    168   shm->ptr = MapViewOfFile (shm->map, FILE_MAP_WRITE, 0, 0, size);
    169   if (shm->ptr == NULL)
    170   {
    171     CloseHandle (shm->map);
    172     free (shm);
    173     return NULL;
    174   }
    175   shm->shm_size = size;
    176   shm->rc = 0;
    177   return shm;
    178 }
    179 
    180 
    181 /**
    182  * Change the reference counter for this shm instance.
    183  *
    184  * @param shm instance to update
    185  * @param delta value to change RC by
    186  * @return new RC
    187  */
    188 unsigned int
    189 EXTRACTOR_IPC_shared_memory_change_rc_ (struct EXTRACTOR_SharedMemory *shm,
    190                                         int delta)
    191 {
    192   shm->rc += delta;
    193   return shm->rc;
    194 }
    195 
    196 
    197 /**
    198  * Destroy shared memory area.
    199  *
    200  * @param shm memory area to destroy
    201  * @return NULL on error
    202  */
    203 void
    204 EXTRACTOR_IPC_shared_memory_destroy_ (struct EXTRACTOR_SharedMemory *shm)
    205 {
    206   if (shm->ptr != NULL)
    207     UnmapViewOfFile (shm->ptr);
    208   if (shm->map != 0)
    209     CloseHandle (shm->map);
    210   free (shm);
    211 }
    212 
    213 
    214 /**
    215  * Initialize shared memory area from data source.
    216  *
    217  * @param shm memory area to initialize
    218  * @param ds data source to use for initialization
    219  * @param off offset to use in data source
    220  * @param size number of bytes to copy
    221  * @return -1 on error, otherwise number of bytes copied
    222  */
    223 ssize_t
    224 EXTRACTOR_IPC_shared_memory_set_ (struct EXTRACTOR_SharedMemory *shm,
    225                                   struct EXTRACTOR_Datasource *ds,
    226                                   uint64_t off,
    227                                   size_t size)
    228 {
    229   if (-1 ==
    230       EXTRACTOR_datasource_seek_ (ds, off, SEEK_SET))
    231   {
    232     LOG ("Failed to set IPC memory due to seek error\n");
    233     return -1;
    234   }
    235   if (size > shm->shm_size)
    236     size = shm->shm_size;
    237   return EXTRACTOR_datasource_read_ (ds,
    238                                      shm->ptr,
    239                                      size);
    240 }
    241 
    242 
    243 /**
    244  * Query datasource for current position
    245  *
    246  * @param ds data source to query
    247  * @return current position in the datasource or UINT_MAX on error
    248  */
    249 uint64_t
    250 EXTRACTOR_datasource_get_pos_ (struct EXTRACTOR_Datasource *ds)
    251 {
    252   int64_t pos = EXTRACTOR_datasource_seek_ (ds, 0, SEEK_CUR);
    253   if (-1 == pos)
    254     return UINT_MAX;
    255   return pos;
    256 }
    257 
    258 
    259 #ifndef PIPE_BUF
    260 #define PIPE_BUF 512
    261 #endif
    262 
    263 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
    264    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
    265 */
    266 /* Create a pipe, and return handles to the read and write ends,
    267    just like CreatePipe, but ensure that the write end permits
    268    FILE_READ_ATTRIBUTES access, on later versions of win32 where
    269    this is supported.  This access is needed by NtQueryInformationFile,
    270    which is used to implement select and nonblocking writes.
    271    Note that the return value is either NO_ERROR or GetLastError,
    272    unlike CreatePipe, which returns a bool for success or failure.  */
    273 static int
    274 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
    275                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
    276                         DWORD dwReadMode, DWORD dwWriteMode)
    277 {
    278   /* Default to error. */
    279   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
    280 
    281   HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
    282 
    283   /* Ensure that there is enough pipe buffer space for atomic writes.  */
    284   if (psize < PIPE_BUF)
    285     psize = PIPE_BUF;
    286 
    287   char pipename[MAX_PATH];
    288 
    289   /* Retry CreateNamedPipe as long as the pipe name is in use.
    290    * Retrying will probably never be necessary, but we want
    291    * to be as robust as possible.  */
    292   while (1)
    293   {
    294     static volatile LONG pipe_unique_id;
    295 
    296     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
    297               getpid (), InterlockedIncrement ((LONG *) &pipe_unique_id));
    298     /* Use CreateNamedPipe instead of CreatePipe, because the latter
    299      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
    300      * access, on versions of win32 earlier than WinXP SP2.
    301      * CreatePipe also stupidly creates a full duplex pipe, which is
    302      * a waste, since only a single direction is actually used.
    303      * It's important to only allow a single instance, to ensure that
    304      * the pipe was not created earlier by some other process, even if
    305      * the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
    306      * because that is only available for Win2k SP2 and WinXP.  */read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode,
    307                                   PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,                                               /* max instances */
    308                                   psize,        /* output buffer size */
    309                                   psize,        /* input buffer size */
    310                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
    311 
    312     if (read_pipe != INVALID_HANDLE_VALUE)
    313     {
    314       break;
    315     }
    316 
    317     DWORD err = GetLastError ();
    318 
    319     switch (err)
    320     {
    321     case ERROR_PIPE_BUSY:
    322       /* The pipe is already open with compatible parameters.
    323        * Pick a new name and retry.  */
    324       continue;
    325     case ERROR_ACCESS_DENIED:
    326       /* The pipe is already open with incompatible parameters.
    327        * Pick a new name and retry.  */
    328       continue;
    329     case ERROR_CALL_NOT_IMPLEMENTED:
    330       /* We are on an older Win9x platform without named pipes.
    331        * Return an anonymous pipe as the best approximation.  */
    332       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
    333       {
    334         return 0;
    335       }
    336       err = GetLastError ();
    337       return err;
    338     default:
    339       return err;
    340     }
    341     /* NOTREACHED */
    342   }
    343 
    344   /* Open the named pipe for writing.
    345    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
    346   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
    347                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
    348                             0); /* handle to template file */
    349 
    350   if (write_pipe == INVALID_HANDLE_VALUE)
    351   {
    352     /* Failure. */
    353     DWORD err = GetLastError ();
    354 
    355     CloseHandle (read_pipe);
    356     return err;
    357   }
    358 
    359   /* Success. */
    360   *read_pipe_ptr = read_pipe;
    361   *write_pipe_ptr = write_pipe;
    362   return 0;
    363 }
    364 
    365 
    366 /**
    367  * Communicates plugin data (library name, options) to the plugin
    368  * process. This is only necessary on W32, where this information
    369  * is not inherited by the plugin, because it is not forked.
    370  *
    371  * @param plugin plugin context
    372  *
    373  * @return 0 on success, -1 on failure
    374  */
    375 static int
    376 write_plugin_data (struct EXTRACTOR_PluginList *plugin,
    377                    struct EXTRACTOR_Channel *channel)
    378 {
    379   size_t libname_len, shortname_len, opts_len;
    380   DWORD len;
    381   char *str;
    382   size_t total_len = 0;
    383   unsigned char *buf, *ptr;
    384   ssize_t write_result;
    385 
    386   libname_len = strlen (plugin->libname) + 1;
    387   total_len += sizeof (size_t) + libname_len;
    388   shortname_len = strlen (plugin->short_libname) + 1;
    389   total_len += sizeof (size_t) + shortname_len;
    390   if (plugin->plugin_options != NULL)
    391   {
    392     opts_len = strlen (plugin->plugin_options) + 1;
    393     total_len += opts_len;
    394   }
    395   else
    396   {
    397     opts_len = 0;
    398   }
    399   total_len += sizeof (size_t);
    400 
    401   buf = malloc (total_len);
    402   if (buf == NULL)
    403     return -1;
    404   ptr = buf;
    405   memcpy (ptr, &libname_len, sizeof (size_t));
    406   ptr += sizeof (size_t);
    407   memcpy (ptr, plugin->libname, libname_len);
    408   ptr += libname_len;
    409   memcpy (ptr, &shortname_len, sizeof (size_t));
    410   ptr += sizeof (size_t);
    411   memcpy (ptr, plugin->short_libname, shortname_len);
    412   ptr += shortname_len;
    413   memcpy (ptr, &opts_len, sizeof (size_t));
    414   ptr += sizeof (size_t);
    415   if (opts_len > 0)
    416   {
    417     memcpy (ptr, plugin->plugin_options, opts_len);
    418     ptr += opts_len;
    419   }
    420   write_result = EXTRACTOR_IPC_channel_send_ (channel, buf, total_len);
    421   free (buf);
    422   return total_len == write_result;
    423 }
    424 
    425 
    426 /**
    427  * Create a channel to communicate with a process wrapping
    428  * the plugin of the given name.  Starts the process as well.
    429  *
    430  * @param plugin the plugin
    431  * @param shm memory to share with the process
    432  * @return NULL on error, otherwise IPC channel
    433  */
    434 struct EXTRACTOR_Channel *
    435 EXTRACTOR_IPC_channel_create_ (struct EXTRACTOR_PluginList *plugin,
    436                                struct EXTRACTOR_SharedMemory *shm)
    437 {
    438   struct EXTRACTOR_Channel *channel;
    439   HANDLE p1[2];
    440   HANDLE p2[2];
    441   struct InitMessage *init;
    442   size_t slen;
    443 
    444   STARTUPINFOA startup;
    445   PROCESS_INFORMATION proc;
    446   char cmd[MAX_PATH + 1];
    447   char arg1[10], arg2[10];
    448   HANDLE p10_os_inh = INVALID_HANDLE_VALUE;
    449   HANDLE p21_os_inh = INVALID_HANDLE_VALUE;
    450   SECURITY_ATTRIBUTES sa;
    451 
    452   if (NULL == (channel = malloc (sizeof (struct EXTRACTOR_Channel))))
    453   {
    454     LOG_STRERROR ("malloc");
    455     return NULL;
    456   }
    457   memset (channel, 0, sizeof (struct EXTRACTOR_Channel));
    458   channel->mdata_size = 1024;
    459   if (NULL == (channel->mdata = malloc (channel->mdata_size)))
    460   {
    461     LOG_STRERROR ("malloc");
    462     free (channel);
    463     return NULL;
    464   }
    465   channel->shm = shm;
    466   channel->plugin = plugin;
    467   channel->size = 0;
    468 
    469   sa.nLength = sizeof (sa);
    470   sa.lpSecurityDescriptor = NULL;
    471   sa.bInheritHandle = FALSE;
    472 
    473   if (0 != create_selectable_pipe (&p1[0], &p1[1], &sa, 1024,
    474                                    FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED))
    475   {
    476     LOG_STRERROR ("pipe");
    477     free (channel);
    478     return NULL;
    479   }
    480   if (0 != create_selectable_pipe (&p2[0], &p2[1], &sa, 1024,
    481                                    FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED))
    482   {
    483     LOG_STRERROR ("pipe");
    484     CloseHandle (p1[0]);
    485     CloseHandle (p1[1]);
    486     free (channel);
    487     return NULL;
    488   }
    489 
    490   if (! DuplicateHandle (GetCurrentProcess (), p1[0], GetCurrentProcess (),
    491                          &p10_os_inh, 0, TRUE, DUPLICATE_SAME_ACCESS)
    492       || ! DuplicateHandle (GetCurrentProcess (), p2[1], GetCurrentProcess (),
    493                             &p21_os_inh, 0, TRUE, DUPLICATE_SAME_ACCESS))
    494   {
    495     LOG_STRERROR ("DuplicateHandle");
    496     if (p10_os_inh != INVALID_HANDLE_VALUE)
    497       CloseHandle (p10_os_inh);
    498     if (p21_os_inh != INVALID_HANDLE_VALUE)
    499       CloseHandle (p21_os_inh);
    500     CloseHandle (p1[0]);
    501     CloseHandle (p1[1]);
    502     CloseHandle (p2[0]);
    503     CloseHandle (p2[1]);
    504     CloseHandle (p1[0]);
    505     CloseHandle (p1[1]);
    506     free (channel);
    507     return NULL;
    508   }
    509 
    510   memset (&proc, 0, sizeof (PROCESS_INFORMATION));
    511   memset (&startup, 0, sizeof (STARTUPINFOA));
    512 
    513   /* TODO: write our own plugin-hosting executable? rundll32, for once, has smaller than usual stack size.
    514    * Also, users might freak out seeing over 9000 rundll32 processes (seeing over 9000 processes named
    515    * "libextractor_plugin_helper" is probably less confusing).
    516    */
    517   snprintf (cmd, MAX_PATH,
    518             "rundll32.exe libextractor-3.dll,RundllEntryPoint@16 %llu %llu",
    519             (unsigned long long) (uintptr_t) p10_os_inh,
    520             (unsigned long long) (uintptr_t) p21_os_inh);
    521   cmd[MAX_PATH] = '\0';
    522   startup.cb = sizeof (STARTUPINFOA);
    523   if (CreateProcessA (NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL,
    524                       &startup, &proc))
    525   {
    526     channel->hProcess = proc.hProcess;
    527     ResumeThread (proc.hThread);
    528     CloseHandle (proc.hThread);
    529   }
    530   else
    531   {
    532     LOG_STRERROR ("CreateProcess");
    533     CloseHandle (p1[0]);
    534     CloseHandle (p1[1]);
    535     CloseHandle (p2[0]);
    536     CloseHandle (p2[1]);
    537     free (channel);
    538     return NULL;
    539   }
    540   CloseHandle (p1[0]);
    541   CloseHandle (p2[1]);
    542   CloseHandle (p10_os_inh);
    543   CloseHandle (p21_os_inh);
    544 
    545   channel->cpipe_in = p1[1];
    546   channel->cpipe_out = p2[0];
    547 
    548   memset (&channel->ov_read, 0, sizeof (OVERLAPPED));
    549   memset (&channel->ov_write, 0, sizeof (OVERLAPPED));
    550 
    551   channel->ov_write_buffer = NULL;
    552 
    553   channel->ov_write.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL);
    554   channel->ov_read.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL);
    555 
    556   if (! write_plugin_data (plugin, channel))
    557   {
    558     LOG_STRERROR ("write_plugin_data");
    559     EXTRACTOR_IPC_channel_destroy_ (channel);
    560     return NULL;
    561   }
    562 
    563   slen = strlen (shm->shm_name) + 1;
    564   if (NULL == (init = malloc (sizeof (struct InitMessage) + slen)))
    565   {
    566     LOG_STRERROR ("malloc");
    567     EXTRACTOR_IPC_channel_destroy_ (channel);
    568     return NULL;
    569   }
    570   init->opcode = MESSAGE_INIT_STATE;
    571   init->reserved = 0;
    572   init->reserved2 = 0;
    573   init->shm_name_length = slen;
    574   init->shm_map_size = shm->shm_size;
    575   memcpy (&init[1], shm->shm_name, slen);
    576   if (sizeof (struct InitMessage) + slen !=
    577       EXTRACTOR_IPC_channel_send_ (channel, init,
    578                                    sizeof (struct InitMessage) + slen))
    579   {
    580     LOG ("Failed to send INIT_STATE message to plugin\n");
    581     EXTRACTOR_IPC_channel_destroy_ (channel);
    582     free (init);
    583     return NULL;
    584   }
    585   free (init);
    586   return channel;
    587 }
    588 
    589 
    590 /**
    591  * Destroy communication channel with a plugin/process.  Also
    592  * destroys the process.
    593  *
    594  * @param channel channel to communicate with the plugin
    595  */
    596 void
    597 EXTRACTOR_IPC_channel_destroy_ (struct EXTRACTOR_Channel *channel)
    598 {
    599   int status;
    600 
    601   CloseHandle (channel->cpipe_out);
    602   CloseHandle (channel->cpipe_in);
    603   CloseHandle (channel->ov_read.hEvent);
    604   CloseHandle (channel->ov_write.hEvent);
    605   if (channel->ov_write_buffer != NULL)
    606   {
    607     free (channel->ov_write_buffer);
    608     channel->ov_write_buffer = NULL;
    609   }
    610   if (NULL != channel->old_buf)
    611   {
    612     free (channel->old_buf);
    613     channel->old_buf = NULL;
    614   }
    615   if (NULL != channel->plugin)
    616     channel->plugin->channel = NULL;
    617   free (channel->mdata);
    618   WaitForSingleObject (channel->hProcess, 1000);
    619   TerminateProcess (channel->hProcess, 0);
    620   CloseHandle (channel->hProcess);
    621   free (channel);
    622 }
    623 
    624 
    625 /**
    626  * Send data via the given IPC channel (blocking).
    627  *
    628  * @param channel channel to communicate with the plugin
    629  * @param buf data to send
    630  * @param size number of bytes in buf to send
    631  * @return -1 on error, number of bytes sent on success
    632  *           (never does partial writes)
    633  */
    634 ssize_t
    635 EXTRACTOR_IPC_channel_send_ (struct EXTRACTOR_Channel *channel,
    636                              const void *data,
    637                              size_t size)
    638 {
    639   DWORD written;
    640   DWORD err;
    641   BOOL bresult;
    642   const char *cdata = data;
    643 
    644   if (WAIT_OBJECT_0 != WaitForSingleObject (channel->ov_write.hEvent, INFINITE))
    645     return -1;
    646 
    647   ResetEvent (channel->ov_write.hEvent);
    648 
    649   if (channel->old_buf != NULL)
    650     free (channel->old_buf);
    651 
    652   channel->old_buf = malloc (size);
    653   if (channel->old_buf == NULL)
    654     return -1;
    655 
    656   memcpy (channel->old_buf, data, size);
    657   written = 0;
    658   channel->ov_write.Offset = 0;
    659   channel->ov_write.OffsetHigh = 0;
    660   channel->ov_write.Pointer = 0;
    661   channel->ov_write.Internal = 0;
    662   channel->ov_write.InternalHigh = 0;
    663   bresult = WriteFile (channel->cpipe_in, channel->old_buf, size, &written,
    664                        &channel->ov_write);
    665 
    666   if (bresult == TRUE)
    667   {
    668     SetEvent (channel->ov_write.hEvent);
    669     free (channel->old_buf);
    670     channel->old_buf = NULL;
    671     return written;
    672   }
    673 
    674   err = GetLastError ();
    675   if (err == ERROR_IO_PENDING)
    676     return size;
    677   SetEvent (channel->ov_write.hEvent);
    678   free (channel->old_buf);
    679   channel->old_buf = NULL;
    680   SetLastError (err);
    681   return -1;
    682 }
    683 
    684 
    685 /**
    686  * Receive data from any of the given IPC channels (blocking).
    687  * Wait for one of the plugins to reply.
    688  * Selects on plugin output pipes, runs 'receive_reply'
    689  * on each activated pipe until it gets a seek request
    690  * or a done message. Called repeatedly by the user until all pipes are dry or
    691  * broken.
    692  *
    693  * @param channels array of channels, channels that break may be set to NULL
    694  * @param num_channels length of the 'channels' array
    695  * @param proc function to call to process messages (may be called
    696  *             more than once)
    697  * @param proc_cls closure for 'proc'
    698  * @return -1 on error, 1 on success
    699  */
    700 int
    701 EXTRACTOR_IPC_channel_recv_ (struct EXTRACTOR_Channel **channels,
    702                              unsigned int num_channels,
    703                              EXTRACTOR_ChannelMessageProcessor proc,
    704                              void *proc_cls)
    705 {
    706   DWORD ms;
    707   DWORD first_ready;
    708   DWORD dwresult;
    709   DWORD bytes_read;
    710   BOOL bresult;
    711   unsigned int i;
    712   unsigned int c;
    713   char *ndata;
    714   HANDLE events[MAXIMUM_WAIT_OBJECTS];
    715   int closed_channel;
    716 
    717   c = 0;
    718   for (i = 0; i < num_channels; i++)
    719   {
    720     if (NULL == channels[i])
    721       continue;
    722     if (MAXIMUM_WAIT_OBJECTS == c)
    723       return -1;
    724     if (WaitForSingleObject (channels[i]->ov_read.hEvent, 0) == WAIT_OBJECT_0)
    725     {
    726       ResetEvent (channels[i]->ov_read.hEvent);
    727       bresult = ReadFile (channels[i]->cpipe_out, &i, 0, &bytes_read,
    728                           &channels[i]->ov_read);
    729       if (bresult == TRUE)
    730       {
    731         SetEvent (channels[i]->ov_read.hEvent);
    732       }
    733       else
    734       {
    735         DWORD err = GetLastError ();
    736         if (err != ERROR_IO_PENDING)
    737           SetEvent (channels[i]->ov_read.hEvent);
    738       }
    739     }
    740     events[c] = channels[i]->ov_read.hEvent;
    741     c++;
    742   }
    743 
    744   if (c == 0)
    745     return 1; /* nothing left to do! */
    746 
    747   ms = 500;
    748   first_ready = WaitForMultipleObjects (c, events, FALSE, ms);
    749   if ((first_ready == WAIT_TIMEOUT) || (first_ready == WAIT_FAILED))
    750   {
    751     /* an error or timeout -> something's wrong or all plugins hung up */
    752     closed_channel = 0;
    753     for (i = 0; i < num_channels; i++)
    754     {
    755       struct EXTRACTOR_Channel *channel = channels[i];
    756       if (NULL == channel)
    757         continue;
    758       if (-1 == channel->plugin->seek_request)
    759       {
    760         /* plugin blocked for too long, kill the channel */
    761         LOG ("Channel blocked, closing channel to %s\n",
    762              channel->plugin->libname);
    763         channel->plugin->channel = NULL;
    764         channel->plugin->round_finished = 1;
    765         EXTRACTOR_IPC_channel_destroy_ (channel);
    766         channels[i] = NULL;
    767         closed_channel = 1;
    768       }
    769     }
    770     if (1 == closed_channel)
    771       return 1;
    772     LOG_STRERROR ("WaitForMultipleObjects");
    773     return -1;
    774   }
    775 
    776   i = 0;
    777   for (i = 0; i < num_channels; i++)
    778   {
    779     if (NULL == channels[i])
    780       continue;
    781     dwresult = WaitForSingleObject (channels[i]->ov_read.hEvent, 0);
    782     if (dwresult == WAIT_OBJECT_0)
    783     {
    784       int ret;
    785 
    786       if (channels[i]->mdata_size == channels[i]->size)
    787       {
    788         /* not enough space, need to grow allocation (if allowed) */
    789         if (MAX_META_DATA == channels[i]->mdata_size)
    790         {
    791           LOG ("Inbound message from channel too large, aborting\n");
    792           EXTRACTOR_IPC_channel_destroy_ (channels[i]);
    793           channels[i] = NULL;
    794           continue;
    795         }
    796         channels[i]->mdata_size *= 2;
    797         if (channels[i]->mdata_size > MAX_META_DATA)
    798           channels[i]->mdata_size = MAX_META_DATA;
    799         if (NULL == (ndata = realloc (channels[i]->mdata,
    800                                       channels[i]->mdata_size)))
    801         {
    802           LOG_STRERROR ("realloc");
    803           EXTRACTOR_IPC_channel_destroy_ (channels[i]);
    804           channels[i] = NULL;
    805           continue;
    806         }
    807         channels[i]->mdata = ndata;
    808       }
    809       bresult = ReadFile (channels[i]->cpipe_out,
    810                           &channels[i]->mdata[channels[i]->size],
    811                           channels[i]->mdata_size - channels[i]->size,
    812                           &bytes_read, NULL);
    813       if (bresult)
    814         ret = EXTRACTOR_IPC_process_reply_ (channels[i]->plugin,
    815                                             channels[i]->mdata,
    816                                             channels[i]->size + bytes_read,
    817                                             proc, proc_cls);
    818       if (! bresult || (-1 == ret))
    819       {
    820         DWORD error = GetLastError ();
    821 
    822         SetErrnoFromWinError (error);
    823         if (! bresult)
    824           LOG_STRERROR ("ReadFile");
    825         EXTRACTOR_IPC_channel_destroy_ (channels[i]);
    826         channels[i] = NULL;
    827         continue;
    828       }
    829       memmove (channels[i]->mdata,
    830                &channels[i]->mdata[ret],
    831                channels[i]->size + bytes_read - ret);
    832       channels[i]->size = channels[i]->size + bytes_read - ret;
    833     }
    834   }
    835   return 1;
    836 }