libextractor

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

extractor_plugin_main.c (20877B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 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  * @file main/extractor_plugin_main.c
     22  * @brief main loop for an out-of-process plugin
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "extractor.h"
     27 #include "extractor_common.h"
     28 #include "extractor_datasource.h"
     29 #include "extractor_plugins.h"
     30 #include "extractor_ipc.h"
     31 #include "extractor_logging.h"
     32 #include "extractor_plugin_main.h"
     33 #include <dirent.h>
     34 #include <sys/types.h>
     35 #if GNU_LINUX
     36 #include <sys/wait.h>
     37 #include <sys/shm.h>
     38 #include <signal.h>
     39 #endif
     40 
     41 #if WINDOWS
     42 #define SHM_ID HANDLE
     43 #define INVALID_SHM_ID NULL
     44 #else
     45 #define SHM_ID int
     46 #define INVALID_SHM_ID -1
     47 #endif
     48 
     49 /**
     50  * Closure we use for processing requests inside the helper process.
     51  */
     52 struct ProcessingContext
     53 {
     54   /**
     55    * Our plugin handle.
     56    */
     57   struct EXTRACTOR_PluginList *plugin;
     58 
     59   /**
     60    * Shared memory area.
     61    */
     62   void *shm;
     63 
     64   /**
     65    * Overall size of the file.
     66    */
     67   uint64_t file_size;
     68 
     69   /**
     70    * Current read offset when reading from the SHM.
     71    */
     72   uint64_t read_position;
     73 
     74   /**
     75    * Current offset of the SHM in the file.
     76    */
     77   uint64_t shm_off;
     78 
     79   /**
     80    * Handle to the shared memory.
     81    */
     82   SHM_ID shm_id;
     83 
     84   /**
     85    * Size of the shared memory map.
     86    */
     87   uint32_t shm_map_size;
     88 
     89   /**
     90    * Number of bytes ready in SHM.
     91    */
     92   uint32_t shm_ready_bytes;
     93 
     94   /**
     95    * Input stream.
     96    */
     97   int in;
     98 
     99   /**
    100    * Output stream.
    101    */
    102   int out;
    103 
    104   /**
    105    * When we last sent a #MESSAGE_ALIVE, in milliseconds.
    106    */
    107   uint64_t last_alive;
    108 };
    109 
    110 
    111 /**
    112  * Minimum time between two #MESSAGE_ALIVE, in milliseconds; well below
    113  * the silence LE tolerates (500 ms).
    114  */
    115 #define ALIVE_INTERVAL_MS 100
    116 
    117 
    118 /**
    119  * Get a monotonic time stamp.
    120  *
    121  * @return current time in milliseconds since some arbitrary epoch
    122  */
    123 static uint64_t
    124 now_ms (void)
    125 {
    126 #if WINDOWS
    127   return (uint64_t) GetTickCount64 ();
    128 #else
    129   struct timespec ts;
    130 
    131   if (0 != clock_gettime (CLOCK_MONOTONIC,
    132                           &ts))
    133     return 0;
    134   return ((uint64_t) ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
    135 #endif
    136 }
    137 
    138 
    139 /**
    140  * Tell LE that we are still busy, unless we did so very recently.
    141  *
    142  * @param pc processing context
    143  */
    144 static void
    145 send_alive (struct ProcessingContext *pc)
    146 {
    147   unsigned char alive = MESSAGE_ALIVE;
    148   uint64_t now = now_ms ();
    149 
    150   if (now - pc->last_alive < ALIVE_INTERVAL_MS)
    151     return;
    152   pc->last_alive = now;
    153   if (-1 == EXTRACTOR_write_all_ (pc->out,
    154                                   &alive,
    155                                   sizeof (alive)))
    156     LOG ("Failed to send MESSAGE_ALIVE\n");
    157 }
    158 
    159 
    160 /**
    161  * Moves current absolute buffer position to 'pos' in 'whence' mode.
    162  * Will move logical position without shifting the buffer, if possible.
    163  * Will not move beyond the end of file.
    164  *
    165  * @param plugin plugin context
    166  * @param pos position to move to
    167  * @param whence seek mode (SEEK_CUR, SEEK_SET, SEEK_END)
    168  * @return new absolute position, -1 on error
    169  */
    170 static int64_t
    171 plugin_env_seek (void *cls,
    172                  int64_t pos,
    173                  int whence)
    174 {
    175   struct ProcessingContext *pc = cls;
    176   struct SeekRequestMessage srm = {
    177     .opcode = MESSAGE_SEEK
    178   };
    179   struct UpdateMessage um;
    180   uint64_t npos;
    181   unsigned char reply;
    182   uint16_t wval;
    183 
    184   if ( (SEEK_CUR == whence) &&
    185        (0 == pos) )
    186   {
    187     /* a no-op seek is how a busy plugin keeps its channel open */
    188     send_alive (pc);
    189     return (int64_t) pc->read_position;
    190   }
    191   switch (whence)
    192   {
    193   case SEEK_CUR:
    194     if ( (pos < 0) && (pc->read_position < -pos) )
    195     {
    196       LOG ("Invalid seek operation\n");
    197       return -1;
    198     }
    199     if ((pos > 0) && ((pc->read_position + pos < pc->read_position) ||
    200                       (pc->read_position + pos > pc->file_size)))
    201     {
    202       LOG ("Invalid seek operation\n");
    203       return -1;
    204     }
    205     npos = (uint64_t) (pc->read_position + pos);
    206     wval = 0;
    207     break;
    208   case SEEK_END:
    209     if (pos > 0)
    210     {
    211       LOG ("Invalid seek operation\n");
    212       return -1;
    213     }
    214     if (UINT64_MAX == pc->file_size)
    215     {
    216       wval = 2;
    217       npos = (uint64_t) -pos;
    218       break;
    219     }
    220     pos = (int64_t) (pc->file_size + pos);
    221   /* fall-through! */
    222   case SEEK_SET:
    223     if ( (pos < 0) || (pc->file_size < pos) )
    224     {
    225       LOG ("Invalid seek operation\n");
    226       return -1;
    227     }
    228     npos = (uint64_t) pos;
    229     wval = 0;
    230     break;
    231   default:
    232     LOG ("Invalid seek operation\n");
    233     return -1;
    234   }
    235   if ( (pc->shm_off <= npos) &&
    236        (pc->shm_off + pc->shm_ready_bytes > npos) &&
    237        (0 == wval) )
    238   {
    239     pc->read_position = npos;
    240     return (int64_t) npos;
    241   }
    242   /* need to seek */
    243   srm.opcode = MESSAGE_SEEK;
    244   srm.reserved = 0;
    245   srm.whence = wval;
    246   srm.requested_bytes = pc->shm_map_size;
    247   if (0 == wval)
    248   {
    249     if (srm.requested_bytes > pc->file_size - npos)
    250       srm.requested_bytes = pc->file_size - npos;
    251   }
    252   else
    253   {
    254     srm.requested_bytes = npos;
    255   }
    256   srm.file_offset = npos;
    257   if (-1 == EXTRACTOR_write_all_ (pc->out, &srm, sizeof (srm)))
    258   {
    259     LOG ("Failed to send MESSAGE_SEEK\n");
    260     return -1;
    261   }
    262   if (-1 ==
    263       EXTRACTOR_read_all_ (pc->in,
    264                            &reply, sizeof (reply)))
    265   {
    266     LOG ("Plugin `%s' failed to read response to MESSAGE_SEEK\n",
    267          pc->plugin->short_libname);
    268     return -1;
    269   }
    270   if (MESSAGE_UPDATED_SHM != reply)
    271   {
    272     LOG ("Unexpected reply %d to seek\n", reply);
    273     return -1;   /* was likely a MESSAGE_DISCARD_STATE */
    274   }
    275   if (-1 == EXTRACTOR_read_all_ (pc->in, &um.reserved, sizeof (um) - 1))
    276   {
    277     LOG ("Failed to read MESSAGE_UPDATED_SHM\n");
    278     return -1;
    279   }
    280   pc->shm_off = um.shm_off;
    281   pc->shm_ready_bytes = um.shm_ready_bytes;
    282   pc->file_size = um.file_size;
    283   if (2 == wval)
    284   {
    285     /* convert offset to be absolute from beginning of the file */
    286     npos = pc->file_size - npos;
    287   }
    288   if ( (pc->shm_off <= npos) &&
    289        ((pc->shm_off + pc->shm_ready_bytes > npos) ||
    290         (pc->file_size == pc->shm_off)) )
    291   {
    292     pc->read_position = npos;
    293     return (int64_t) npos;
    294   }
    295   /* oops, serious missunderstanding, we asked to seek
    296      and then were notified about a different position!? */
    297   LOG (
    298     "Plugin `%s' got invalid MESSAGE_UPDATED_SHM in response to my %d-seek (%llu not in %llu-%llu)\n",
    299     pc->plugin->short_libname,
    300     (int) wval,
    301     (unsigned long long) npos,
    302     (unsigned long long) pc->shm_off,
    303     (unsigned long long) pc->shm_off + pc->shm_ready_bytes);
    304   return -1;
    305 }
    306 
    307 
    308 /**
    309  * Fills @a data with a pointer to the data buffer.
    310  *
    311  * @param plugin plugin context
    312  * @param data location to store data pointer
    313  * @param count number of bytes to read
    314  * @return number of bytes (<= count) available in @a data, -1 on error
    315  */
    316 static ssize_t
    317 plugin_env_read (void *cls,
    318                  void **data,
    319                  size_t count)
    320 {
    321   struct ProcessingContext *pc = cls;
    322   unsigned char *dp;
    323 
    324   *data = NULL;
    325   if ( (count + pc->read_position > pc->file_size) ||
    326        (count + pc->read_position < pc->read_position) )
    327     count = pc->file_size - pc->read_position;
    328   if ( ( ( (pc->read_position >= pc->shm_off + pc->shm_ready_bytes) &&
    329            (pc->read_position < pc->file_size)) ||
    330          (pc->read_position < pc->shm_off) ) &&
    331        (-1 == plugin_env_seek (pc, pc->read_position, SEEK_SET) ) )
    332   {
    333     LOG ("Failed to seek to satisfy read\n");
    334     return -1;
    335   }
    336   if (pc->read_position + count > pc->shm_off + pc->shm_ready_bytes)
    337     count = pc->shm_off + pc->shm_ready_bytes - pc->read_position;
    338   dp = pc->shm;
    339   *data = &dp[pc->read_position - pc->shm_off];
    340   pc->read_position += count;
    341   return count;
    342 }
    343 
    344 
    345 /**
    346  * Provide the overall file size to plugins.
    347  *
    348  * @param cls the 'struct ProcessingContext'
    349  * @return overall file size of the current file
    350  */
    351 static uint64_t
    352 plugin_env_get_size (void *cls)
    353 {
    354   struct ProcessingContext *pc = cls;
    355 
    356   return pc->file_size;
    357 }
    358 
    359 
    360 /**
    361  * Function called by a plugin in a child process.  Transmits
    362  * the meta data back to the parent process.
    363  *
    364  * @param cls closure, "struct ProcessingContext" with the FD for transmission
    365  * @param plugin_name name of the plugin that produced this value;
    366  *        special values can be used (i.e. '<zlib>' for zlib being
    367  *        used in the main libextractor library and yielding
    368  *        meta data).
    369  * @param type libextractor-type describing the meta data
    370  * @param format basic format information about data
    371  * @param data_mime_type mime-type of data (not of the original file);
    372  *        can be NULL (if mime-type is not known)
    373  * @param data actual meta-data found
    374  * @param data_len number of bytes in data
    375  * @return 0 to continue extracting, 1 to abort (transmission error)
    376  */
    377 static int
    378 plugin_env_send_proc (void *cls,
    379                       const char *plugin_name,
    380                       enum EXTRACTOR_MetaType type,
    381                       enum EXTRACTOR_MetaFormat format,
    382                       const char *data_mime_type,
    383                       const char *data,
    384                       size_t data_len)
    385 {
    386   struct ProcessingContext *pc = cls;
    387   struct MetaMessage mm;
    388   size_t mime_len;
    389   unsigned char reply;
    390 
    391   if (data_len > MAX_META_DATA)
    392     return 0; /* skip, too large */
    393   if (NULL == data_mime_type)
    394     mime_len = 0;
    395   else
    396     mime_len = strlen (data_mime_type) + 1;
    397   if (mime_len > UINT16_MAX)
    398     mime_len = UINT16_MAX;
    399   mm.opcode = MESSAGE_META;
    400   mm.reserved = 0;
    401   mm.meta_type = type;
    402   mm.meta_format = (uint16_t) format;
    403   mm.mime_length = (uint16_t) mime_len;
    404   mm.value_size = (uint32_t) data_len;
    405   if ( (sizeof (mm) !=
    406         EXTRACTOR_write_all_ (pc->out,
    407                               &mm, sizeof (mm))) ||
    408        (mime_len !=
    409         EXTRACTOR_write_all_ (pc->out,
    410                               data_mime_type, mime_len)) ||
    411        (data_len !=
    412         EXTRACTOR_write_all_ (pc->out,
    413                               data, data_len)) )
    414   {
    415     LOG ("Failed to send meta message\n");
    416     return 1;
    417   }
    418   if (-1 ==
    419       EXTRACTOR_read_all_ (pc->in,
    420                            &reply, sizeof (reply)))
    421   {
    422     LOG ("Failed to read response to meta message\n");
    423     return 1;
    424   }
    425   if (MESSAGE_DISCARD_STATE == reply)
    426     return 1;
    427   if (MESSAGE_CONTINUE_EXTRACTING != reply)
    428   {
    429     LOG ("Received unexpected reply to meta data: %d\n", reply);
    430     return 1;
    431   }
    432   return 0;
    433 }
    434 
    435 
    436 /**
    437  * Handle an init message.  The opcode itself has already been read.
    438  *
    439  * @param pc processing context
    440  * @return 0 on success, -1 on error
    441  */
    442 static int
    443 handle_init_message (struct ProcessingContext *pc)
    444 {
    445   struct InitMessage init = {
    446     .opcode = MESSAGE_INIT_STATE
    447   };
    448 
    449   if (NULL != pc->shm)
    450   {
    451     LOG ("Cannot handle 'init' message, have already been initialized\n");
    452     return -1;
    453   }
    454   if (sizeof (struct InitMessage) - 1
    455       != EXTRACTOR_read_all_ (pc->in,
    456                               &init.reserved,
    457                               sizeof (struct InitMessage) - 1))
    458   {
    459     LOG ("Failed to read 'init' message\n");
    460     return -1;
    461   }
    462   if (init.shm_name_length > MAX_SHM_NAME)
    463   {
    464     LOG ("Invalid 'init' message\n");
    465     return -1;
    466   }
    467   {
    468     char shm_name[init.shm_name_length + 1];
    469 
    470     if (init.shm_name_length
    471         != EXTRACTOR_read_all_ (pc->in,
    472                                 shm_name,
    473                                 init.shm_name_length))
    474     {
    475       LOG ("Failed to read 'init' message\n");
    476       return -1;
    477     }
    478     shm_name[init.shm_name_length] = '\0';
    479 
    480     pc->shm_map_size = init.shm_map_size;
    481 #if WINDOWS
    482     /* FIXME: storing pointer in an int */
    483     pc->shm_id = OpenFileMapping (FILE_MAP_READ, FALSE, shm_name);
    484     if (NULL == pc->shm_id)
    485       return -1;
    486     pc->shm = MapViewOfFile (pc->shm_id, FILE_MAP_READ, 0, 0, 0);
    487     if (NULL == pc->shm)
    488     {
    489       CloseHandle (pc->shm_id);
    490       return -1;
    491     }
    492 #else
    493     pc->shm_id = shm_open (shm_name, O_RDONLY, 0);
    494     if (-1 == pc->shm_id)
    495     {
    496       LOG_STRERROR_FILE ("open", shm_name);
    497       return -1;
    498     }
    499     pc->shm = mmap (NULL,
    500                     pc->shm_map_size,
    501                     PROT_READ,
    502                     MAP_SHARED,
    503                     pc->shm_id, 0);
    504     if ( ((void*) -1) == pc->shm)
    505     {
    506       LOG_STRERROR_FILE ("mmap", shm_name);
    507       return -1;
    508     }
    509 #endif
    510   }
    511   return 0;
    512 }
    513 
    514 
    515 /**
    516  * Handle a start message.  The opcode itself has already been read.
    517  *
    518  * @param pc processing context
    519  * @return 0 on success, -1 on error
    520  */
    521 static int
    522 handle_start_message (struct ProcessingContext *pc)
    523 {
    524   struct StartMessage start = {
    525     .opcode = MESSAGE_EXTRACT_START
    526   };
    527   struct EXTRACTOR_ExtractContext ec;
    528   char done;
    529 
    530   if (sizeof (struct StartMessage) - 1
    531       != EXTRACTOR_read_all_ (pc->in,
    532                               &start.reserved,
    533                               sizeof (struct StartMessage) - 1))
    534   {
    535     LOG ("Failed to read 'start' message\n");
    536     return -1;
    537   }
    538   pc->shm_ready_bytes = start.shm_ready_bytes;
    539   pc->file_size = start.file_size;
    540   pc->read_position = 0;
    541   pc->shm_off = 0;
    542   ec.cls = pc;
    543   ec.config = pc->plugin->plugin_options;
    544   ec.read = &plugin_env_read;
    545   ec.seek = &plugin_env_seek;
    546   ec.get_size = &plugin_env_get_size;
    547   ec.proc = &plugin_env_send_proc;
    548   pc->plugin->extract_method (&ec);
    549   done = MESSAGE_DONE;
    550   if (-1 == EXTRACTOR_write_all_ (pc->out, &done, sizeof (done)))
    551   {
    552     LOG ("Failed to write 'done' message\n");
    553     return -1;
    554   }
    555   if ( (NULL != pc->plugin->specials) &&
    556        (NULL != strstr (pc->plugin->specials, "force-kill")) )
    557   {
    558     /* we're required to die after each file since this
    559  plugin only supports a single file at a time */
    560 #if ! WINDOWS
    561     fsync (pc->out);
    562 #else
    563     _commit (pc->out);
    564 #endif
    565     _exit (0);
    566   }
    567   return 0;
    568 }
    569 
    570 
    571 /**
    572  * Main loop function for plugins.  Reads a message from the plugin
    573  * input pipe and acts on it.
    574  *
    575  * @param pc processing context
    576  */
    577 static void
    578 process_requests (struct ProcessingContext *pc)
    579 {
    580   while (1)
    581   {
    582     unsigned char code;
    583 
    584     if (1 != EXTRACTOR_read_all_ (pc->in, &code, 1))
    585     {
    586       LOG ("Failed to read next request\n");
    587       break;
    588     }
    589     switch (code)
    590     {
    591     case MESSAGE_INIT_STATE:
    592       if (0 != handle_init_message (pc))
    593       {
    594         LOG ("Failure to handle INIT\n");
    595         return;
    596       }
    597       break;
    598     case MESSAGE_EXTRACT_START:
    599       if (0 != handle_start_message (pc))
    600       {
    601         LOG ("Failure to handle START\n");
    602         return;
    603       }
    604       break;
    605     case MESSAGE_UPDATED_SHM:
    606       LOG ("Illegal message\n");
    607       /* not allowed here, we're not waiting for SHM to move! */
    608       return;
    609     case MESSAGE_DISCARD_STATE:
    610       /* odd, we're already in the start state... */
    611       continue;
    612     default:
    613       LOG ("Received invalid message %d\n", (int) code);
    614       /* error, unexpected message */
    615       return;
    616     }
    617   }
    618 }
    619 
    620 
    621 /**
    622  * Open '/dev/null' and make the result the given
    623  * file descriptor.
    624  *
    625  * @param target_fd desired FD to point to /dev/null
    626  * @param flags open flags (O_RDONLY, O_WRONLY)
    627  */
    628 static void
    629 open_dev_null (int target_fd,
    630                int flags)
    631 {
    632   int fd;
    633 
    634 #ifndef WINDOWS
    635   fd = open ("/dev/null", flags);
    636 #else
    637   fd = open ("\\\\?\\NUL", flags);
    638 #endif
    639   if (-1 == fd)
    640   {
    641     LOG_STRERROR_FILE ("open", "/dev/null");
    642     return;   /* good luck */
    643   }
    644   if (fd == target_fd)
    645     return; /* already done */
    646   if (-1 == dup2 (fd, target_fd))
    647   {
    648     LOG_STRERROR ("dup2");
    649     (void) close (fd);
    650     return; /* good luck */
    651   }
    652   /* close original result from 'open' */
    653   if (0 != close (fd))
    654     LOG_STRERROR ("close");
    655 }
    656 
    657 
    658 /**
    659  * 'main' function of the child process. Loads the plugin,
    660  * sets up its in and out pipes, then runs the request serving function.
    661  *
    662  * @param plugin extractor plugin to use
    663  * @param in stream to read from
    664  * @param out stream to write to
    665  */
    666 void
    667 EXTRACTOR_plugin_main_ (struct EXTRACTOR_PluginList *plugin,
    668                         int in, int out)
    669 {
    670   struct ProcessingContext pc;
    671 
    672   if (0 != EXTRACTOR_plugin_load_ (plugin))
    673   {
    674 #if DEBUG
    675     fprintf (stderr, "Plugin `%s' failed to load!\n",
    676              plugin->short_libname);
    677 #endif
    678     return;
    679   }
    680   if ( (NULL != plugin->specials) &&
    681        (NULL != strstr (plugin->specials, "close-stderr")))
    682   {
    683     if (0 != close (2))
    684       LOG_STRERROR ("close");
    685     open_dev_null (2, O_WRONLY);
    686   }
    687   if ( (NULL != plugin->specials) &&
    688        (NULL != strstr (plugin->specials, "close-stdout")))
    689   {
    690     if (0 != close (1))
    691       LOG_STRERROR ("close");
    692     open_dev_null (1, O_WRONLY);
    693   }
    694   pc.plugin = plugin;
    695   pc.in = in;
    696   pc.out = out;
    697   pc.shm_id = INVALID_SHM_ID;
    698   pc.shm = NULL;
    699   pc.shm_map_size = 0;
    700   process_requests (&pc);
    701   LOG ("IPC error; plugin `%s' terminates!\n",
    702        plugin->short_libname);
    703 #if WINDOWS
    704   if (NULL != pc.shm)
    705     UnmapViewOfFile (pc.shm);
    706   if (NULL != pc.shm_id)
    707     CloseHandle (pc.shm_id);
    708 #else
    709   if ( (NULL != pc.shm) &&
    710        (MAP_FAILED != pc.shm) )
    711     munmap (pc.shm,
    712             pc.shm_map_size);
    713   if (-1 != pc.shm_id)
    714   {
    715     if (0 != close (pc.shm_id))
    716       LOG_STRERROR ("close");
    717   }
    718 #endif
    719 }
    720 
    721 
    722 #if WINDOWS
    723 /**
    724  * Reads plugin data from the LE server process.
    725  * Also initializes allocation granularity (duh...).
    726  *
    727  * @param fd the pipe to read from
    728  * @return newly allocated plugin context
    729  */
    730 static struct EXTRACTOR_PluginList *
    731 read_plugin_data (int fd)
    732 {
    733   struct EXTRACTOR_PluginList *ret;
    734   SYSTEM_INFO si;
    735   size_t i;
    736 
    737   if (NULL == (ret = malloc (sizeof (struct EXTRACTOR_PluginList))))
    738   {
    739     LOG_STRERROR ("malloc");
    740     return NULL;
    741   }
    742   memset (ret,
    743           0,
    744           sizeof (struct EXTRACTOR_PluginList));
    745   /*GetSystemInfo (&si);
    746   ret->allocation_granularity = si.dwAllocationGranularity;*/
    747   if (-1 == EXTRACTOR_read_all_ (fd, &i, sizeof (size_t)))
    748   {
    749     free (ret);
    750     return NULL;
    751   }
    752   if ( (0 == i) ||
    753        (NULL == (ret->libname = malloc (i))) )
    754   {
    755     free (ret);
    756     return NULL;
    757   }
    758   EXTRACTOR_read_all_ (fd, ret->libname, i);
    759   ret->libname[i - 1] = '\0';
    760   if (-1 == EXTRACTOR_read_all_ (fd, &i, sizeof (size_t)))
    761   {
    762     free (ret->libname);
    763     free (ret);
    764     return NULL;
    765   }
    766   if ( (0 == i) ||
    767        (NULL == (ret->short_libname = malloc (i))) )
    768   {
    769     free (ret->libname);
    770     free (ret);
    771     return NULL;
    772   }
    773   if (-1 == EXTRACTOR_read_all_ (fd, ret->short_libname, i))
    774   {
    775     free (ret->short_libname);
    776     free (ret->libname);
    777     free (ret);
    778     return NULL;
    779   }
    780   ret->short_libname[i - 1] = '\0';
    781   if (-1 == EXTRACTOR_read_all_ (fd, &i, sizeof (size_t)))
    782   {
    783     free (ret->short_libname);
    784     free (ret->libname);
    785     free (ret);
    786     return NULL;
    787   }
    788   if (0 == i)
    789   {
    790     /* no options, that's OK, too! */
    791     return ret;
    792   }
    793   if (NULL == (ret->plugin_options = malloc (i)))
    794   {
    795     free (ret->short_libname);
    796     free (ret->libname);
    797     free (ret);
    798     return NULL;
    799   }
    800   if (-1 == EXTRACTOR_read_all_ (fd, ret->plugin_options, i))
    801   {
    802     free (ret->plugin_options);
    803     free (ret->short_libname);
    804     free (ret->libname);
    805     free (ret);
    806     return NULL;
    807   }
    808   ret->plugin_options[i - 1] = '\0';
    809   return ret;
    810 }
    811 
    812 
    813 /**
    814  * FIXME: document.
    815  */
    816 void CALLBACK
    817 RundllEntryPoint (HWND hwnd,
    818                   HINSTANCE hinst,
    819                   LPSTR lpszCmdLine,
    820                   int nCmdShow)
    821 {
    822   struct EXTRACTOR_PluginList *plugin;
    823   intptr_t in_h;
    824   intptr_t out_h;
    825   int in;
    826   int out;
    827 
    828   sscanf (lpszCmdLine, "%lu %lu", &in_h, &out_h);
    829   in = _open_osfhandle (in_h, _O_RDONLY);
    830   out = _open_osfhandle (out_h, 0);
    831   setmode (in, _O_BINARY);
    832   setmode (out, _O_BINARY);
    833   if (NULL == (plugin = read_plugin_data (in)))
    834   {
    835     close (in);
    836     close (out);
    837     return;
    838   }
    839   EXTRACTOR_plugin_main_ (plugin, in, out);
    840   close (in);
    841   close (out);
    842   /* libgobject may crash us hard if we LoadLibrary() it directly or
    843    * indirectly, and then exit normally (causing FreeLibrary() to be
    844    * called by the OS) or call FreeLibrary() on it directly or
    845    * indirectly.
    846    * By terminating here we alleviate that problem.
    847    */TerminateProcess (GetCurrentProcess (), 0);
    848 }
    849 
    850 
    851 /**
    852  * FIXME: document.
    853  */
    854 void CALLBACK
    855 RundllEntryPointA (HWND hwnd,
    856                    HINSTANCE hinst,
    857                    LPSTR lpszCmdLine,
    858                    int nCmdShow)
    859 {
    860   return RundllEntryPoint (hwnd, hinst, lpszCmdLine, nCmdShow);
    861 }
    862 
    863 
    864 #endif