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