libextractor

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

rpm_extractor.c (11638B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2002, 2003, 2008, 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  * @file plugins/rpm_extractor.c
     22  * @brief plugin to support RPM files
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "extractor.h"
     27 #include <stdint.h>
     28 #include <rpm/rpmlib.h>
     29 #include <rpm/rpmts.h>
     30 #include <rpm/rpmlog.h>
     31 #if SOMEBSD
     32 #include <pthread_np.h>
     33 #else
     34 #include <pthread.h>
     35 #endif
     36 #include <sys/types.h>
     37 #include <signal.h>
     38 
     39 
     40 /**
     41  * Closure for the 'pipe_feeder'.
     42  */
     43 struct PipeArgs
     44 {
     45 
     46   /**
     47    * Context for reading data from.
     48    */
     49   struct EXTRACTOR_ExtractContext *ec;
     50 
     51   /**
     52    * Lock for synchronizing access to 'ec'.
     53    */
     54   pthread_mutex_t lock;
     55 
     56   /**
     57    * Pipe to write to at [1].
     58    */
     59   int pi[2];
     60 
     61   /**
     62    * Set to 1 if we should stop writing to the pipe.
     63    */
     64   int shutdown;
     65 };
     66 
     67 
     68 /**
     69  * Size of the buffer we use for reading.
     70  */
     71 #define BUF_SIZE (16 * 1024)
     72 
     73 
     74 /**
     75  * Main function of a helper thread that passes the package data
     76  * to librpm.
     77  *
     78  * @param args the 'struct PipeArgs*'
     79  * @return NULL
     80  */
     81 static void *
     82 pipe_feeder (void *args)
     83 {
     84   struct PipeArgs *p = args;
     85   ssize_t rret;
     86   ssize_t wret;
     87   ssize_t done;
     88   void *ptr;
     89   char *buf;
     90 
     91   /* buffer is heap-allocated as this is a thread and
     92      large stack allocations might not be the best idea */
     93   while (0 == p->shutdown)
     94   {
     95     pthread_mutex_lock (&p->lock);
     96     if (-1 == (rret = p->ec->read (p->ec->cls, &ptr, BUF_SIZE)))
     97     {
     98       pthread_mutex_unlock (&p->lock);
     99       break;
    100     }
    101     pthread_mutex_unlock (&p->lock);
    102     if (0 == rret)
    103       break;
    104     buf = ptr;
    105     done = 0;
    106     while ( (0 == p->shutdown) &&
    107             (done < rret) )
    108     {
    109       if (-1 == (wret = write (p->pi[1],
    110                                &buf[done],
    111                                rret - done)))
    112       {
    113         break;
    114       }
    115       if (0 == wret)
    116         break;
    117       done += wret;
    118     }
    119     if (done != rret)
    120       break;
    121   }
    122   close (p->pi[1]);
    123   return NULL;
    124 }
    125 
    126 
    127 /**
    128  * LOG callback called by librpm.  Does nothing, we
    129  * just need this to override the default behavior.
    130  */
    131 static int
    132 discard_log_callback (rpmlogRec rec,
    133                       void *ctx)
    134 {
    135   /* do nothing! */
    136   return 0;
    137 }
    138 
    139 
    140 /**
    141  * Mapping from RPM tags to LE types.
    142  */
    143 struct Matches
    144 {
    145   /**
    146    * RPM tag.
    147    */
    148   int32_t rtype;
    149 
    150   /**
    151    * Corresponding LE type.
    152    */
    153   enum EXTRACTOR_MetaType type;
    154 };
    155 
    156 
    157 /**
    158  * List of mappings from RPM tags to LE types.
    159  */
    160 static struct Matches tests[] = {
    161   {RPMTAG_NAME, EXTRACTOR_METATYPE_PACKAGE_NAME},
    162   {RPMTAG_VERSION, EXTRACTOR_METATYPE_SOFTWARE_VERSION},
    163   {RPMTAG_GROUP, EXTRACTOR_METATYPE_SECTION},
    164   {RPMTAG_SIZE, EXTRACTOR_METATYPE_PACKAGE_INSTALLED_SIZE},
    165   {RPMTAG_SUMMARY, EXTRACTOR_METATYPE_SUMMARY},
    166   {RPMTAG_PACKAGER, EXTRACTOR_METATYPE_PACKAGE_MAINTAINER},
    167   {RPMTAG_BUILDTIME, EXTRACTOR_METATYPE_CREATION_DATE},
    168 #ifdef RPMTAG_COPYRIGHT
    169   {RPMTAG_COPYRIGHT, EXTRACTOR_METATYPE_COPYRIGHT},
    170 #endif
    171   {RPMTAG_LICENSE, EXTRACTOR_METATYPE_LICENSE},
    172   {RPMTAG_DISTRIBUTION, EXTRACTOR_METATYPE_PACKAGE_DISTRIBUTION},
    173   {RPMTAG_BUILDHOST, EXTRACTOR_METATYPE_BUILDHOST},
    174   {RPMTAG_VENDOR, EXTRACTOR_METATYPE_VENDOR},
    175   {RPMTAG_OS, EXTRACTOR_METATYPE_TARGET_OS},
    176   {RPMTAG_DESCRIPTION, EXTRACTOR_METATYPE_DESCRIPTION},
    177   {RPMTAG_URL, EXTRACTOR_METATYPE_URL},
    178   {RPMTAG_DISTURL, EXTRACTOR_METATYPE_URL},
    179   {RPMTAG_RELEASE, EXTRACTOR_METATYPE_PACKAGE_VERSION},
    180   {RPMTAG_PLATFORM, EXTRACTOR_METATYPE_TARGET_PLATFORM},
    181   {RPMTAG_ARCH, EXTRACTOR_METATYPE_TARGET_ARCHITECTURE},
    182   {RPMTAG_CONFLICTNAME, EXTRACTOR_METATYPE_PACKAGE_CONFLICTS},
    183   {RPMTAG_REQUIRENAME, EXTRACTOR_METATYPE_PACKAGE_DEPENDENCY},
    184   {RPMTAG_CONFLICTNAME, EXTRACTOR_METATYPE_PACKAGE_CONFLICTS},
    185   {RPMTAG_PROVIDENAME, EXTRACTOR_METATYPE_PACKAGE_PROVIDES},
    186 
    187 #if 0
    188   {RPMTAG_CHANGELOGTEXT, EXTRACTOR_METATYPE_REVISION_HISTORY},
    189 #endif
    190 
    191 #if 0
    192   /* FIXME: add support for some of these */
    193   RPMTAG_GIF      = 1012,   /* x */
    194   RPMTAG_XPM      = 1013,   /* x */
    195   RPMTAG_SOURCE   = 1018,   /* s[] */
    196   RPMTAG_PATCH    = 1019,   /* s[] */
    197   RPMTAG_PREIN    = 1023,   /* s */
    198   RPMTAG_POSTIN   = 1024,   /* s */
    199   RPMTAG_PREUN    = 1025,   /* s */
    200   RPMTAG_POSTUN   = 1026,   /* s */
    201   RPMTAG_ICON     = 1043,   /* x */
    202   RPMTAG_SOURCERPM    = 1044,   /* s */
    203   RPMTAG_PROVIDENAME    = 1047,   /* s[] */
    204   RPMTAG_EXCLUDEARCH    = 1059,   /* s[] */
    205   RPMTAG_EXCLUDEOS    = 1060,   /* s[] */
    206   RPMTAG_EXCLUSIVEARCH  = 1061,   /* s[] */
    207   RPMTAG_EXCLUSIVEOS    = 1062,   /* s[] */
    208   RPMTAG_TRIGGERSCRIPTS = 1065,   /* s[] */
    209   RPMTAG_TRIGGERNAME    = 1066,   /* s[] */
    210   RPMTAG_TRIGGERVERSION = 1067,   /* s[] */
    211   RPMTAG_VERIFYSCRIPT   = 1079,   /* s */
    212   RPMTAG_PREINPROG    = 1085,   /* s */
    213   RPMTAG_POSTINPROG   = 1086,   /* s */
    214   RPMTAG_PREUNPROG    = 1087,   /* s */
    215   RPMTAG_POSTUNPROG   = 1088,   /* s */
    216   RPMTAG_BUILDARCHS   = 1089,   /* s[] */
    217   RPMTAG_OBSOLETENAME   = 1090,   /* s[] */
    218   RPMTAG_VERIFYSCRIPTPROG = 1091,   /* s */
    219   RPMTAG_TRIGGERSCRIPTPROG  = 1092,   /* s[] */
    220   RPMTAG_COOKIE   = 1094,   /* s */
    221   RPMTAG_FILELANGS    = 1097,   /* s[] */
    222   RPMTAG_PREFIXES   = 1098,   /* s[] */
    223   RPMTAG_INSTPREFIXES   = 1099,   /* s[] */
    224   RPMTAG_PROVIDEVERSION = 1113,   /* s[] */
    225   RPMTAG_OBSOLETEVERSION  = 1115,   /* s[] */
    226   RPMTAG_BASENAMES    = 1117,   /* s[] */
    227   RPMTAG_DIRNAMES   = 1118,   /* s[] */
    228   RPMTAG_OPTFLAGS   = 1122,   /* s */
    229   RPMTAG_PAYLOADFORMAT  = 1124,   /* s */
    230   RPMTAG_PAYLOADCOMPRESSOR  = 1125,   /* s */
    231   RPMTAG_PAYLOADFLAGS   = 1126,   /* s */
    232   RPMTAG_CLASSDICT    = 1142,   /* s[] */
    233   RPMTAG_SOURCEPKGID    = 1146,   /* x */
    234   RPMTAG_PRETRANS   = 1151,   /* s */
    235   RPMTAG_POSTTRANS    = 1152,   /* s */
    236   RPMTAG_PRETRANSPROG   = 1153,   /* s */
    237   RPMTAG_POSTTRANSPROG  = 1154,   /* s */
    238   RPMTAG_DISTTAG    = 1155,   /* s */
    239 #endif
    240   {0, 0}
    241 };
    242 
    243 
    244 /**
    245  * Main entry method for the 'application/x-rpm' extraction plugin.
    246  *
    247  * @param ec extraction context provided to the plugin
    248  */
    249 void
    250 EXTRACTOR_rpm_extract_method (struct EXTRACTOR_ExtractContext *ec)
    251 {
    252   struct PipeArgs parg;
    253   pthread_t pthr;
    254   void *unused;
    255   const char *str;
    256   Header hdr;
    257   HeaderIterator hi;
    258   rpmtd p;
    259   int i;
    260   FD_t fdi;
    261   rpmRC rc;
    262   rpmts ts;
    263   struct sigaction sig;
    264   struct sigaction old;
    265 
    266   /* FIXME: here it might be worthwhile to do some minimal
    267      check to see if this is actually an RPM before we go
    268      and create a pipe and a thread for nothing... */
    269   parg.ec = ec;
    270   parg.shutdown = 0;
    271   if (0 != pipe (parg.pi))
    272     return;
    273   if (0 != pthread_mutex_init (&parg.lock, NULL))
    274   {
    275     close (parg.pi[0]);
    276     close (parg.pi[1]);
    277     return;
    278   }
    279   if (0 != pthread_create (&pthr,
    280                            NULL,
    281                            &pipe_feeder,
    282                            &parg))
    283   {
    284     pthread_mutex_destroy (&parg.lock);
    285     close (parg.pi[0]);
    286     close (parg.pi[1]);
    287     return;
    288   }
    289   rpmlogSetCallback (&discard_log_callback, NULL);
    290   fdi = fdDup (parg.pi[0]);
    291   ts = rpmtsCreate ();
    292   rc = rpmReadPackageFile (ts, fdi, "GNU libextractor", &hdr);
    293   switch (rc)
    294   {
    295   case RPMRC_OK:
    296   case RPMRC_NOKEY:
    297   case RPMRC_NOTTRUSTED:
    298     break;
    299   case RPMRC_NOTFOUND:
    300   case RPMRC_FAIL:
    301   default:
    302     goto END;
    303   }
    304   pthread_mutex_lock (&parg.lock);
    305   if (0 != ec->proc (ec->cls,
    306                      "rpm",
    307                      EXTRACTOR_METATYPE_MIMETYPE,
    308                      EXTRACTOR_METAFORMAT_UTF8,
    309                      "text/plain",
    310                      "application/x-rpm",
    311                      strlen ("application/x-rpm") + 1))
    312   {
    313     pthread_mutex_unlock (&parg.lock);
    314     goto END;
    315   }
    316   pthread_mutex_unlock (&parg.lock);
    317   hi = headerInitIterator (hdr);
    318   p = rpmtdNew ();
    319   while (1 == headerNext (hi, p))
    320     for (i = 0; 0 != tests[i].rtype; i++)
    321     {
    322       if (tests[i].rtype != p->tag)
    323         continue;
    324       switch (p->type)
    325       {
    326       case RPM_STRING_ARRAY_TYPE:
    327       case RPM_I18NSTRING_TYPE:
    328       case RPM_STRING_TYPE:
    329         while (NULL != (str = rpmtdNextString (p)))
    330         {
    331           pthread_mutex_lock (&parg.lock);
    332           if (0 != ec->proc (ec->cls,
    333                              "rpm",
    334                              tests[i].type,
    335                              EXTRACTOR_METAFORMAT_UTF8,
    336                              "text/plain",
    337                              str,
    338                              strlen (str) + 1))
    339 
    340           {
    341             pthread_mutex_unlock (&parg.lock);
    342             goto CLEANUP;
    343           }
    344           pthread_mutex_unlock (&parg.lock);
    345         }
    346         break;
    347       case RPM_INT32_TYPE:
    348         {
    349           if (p->tag == RPMTAG_BUILDTIME)
    350           {
    351             char tmp[80];
    352             uint32_t *v = rpmtdNextUint32 (p);
    353             time_t tp = (time_t) *v;
    354 
    355             if (NULL == ctime_r (&tp, tmp))
    356               break;
    357             if ( (strlen (tmp) > 0) &&
    358                  (isspace ((unsigned char) tmp[strlen (tmp) - 1])) )
    359               tmp[strlen (tmp) - 1] = '\0';         /* eat linefeed */
    360             pthread_mutex_lock (&parg.lock);
    361             if (0 != ec->proc (ec->cls,
    362                                "rpm",
    363                                tests[i].type,
    364                                EXTRACTOR_METAFORMAT_UTF8,
    365                                "text/plain",
    366                                tmp,
    367                                strlen (tmp) + 1))
    368             {
    369               pthread_mutex_unlock (&parg.lock);
    370               goto CLEANUP;
    371             }
    372             pthread_mutex_unlock (&parg.lock);
    373           }
    374           else
    375           {
    376             char tmp[14];
    377             uint32_t *s = rpmtdNextUint32 (p);
    378 
    379             snprintf (tmp,
    380                       sizeof (tmp),
    381                       "%u",
    382                       (unsigned int) *s);
    383             pthread_mutex_lock (&parg.lock);
    384             if (0 != ec->proc (ec->cls,
    385                                "rpm",
    386                                tests[i].type,
    387                                EXTRACTOR_METAFORMAT_UTF8,
    388                                "text/plain",
    389                                tmp,
    390                                strlen (tmp) + 1))
    391             {
    392               pthread_mutex_unlock (&parg.lock);
    393               goto CLEANUP;
    394             }
    395             pthread_mutex_unlock (&parg.lock);
    396           }
    397           break;
    398         }
    399       default:
    400         break;
    401       }
    402     }
    403 CLEANUP:
    404   rpmtdFree (p);
    405   headerFreeIterator (hi);
    406 
    407 END:
    408   headerFree (hdr);
    409   rpmtsFree (ts);
    410 
    411   /* make sure SIGALRM does not kill us, then use it to
    412      kill the thread */
    413   memset (&sig, 0, sizeof (struct sigaction));
    414   memset (&old, 0, sizeof (struct sigaction));
    415   sig.sa_flags = SA_NODEFER;
    416   sig.sa_handler = SIG_IGN;
    417   sigaction (SIGALRM, &sig, &old);
    418   parg.shutdown = 1;
    419   close (parg.pi[0]);
    420   Fclose (fdi);
    421   pthread_kill (pthr, SIGALRM);
    422   pthread_join (pthr, &unused);
    423   pthread_mutex_destroy (&parg.lock);
    424   sigaction (SIGALRM, &old, &sig);
    425 }
    426 
    427 
    428 /* end of rpm_extractor.c */