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