libextractor

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

extractor_ipc_gnu.c (16140B)


      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_ipc_gnu.c
     22  * @brief IPC with plugin for GNU/POSIX systems
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "extractor.h"
     27 #include "extractor_datasource.h"
     28 #include "extractor_logging.h"
     29 #include "extractor_plugin_main.h"
     30 #include "extractor_plugins.h"
     31 #include "extractor_ipc.h"
     32 #include <dirent.h>
     33 #include <sys/types.h>
     34 #include <sys/wait.h>
     35 #include <sys/shm.h>
     36 #include <signal.h>
     37 #if HAVE_SYS_APPARMOR_H
     38 #include <sys/apparmor.h>
     39 #endif
     40 
     41 /**
     42  * A shared memory resource (often shared with several
     43  * other processes).
     44  */
     45 struct EXTRACTOR_SharedMemory
     46 {
     47   /**
     48    * Pointer to the mapped region of the shm (covers the whole shm)
     49    */
     50   void *shm_ptr;
     51 
     52   /**
     53    * Allocated size of the shm
     54    */
     55   size_t shm_size;
     56 
     57   /**
     58    * POSIX id of the shm into which data is uncompressed
     59    */
     60   int shm_id;
     61 
     62   /**
     63    * Name of the shm
     64    */
     65   char shm_name[MAX_SHM_NAME + 1];
     66 
     67   /**
     68    * Reference counter describing how many references share this SHM.
     69    */
     70   unsigned int rc;
     71 
     72 };
     73 
     74 
     75 /**
     76  * Definition of an IPC communication channel with
     77  * some plugin.
     78  */
     79 struct EXTRACTOR_Channel
     80 {
     81 
     82   /**
     83    * Buffer for reading data from the plugin.
     84    */
     85   char *mdata;
     86 
     87   /**
     88    * Size of the @e mdata buffer.
     89    */
     90   size_t mdata_size;
     91 
     92   /**
     93    * Memory segment shared with this process.
     94    */
     95   struct EXTRACTOR_SharedMemory *shm;
     96 
     97   /**
     98    * The plugin this channel is to communicate with.
     99    */
    100   struct EXTRACTOR_PluginList *plugin;
    101 
    102   /**
    103    * Pipe used to communicate information to the plugin child process.
    104    * NULL if not initialized.
    105    */
    106   int cpipe_in;
    107 
    108   /**
    109    * Number of valid bytes in the channel's buffer.
    110    */
    111   size_t size;
    112 
    113   /**
    114    * Pipe used to read information about extracted meta data from
    115    * the plugin child process.  -1 if not initialized.
    116    */
    117   int cpipe_out;
    118 
    119   /**
    120    * Process ID of the child process for this plugin. 0 for none.
    121    */
    122   pid_t cpid;
    123 
    124 };
    125 
    126 
    127 /**
    128  * Create a shared memory area.
    129  *
    130  * @param size size of the shared area
    131  * @return NULL on error
    132  */
    133 struct EXTRACTOR_SharedMemory *
    134 EXTRACTOR_IPC_shared_memory_create_ (size_t size)
    135 {
    136   struct EXTRACTOR_SharedMemory *shm;
    137 
    138   if (NULL == (shm = malloc (sizeof (struct EXTRACTOR_SharedMemory))))
    139   {
    140     LOG_STRERROR ("malloc");
    141     return NULL;
    142   }
    143   /* POSIX only guarantees portability for a shared memory object name
    144      that consists of an initial slash followed by one or more
    145      characters, none of which is a slash.  The name is not a file
    146      system path: NetBSD keeps POSIX shared memory in a tmpfs of its
    147      own on /var/shm and rejects any name with an embedded slash with
    148      EINVAL, so prefixing a directory such as /tmp made every call to
    149      shm_open() fail there. */
    150   snprintf (shm->shm_name,
    151             MAX_SHM_NAME,
    152             "/LE-%u-%u",
    153             getpid (),
    154             (unsigned int) random ());
    155   if (-1 == (shm->shm_id = shm_open (shm->shm_name,
    156                                      O_RDWR | O_CREAT,
    157                                      S_IRUSR | S_IWUSR)))
    158   {
    159     LOG_STRERROR_FILE ("shm_open",
    160                        shm->shm_name);
    161     free (shm);
    162     return NULL;
    163   }
    164   if ( (0 != ftruncate (shm->shm_id, size)) ||
    165        (NULL == (shm->shm_ptr = mmap (NULL,
    166                                       size,
    167                                       PROT_WRITE,
    168                                       MAP_SHARED,
    169                                       shm->shm_id,
    170                                       0))) ||
    171        (((void*) -1) == shm->shm_ptr) )
    172   {
    173     LOG_STRERROR ("ftruncate/mmap");
    174     (void) close (shm->shm_id);
    175     (void) shm_unlink (shm->shm_name);
    176     free (shm);
    177     return NULL;
    178   }
    179   shm->shm_size = size;
    180   shm->rc = 0;
    181   return shm;
    182 }
    183 
    184 
    185 /**
    186  * Change the reference counter for this shm instance.
    187  *
    188  * @param shm instance to update
    189  * @param delta value to change RC by
    190  * @return new RC
    191  */
    192 unsigned int
    193 EXTRACTOR_IPC_shared_memory_change_rc_ (struct EXTRACTOR_SharedMemory *shm,
    194                                         int delta)
    195 {
    196   shm->rc += delta;
    197   return shm->rc;
    198 }
    199 
    200 
    201 /**
    202  * Destroy shared memory area.
    203  *
    204  * @param shm memory area to destroy
    205  * @return NULL on error
    206  */
    207 void
    208 EXTRACTOR_IPC_shared_memory_destroy_ (struct EXTRACTOR_SharedMemory *shm)
    209 {
    210   munmap (shm->shm_ptr,
    211           shm->shm_size);
    212   (void) close (shm->shm_id);
    213   (void) shm_unlink (shm->shm_name);
    214   free (shm);
    215 }
    216 
    217 
    218 /**
    219  * Initialize shared memory area from data source.
    220  *
    221  * @param shm memory area to initialize
    222  * @param ds data source to use for initialization
    223  * @param off offset to use in data source
    224  * @param size number of bytes to copy
    225  * @return -1 on error, otherwise number of bytes copied
    226  */
    227 ssize_t
    228 EXTRACTOR_IPC_shared_memory_set_ (struct EXTRACTOR_SharedMemory *shm,
    229                                   struct EXTRACTOR_Datasource *ds,
    230                                   uint64_t off,
    231                                   size_t size)
    232 {
    233   if (-1 ==
    234       EXTRACTOR_datasource_seek_ (ds,
    235                                   off,
    236                                   SEEK_SET))
    237   {
    238     LOG ("Failed to set IPC memory due to seek error\n");
    239     return -1;
    240   }
    241   if (size > shm->shm_size)
    242     size = shm->shm_size;
    243   return EXTRACTOR_datasource_read_ (ds,
    244                                      shm->shm_ptr,
    245                                      size);
    246 }
    247 
    248 
    249 /**
    250  * Query datasource for current position
    251  *
    252  * @param ds data source to query
    253  * @return current position in the datasource or UINT64_MAX on error
    254  */
    255 uint64_t
    256 EXTRACTOR_datasource_get_pos_ (struct EXTRACTOR_Datasource *ds)
    257 {
    258   int64_t pos = EXTRACTOR_datasource_seek_ (ds,
    259                                             0,
    260                                             SEEK_CUR);
    261 
    262   if (-1 == pos)
    263     return UINT64_MAX;
    264   return pos;
    265 }
    266 
    267 
    268 /**
    269  * Create a channel to communicate with a process wrapping
    270  * the plugin of the given name.  Starts the process as well.
    271  *
    272  * @param plugin the plugin
    273  * @param shm memory to share with the process
    274  * @return NULL on error, otherwise IPC channel
    275  */
    276 struct EXTRACTOR_Channel *
    277 EXTRACTOR_IPC_channel_create_ (struct EXTRACTOR_PluginList *plugin,
    278                                struct EXTRACTOR_SharedMemory *shm)
    279 {
    280   struct EXTRACTOR_Channel *channel;
    281   int p1[2];
    282   int p2[2];
    283   pid_t pid;
    284   struct InitMessage *init;
    285   size_t slen;
    286   static int sigpipe_ignored;
    287 
    288   if (! sigpipe_ignored)
    289   {
    290     /* The cpipe_in end may be written to after the plugin child
    291        has died; without ignoring SIGPIPE this would terminate the
    292        host application.  As the channel uses pipes (not sockets),
    293        MSG_NOSIGNAL is not applicable, so we ignore SIGPIPE here. */
    294     signal (SIGPIPE, SIG_IGN);
    295     sigpipe_ignored = 1;
    296   }
    297   if (NULL == (channel = malloc (sizeof (struct EXTRACTOR_Channel))))
    298   {
    299     LOG_STRERROR ("malloc");
    300     return NULL;
    301   }
    302   channel->mdata_size = 1024;
    303   if (NULL == (channel->mdata = malloc (channel->mdata_size)))
    304   {
    305     LOG_STRERROR ("malloc");
    306     free (channel);
    307     return NULL;
    308   }
    309   channel->shm = shm;
    310   channel->plugin = plugin;
    311   channel->size = 0;
    312   if (0 != pipe (p1))
    313   {
    314     LOG_STRERROR ("pipe");
    315     free (channel->mdata);
    316     free (channel);
    317     return NULL;
    318   }
    319   if (0 != pipe (p2))
    320   {
    321     LOG_STRERROR ("pipe");
    322     (void) close (p1[0]);
    323     (void) close (p1[1]);
    324     free (channel->mdata);
    325     free (channel);
    326     return NULL;
    327   }
    328   pid = fork ();
    329   if (pid == -1)
    330   {
    331     LOG_STRERROR ("fork");
    332     (void) close (p1[0]);
    333     (void) close (p1[1]);
    334     (void) close (p2[0]);
    335     (void) close (p2[1]);
    336     free (channel->mdata);
    337     free (channel);
    338     return NULL;
    339   }
    340   if (0 == pid)
    341   {
    342     (void) close (p1[1]);
    343     (void) close (p2[0]);
    344     free (channel->mdata);
    345     free (channel);
    346 #if HAVE_SYS_APPARMOR_H
    347 #if HAVE_APPARMOR
    348     if (0 > aa_change_profile ("libextractor"))
    349     {
    350       int eno = errno;
    351 
    352       if ( (EINVAL != eno) &&
    353            (ENOENT != eno) )
    354       {
    355         fprintf (stderr,
    356                  "Failure changing AppArmor profile: %s\n",
    357                  strerror (errno));
    358         _exit (1);
    359       }
    360     }
    361 #endif
    362 #endif
    363     EXTRACTOR_plugin_main_ (plugin, p1[0], p2[1]);
    364     _exit (0);
    365   }
    366   (void) close (p1[0]);
    367   (void) close (p2[1]);
    368   channel->cpipe_in = p1[1];
    369   channel->cpipe_out = p2[0];
    370   channel->cpid = pid;
    371   slen = strlen (shm->shm_name) + 1;
    372   if (NULL == (init = malloc (sizeof (struct InitMessage) + slen)))
    373   {
    374     LOG_STRERROR ("malloc");
    375     EXTRACTOR_IPC_channel_destroy_ (channel);
    376     return NULL;
    377   }
    378   init->opcode = MESSAGE_INIT_STATE;
    379   init->reserved = 0;
    380   init->reserved2 = 0;
    381   init->shm_name_length = slen;
    382   init->shm_map_size = shm->shm_size;
    383   memcpy (&init[1], shm->shm_name, slen);
    384   if (sizeof (struct InitMessage) + slen !=
    385       EXTRACTOR_IPC_channel_send_ (channel,
    386                                    init,
    387                                    sizeof (struct InitMessage) + slen) )
    388   {
    389     LOG ("Failed to send INIT_STATE message to plugin\n");
    390     EXTRACTOR_IPC_channel_destroy_ (channel);
    391     free (init);
    392     return NULL;
    393   }
    394   free (init);
    395   return channel;
    396 }
    397 
    398 
    399 /**
    400  * Destroy communication channel with a plugin/process.  Also
    401  * destroys the process.
    402  *
    403  * @param channel channel to communicate with the plugin
    404  */
    405 void
    406 EXTRACTOR_IPC_channel_destroy_ (struct EXTRACTOR_Channel *channel)
    407 {
    408   int status;
    409 
    410   if (0 != kill (channel->cpid, SIGKILL))
    411     LOG_STRERROR ("kill");
    412   if (-1 == waitpid (channel->cpid, &status, 0))
    413     LOG_STRERROR ("waitpid");
    414   if (0 != close (channel->cpipe_out))
    415     LOG_STRERROR ("close");
    416   if (0 != close (channel->cpipe_in))
    417     LOG_STRERROR ("close");
    418   if (NULL != channel->plugin)
    419     channel->plugin->channel = NULL;
    420   free (channel->mdata);
    421   free (channel);
    422 }
    423 
    424 
    425 /**
    426  * Send data via the given IPC channel (blocking).
    427  *
    428  * @param channel channel to communicate with the plugin
    429  * @param buf data to send
    430  * @param size number of bytes in buf to send
    431  * @return -1 on error, number of bytes sent on success
    432  *           (never does partial writes)
    433  */
    434 ssize_t
    435 EXTRACTOR_IPC_channel_send_ (struct EXTRACTOR_Channel *channel,
    436                              const void *data,
    437                              size_t size)
    438 {
    439   const char *cdata = data;
    440   size_t off = 0;
    441   ssize_t ret;
    442 
    443   while (off < size)
    444   {
    445     ret = write (channel->cpipe_in, &cdata[off], size - off);
    446     if (ret <= 0)
    447     {
    448       if ((-1 == ret) && (EINTR == errno))
    449         continue;
    450       if (-1 == ret)
    451         LOG_STRERROR ("write");
    452       return -1;
    453     }
    454     off += ret;
    455   }
    456   return size;
    457 }
    458 
    459 
    460 /**
    461  * Receive data from any of the given IPC channels (blocking).
    462  * Wait for one of the plugins to reply.
    463  * Selects on plugin output pipes, runs 'receive_reply'
    464  * on each activated pipe until it gets a seek request
    465  * or a done message. Called repeatedly by the user until all pipes are dry or
    466  * broken.
    467  *
    468  * @param channels array of channels, channels that break may be set to NULL
    469  * @param num_channels length of the @a channels array
    470  * @param proc function to call to process messages (may be called
    471  *             more than once)
    472  * @param proc_cls closure for @a proc
    473  * @return -1 on error, 1 on success
    474  */
    475 int
    476 EXTRACTOR_IPC_channel_recv_ (struct EXTRACTOR_Channel **channels,
    477                              unsigned int num_channels,
    478                              EXTRACTOR_ChannelMessageProcessor proc,
    479                              void *proc_cls)
    480 {
    481   struct timeval tv;
    482   fd_set to_check;
    483   int max;
    484   unsigned int i;
    485   struct EXTRACTOR_Channel *channel;
    486   ssize_t ret;
    487   ssize_t iret;
    488   char *ndata;
    489   int closed_channel;
    490 
    491   FD_ZERO (&to_check);
    492   max = -1;
    493   for (i = 0; i<num_channels; i++)
    494   {
    495     channel = channels[i];
    496     if (NULL == channel)
    497       continue;
    498     FD_SET (channel->cpipe_out, &to_check);
    499     if (max < channel->cpipe_out)
    500       max = channel->cpipe_out;
    501   }
    502   if (-1 == max)
    503   {
    504     return 1;   /* nothing left to do! */
    505   }
    506   tv.tv_sec = 0;
    507   tv.tv_usec = 500000; /* 500 ms */
    508   if (0 >= select (max + 1, &to_check, NULL, NULL, &tv))
    509   {
    510     /* an error or timeout -> something's wrong or all plugins hung up */
    511     closed_channel = 0;
    512     for (i = 0; i<num_channels; i++)
    513     {
    514       channel = channels[i];
    515       if (NULL == channel)
    516         continue;
    517       if (-1 == channel->plugin->seek_request)
    518       {
    519         /* plugin blocked for too long, kill channel */
    520         LOG ("Channel blocked, closing channel to %s\n",
    521              channel->plugin->libname);
    522         channel->plugin->channel = NULL;
    523         channel->plugin->round_finished = 1;
    524         EXTRACTOR_IPC_channel_destroy_ (channel);
    525         channels[i] = NULL;
    526         closed_channel = 1;
    527       }
    528     }
    529     if (1 == closed_channel)
    530       return 1;
    531     /* strange, no channel is to blame, let's die just to be safe */
    532     if ((EINTR != errno) && (0 != errno))
    533       LOG_STRERROR ("select");
    534     return -1;
    535   }
    536   for (i = 0; i<num_channels; i++)
    537   {
    538     channel = channels[i];
    539     if (NULL == channel)
    540       continue;
    541     if (! FD_ISSET (channel->cpipe_out, &to_check))
    542       continue;
    543     if (channel->mdata_size == channel->size)
    544     {
    545       /* not enough space, need to grow allocation (if allowed) */
    546       if (MAX_META_DATA == channel->mdata_size)
    547       {
    548         LOG ("Inbound message from channel too large, aborting\n");
    549         channel->plugin->channel = NULL;
    550         channel->plugin->round_finished = 1;
    551         EXTRACTOR_IPC_channel_destroy_ (channel);
    552         channels[i] = NULL;
    553         continue;
    554       }
    555       channel->mdata_size *= 2;
    556       if (channel->mdata_size > MAX_META_DATA)
    557         channel->mdata_size = MAX_META_DATA;
    558       if (NULL == (ndata = realloc (channel->mdata,
    559                                     channel->mdata_size)))
    560       {
    561         LOG_STRERROR ("realloc");
    562         channel->plugin->channel = NULL;
    563         channel->plugin->round_finished = 1;
    564         EXTRACTOR_IPC_channel_destroy_ (channel);
    565         channels[i] = NULL;
    566         continue;
    567       }
    568       channel->mdata = ndata;
    569     }
    570     do
    571     {
    572       iret = read (channel->cpipe_out,
    573                    &channel->mdata[channel->size],
    574                    channel->mdata_size - channel->size);
    575     }
    576     while ((-1 == iret) && (EINTR == errno));
    577     if ( (-1 == iret) ||
    578          (0 == iret) ||
    579          (-1 == (ret = EXTRACTOR_IPC_process_reply_ (channel->plugin,
    580                                                      channel->mdata,
    581                                                      channel->size + iret,
    582                                                      proc, proc_cls)) ) )
    583     {
    584       if (-1 == iret)
    585         LOG_STRERROR ("read");
    586       LOG ("Read error from channel, closing channel %s\n",
    587            channel->plugin->libname);
    588       /* The plugin is gone -- a crash in a plugin is exactly what this
    589          library is built to survive.  Forget the channel on the plugin
    590          as well, or the next file would be handed to a
    591          `struct EXTRACTOR_Channel' that has already been freed. */
    592       channel->plugin->channel = NULL;
    593       channel->plugin->round_finished = 1;
    594       EXTRACTOR_IPC_channel_destroy_ (channel);
    595       channels[i] = NULL;
    596       continue;
    597     }
    598     else
    599     {
    600       channel->size = channel->size + iret - ret;
    601       memmove (channel->mdata,
    602                &channel->mdata[ret],
    603                channel->size);
    604     }
    605   }
    606   return 1;
    607 }
    608 
    609 
    610 /* end of extractor_ipc_gnu.c */