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 */