libextractor

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

rpm_extractor.c (11824B)


      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 void
    253 EXTRACTOR_rpm_extract_method (struct EXTRACTOR_ExtractContext *ec)
    254 {
    255   struct PipeArgs parg;
    256   pthread_t pthr;
    257   void *unused;
    258   const char *str;
    259   Header hdr;
    260   HeaderIterator hi;
    261   rpmtd p;
    262   int i;
    263   FD_t fdi;
    264   rpmRC rc;
    265   rpmts ts;
    266   struct sigaction sig;
    267   struct sigaction old;
    268 
    269   /* FIXME: here it might be worthwhile to do some minimal
    270      check to see if this is actually an RPM before we go
    271      and create a pipe and a thread for nothing... */
    272   parg.ec = ec;
    273   parg.shutdown = 0;
    274   if (0 != pipe (parg.pi))
    275     return;
    276   if (0 != pthread_mutex_init (&parg.lock, NULL))
    277   {
    278     close (parg.pi[0]);
    279     close (parg.pi[1]);
    280     return;
    281   }
    282   if (0 != pthread_create (&pthr,
    283                            NULL,
    284                            &pipe_feeder,
    285                            &parg))
    286   {
    287     pthread_mutex_destroy (&parg.lock);
    288     close (parg.pi[0]);
    289     close (parg.pi[1]);
    290     return;
    291   }
    292   rpmlogSetCallback (&discard_log_callback, NULL);
    293   fdi = fdDup (parg.pi[0]);
    294   ts = rpmtsCreate ();
    295   rc = rpmReadPackageFile (ts, fdi, "GNU libextractor", &hdr);
    296   switch (rc)
    297   {
    298   case RPMRC_OK:
    299   case RPMRC_NOKEY:
    300   case RPMRC_NOTTRUSTED:
    301     break;
    302   case RPMRC_NOTFOUND:
    303   case RPMRC_FAIL:
    304   default:
    305     goto END;
    306   }
    307   pthread_mutex_lock (&parg.lock);
    308   if (0 != ec->proc (ec->cls,
    309                      "rpm",
    310                      EXTRACTOR_METATYPE_MIMETYPE,
    311                      EXTRACTOR_METAFORMAT_UTF8,
    312                      "text/plain",
    313                      "application/x-rpm",
    314                      strlen ("application/x-rpm") + 1))
    315   {
    316     pthread_mutex_unlock (&parg.lock);
    317     goto END;
    318   }
    319   pthread_mutex_unlock (&parg.lock);
    320   hi = headerInitIterator (hdr);
    321   p = rpmtdNew ();
    322   while (1 == headerNext (hi, p))
    323     for (i = 0; 0 != tests[i].rtype; i++)
    324     {
    325       if (tests[i].rtype != p->tag)
    326         continue;
    327       switch (p->type)
    328       {
    329       case RPM_STRING_ARRAY_TYPE:
    330       case RPM_I18NSTRING_TYPE:
    331       case RPM_STRING_TYPE:
    332         while (NULL != (str = rpmtdNextString (p)))
    333         {
    334           pthread_mutex_lock (&parg.lock);
    335           if (0 != ec->proc (ec->cls,
    336                              "rpm",
    337                              tests[i].type,
    338                              EXTRACTOR_METAFORMAT_UTF8,
    339                              "text/plain",
    340                              str,
    341                              strlen (str) + 1))
    342 
    343           {
    344             pthread_mutex_unlock (&parg.lock);
    345             goto CLEANUP;
    346           }
    347           pthread_mutex_unlock (&parg.lock);
    348         }
    349         break;
    350       case RPM_INT32_TYPE:
    351         {
    352           if (p->tag == RPMTAG_BUILDTIME)
    353           {
    354             char tmp[80];
    355             uint32_t *v = rpmtdNextUint32 (p);
    356             time_t tp;
    357 
    358             if (NULL == v)
    359               break;
    360             tp = (time_t) *v;
    361             if (NULL == ctime_r (&tp, tmp))
    362               break;
    363             if ( (strlen (tmp) > 0) &&
    364                  (isspace ((unsigned char) tmp[strlen (tmp) - 1])) )
    365               tmp[strlen (tmp) - 1] = '\0';         /* eat linefeed */
    366             pthread_mutex_lock (&parg.lock);
    367             if (0 != ec->proc (ec->cls,
    368                                "rpm",
    369                                tests[i].type,
    370                                EXTRACTOR_METAFORMAT_UTF8,
    371                                "text/plain",
    372                                tmp,
    373                                strlen (tmp) + 1))
    374             {
    375               pthread_mutex_unlock (&parg.lock);
    376               goto CLEANUP;
    377             }
    378             pthread_mutex_unlock (&parg.lock);
    379           }
    380           else
    381           {
    382             char tmp[14];
    383             uint32_t *s = rpmtdNextUint32 (p);
    384 
    385             if (NULL == s)
    386               break;
    387             snprintf (tmp,
    388                       sizeof (tmp),
    389                       "%u",
    390                       (unsigned int) *s);
    391             pthread_mutex_lock (&parg.lock);
    392             if (0 != ec->proc (ec->cls,
    393                                "rpm",
    394                                tests[i].type,
    395                                EXTRACTOR_METAFORMAT_UTF8,
    396                                "text/plain",
    397                                tmp,
    398                                strlen (tmp) + 1))
    399             {
    400               pthread_mutex_unlock (&parg.lock);
    401               goto CLEANUP;
    402             }
    403             pthread_mutex_unlock (&parg.lock);
    404           }
    405           break;
    406         }
    407       default:
    408         break;
    409       }
    410     }
    411 CLEANUP:
    412   rpmtdFree (p);
    413   headerFreeIterator (hi);
    414 
    415 END:
    416   headerFree (hdr);
    417   rpmtsFree (ts);
    418 
    419   /* make sure SIGALRM does not kill us, then use it to
    420      kill the thread */
    421   memset (&sig, 0, sizeof (struct sigaction));
    422   memset (&old, 0, sizeof (struct sigaction));
    423   sig.sa_flags = SA_NODEFER;
    424   sig.sa_handler = SIG_IGN;
    425   sigaction (SIGALRM, &sig, &old);
    426   parg.shutdown = 1;
    427   close (parg.pi[0]);
    428   Fclose (fdi);
    429   pthread_kill (pthr, SIGALRM);
    430   pthread_join (pthr, &unused);
    431   pthread_mutex_destroy (&parg.lock);
    432   sigaction (SIGALRM, &old, &sig);
    433 }
    434 
    435 
    436 /* end of rpm_extractor.c */