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