- remove unused private include from rpmhandler.h (RH bugzilla #157734)
[apt.git] / tools / genpkglist.cc
1 /*
2  * $Id: genpkglist.cc,v 1.7 2003/01/30 17:18:21 niemeyer Exp $
3  */
4 #include <alloca.h>
5 #include <ctype.h>
6 #include <dirent.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <rpm/rpmlib.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <assert.h>
16
17 #include <map>
18 #include <iostream>
19
20 #include <apt-pkg/error.h>
21 #include <apt-pkg/tagfile.h>
22 #include <apt-pkg/configuration.h>
23 #include <apt-pkg/rpmhandler.h>
24 #include <config.h>
25
26 #include "cached_md5.h"
27
28 #if RPM_VERSION >= 0x040100
29 #include <rpm/rpmts.h>
30 #endif
31
32 #define CRPMTAG_TIMESTAMP   1012345
33
34 int tags[] =  {
35        RPMTAG_NAME, 
36        RPMTAG_EPOCH,
37        RPMTAG_VERSION,
38        RPMTAG_RELEASE,
39        RPMTAG_GROUP,
40        RPMTAG_ARCH,
41        RPMTAG_PACKAGER,
42        RPMTAG_SOURCERPM,
43        RPMTAG_SIZE,
44        RPMTAG_VENDOR,
45        RPMTAG_OS,
46        
47        RPMTAG_DESCRIPTION, 
48        RPMTAG_SUMMARY, 
49        /*RPMTAG_HEADERI18NTABLE*/ HEADER_I18NTABLE,
50        
51        RPMTAG_REQUIREFLAGS, 
52        RPMTAG_REQUIRENAME,
53        RPMTAG_REQUIREVERSION,
54        
55        RPMTAG_CONFLICTFLAGS,
56        RPMTAG_CONFLICTNAME,
57        RPMTAG_CONFLICTVERSION,
58        
59        RPMTAG_PROVIDENAME,
60        RPMTAG_PROVIDEFLAGS,
61        RPMTAG_PROVIDEVERSION,
62        
63        RPMTAG_OBSOLETENAME,
64        RPMTAG_OBSOLETEFLAGS,
65        RPMTAG_OBSOLETEVERSION,
66
67        RPMTAG_FILEFLAGS
68 };
69 int numTags = sizeof(tags) / sizeof(int);
70
71
72
73 typedef struct {
74    string importance;
75    string date;
76    string summary;
77    string url;
78 } UpdateInfo;
79
80
81 static inline int usefullFile(char *a)
82 {
83    int l = strlen(a);
84    
85    if (strstr(a, "bin") || strstr(a, "/etc") || strncmp(a, "/lib", 4) == 0)
86        return 1;
87    
88    if (l < 3)
89        return 0;
90    
91    if (strcmp(a + l - 3, ".so") == 0
92        || strstr(a, ".so."))
93        return 1;
94    return 0;
95 }
96
97
98 static void copyStrippedFileList(Header header, Header newHeader)
99 {
100    int i;
101    int i1, i2;
102    
103    int type1, type2, type3;
104    int count1, count2, count3;
105    char **dirnames = NULL, **basenames = NULL;
106    int_32 *dirindexes = NULL;
107    char **dnames, **bnames;
108    int_32 *dindexes;
109    int res1, res2, res3;
110    
111 #define FREE(a) if (a) free(a);
112    
113    res1 = headerGetEntry(header, RPMTAG_DIRNAMES, &type1, 
114                          (void**)&dirnames, &count1);
115    res2 = headerGetEntry(header, RPMTAG_BASENAMES, &type2, 
116                          (void**)&basenames, &count2);
117    res3 = headerGetEntry(header, RPMTAG_DIRINDEXES, &type3, 
118                          (void**)&dirindexes, &count3);
119    
120    if (res1 != 1 || res2 != 1 || res3 != 1) {
121       FREE(dirnames);
122       FREE(basenames);
123       return;
124    }
125
126    dnames = dirnames;
127    bnames = basenames;
128    dindexes = (int_32*)malloc(sizeof(int_32)*count3);
129    
130    i1 = 0;
131    i2 = 0;
132    for (i = 0; i < count2 ; i++) 
133    {
134       int ok = 0;
135       
136       ok = usefullFile(basenames[i]);
137       if (!ok) 
138           ok = usefullFile(dirnames[dirindexes[i]]);
139       
140       if (!ok) {
141          int k = i;
142          while (dirindexes[i] == dirindexes[k] && i < count2)
143              i++;
144          i--;
145          continue;
146       }
147       
148       
149       if (ok)
150       {
151          int j;
152          
153          bnames[i1] = basenames[i];
154          for (j = 0; j < i2; j++)
155          {
156             if (dnames[j] == dirnames[dirindexes[i]])
157             {
158                dindexes[i1] = j;
159                break;
160             }
161          }
162          if (j == i2) 
163          {
164             dnames[i2] = dirnames[dirindexes[i]];
165             dindexes[i1] = i2;
166             i2++;
167          }
168          assert(i2 <= count1);
169          i1++;
170       } 
171    }
172    
173    if (i1 == 0) {
174       FREE(dirnames);
175       FREE(basenames);
176       FREE(dindexes);
177       return;
178    }
179    
180    headerAddEntry(newHeader, RPMTAG_DIRNAMES, type1, dnames, i2);
181    
182    headerAddEntry(newHeader, RPMTAG_BASENAMES, type2, bnames, i1);
183    
184    headerAddEntry(newHeader, RPMTAG_DIRINDEXES, type3, dindexes, i1);
185    
186    FREE(dirnames);
187    FREE(basenames);
188    FREE(dindexes);
189 }
190
191
192
193
194
195 bool loadUpdateInfo(char *path, map<string,UpdateInfo> &map)
196 {
197    FileFd F(path, FileFd::ReadOnly);
198    if (_error->PendingError()) 
199    {
200       return false;
201    }
202    
203    pkgTagFile Tags(&F);
204    pkgTagSection Section;
205    
206    while (Tags.Step(Section)) 
207    {
208       string file = Section.FindS("File");
209       UpdateInfo info;
210
211       info.importance = Section.FindS("Importance");
212       info.date = Section.FindS("Date");
213       info.summary = Section.FindS("Summary");
214       info.url = Section.FindS("URL");
215
216       map[file] = info;
217    }
218    return true;
219 }
220
221 #if RPM_VERSION >= 0x040000
222 // No prototype from rpm after 4.0.
223 extern "C" {
224 int headerGetRawEntry(Header h, int_32 tag, int_32 * type,
225                       void *p, int_32 *c);
226 }
227 #endif
228
229 bool copyFields(Header h, Header newHeader,
230                 FILE *idxfile, const char *directory, char *filename,
231                 unsigned filesize, map<string,UpdateInfo> &updateInfo,
232                 bool fullFileList)
233 {
234    int i;
235    int_32 size[1];
236
237    size[0] = filesize;
238    
239    // the std tags
240    for (i = 0; i < numTags; i++) {
241       int_32 type, count;
242       void *data;
243       int res;
244       
245       // Copy raw entry, so that internationalized strings
246       // will get copied correctly.
247       res = headerGetRawEntry(h, tags[i], &type, &data, &count);
248       if (res != 1)
249          continue;
250       headerAddEntry(newHeader, tags[i], type, data, count);
251    }
252  
253    if (fullFileList) {
254       int type1, type2, type3;
255       int count1, count2, count3;
256       char **dnames, **bnames, **dindexes;
257       int res;
258    
259       res = headerGetEntry(h, RPMTAG_DIRNAMES, &type1, 
260                            (void**)&dnames, &count1);
261       res = headerGetEntry(h, RPMTAG_BASENAMES, &type2, 
262                            (void**)&bnames, &count2);
263       res = headerGetEntry(h, RPMTAG_DIRINDEXES, &type3, 
264                            (void**)&dindexes, &count3);
265
266       if (res == 1) {
267          headerAddEntry(newHeader, RPMTAG_DIRNAMES, type1, dnames, count1);
268          headerAddEntry(newHeader, RPMTAG_BASENAMES, type2, bnames, count2);
269          headerAddEntry(newHeader, RPMTAG_DIRINDEXES, type3, dindexes, count3);
270       }
271    } else {
272        copyStrippedFileList(h, newHeader);
273    }
274    
275    // update index of srpms
276    if (idxfile) {
277       int_32 type, count;
278       char *srpm;
279       char *name;
280       int res;
281       
282       res = headerGetEntry(h, RPMTAG_NAME, &type, 
283                            (void**)&name, &count);
284       res = headerGetEntry(h, RPMTAG_SOURCERPM, &type, 
285                            (void**)&srpm, &count);
286       if (res == 1) {
287          fprintf(idxfile, "%s %s\n", srpm, name);
288       }
289    }
290    // our additional tags
291    headerAddEntry(newHeader, CRPMTAG_DIRECTORY, RPM_STRING_TYPE,
292                   directory, 1);
293    headerAddEntry(newHeader, CRPMTAG_FILENAME, RPM_STRING_TYPE, 
294                   filename, 1);
295    headerAddEntry(newHeader, CRPMTAG_FILESIZE, RPM_INT32_TYPE,
296                   size, 1);
297       
298    // update description tags
299    if (updateInfo.find(string(filename)) != updateInfo.end()) {
300       const char *tmp;
301       string name = string(filename);
302       
303       tmp = updateInfo[name].summary.c_str();
304       headerAddEntry(newHeader, CRPMTAG_UPDATE_SUMMARY,
305                      RPM_STRING_TYPE,
306                      tmp, 1);
307       tmp = updateInfo[name].url.c_str();
308       headerAddEntry(newHeader, CRPMTAG_UPDATE_URL,
309                      RPM_STRING_TYPE,
310                      tmp, 1);
311       tmp = updateInfo[name].date.c_str();
312       headerAddEntry(newHeader, CRPMTAG_UPDATE_DATE,
313                      RPM_STRING_TYPE,
314                      tmp, 1);
315       tmp = updateInfo[name].importance.c_str();
316       headerAddEntry(newHeader, CRPMTAG_UPDATE_IMPORTANCE,
317                      RPM_STRING_TYPE,
318                      tmp, 1);
319    }
320    
321    return true;
322 }
323
324
325 int selectDirent(const struct dirent *ent)
326 {
327    int state = 0;
328    const char *p = ent->d_name;
329    
330    while (1) {
331       if (*p == '.') {
332           state = 1;
333       } else if (state == 1 && *p == 'r')
334           state++;
335       else if (state == 2 && *p == 'p')
336           state++;
337       else if (state == 3 && *p == 'm')
338           state++;
339       else if (state == 4 && *p == '\0')
340           return 1;
341       else if (*p == '\0')
342           return 0;
343       else
344           state = 0;
345       p++;
346    }
347 }
348
349
350 void usage()
351 {
352    cerr << "genpkglist " << VERSION << endl;
353    cerr << "usage: genpkglist [<options>] <dir> <suffix>" << endl;
354    cerr << "options:" << endl;
355    cerr << " --index <file>  file to write srpm index data to" << endl;
356    cerr << " --info <file>   file to read update info from" << endl;
357    cerr << " --meta <suffix> create package file list with given suffix" << endl;
358    cerr << " --bloat         do not strip the package file list. Needed for some" << endl;
359    cerr << "                 distributions that use non-automatically generated" << endl;
360    cerr << "                 file dependencies" << endl;
361    cerr << " --append        append to the package file list, don't overwrite" << endl;
362    cerr << " --progress      show a progress bar" << endl;
363    cerr << " --cachedir=DIR  use a custom directory for package md5sum cache"<<endl;
364 }
365
366
367
368 #ifndef HAVE_SCANDIR
369 // from glibc 1.09.1  mod'd by jmik, ins'd by asm, fix'd by sbi
370 int alphasort(const void * a, const void * b)
371 {
372   return strcmp ((*(struct dirent **) a)->d_name,
373                  (*(struct dirent **) b)->d_name);
374 }
375
376 int scandir(const char * dir, struct dirent *** namelist, 
377         int (* select)(struct dirent *), 
378         int (* cmp)(const void *, const void *))
379
380 {
381   DIR *dp = opendir (dir);
382   struct dirent **v = NULL;
383   size_t vsize = 0, i;
384   struct dirent *d;
385   int save;
386
387   if (dp == NULL)
388     return -1;
389
390   save = errno;
391   errno = 0;
392
393   i = 0;
394   while ((d = readdir (dp)) != NULL)
395     {
396     if (select == NULL || (*select) (d))
397       {
398         if (i == vsize)
399           {
400             struct dirent **newv;
401             if (vsize == 0)
402               vsize = 10;
403             else
404               vsize *= 2;
405             newv = (struct dirent **) realloc (v, vsize * sizeof (*v));
406             if (newv == NULL)
407               {
408               lose:
409                 errno = ENOMEM;
410                 break;
411               }
412             v = newv;
413           }
414
415         v[i] = (struct dirent *) malloc (d->d_reclen);
416         if (v[i] == NULL)
417           goto lose;
418
419         // *v[i++] = *d;
420         memcpy(v[i], d, d->d_reclen);
421         i++;
422       }
423     }
424
425   v[i] = NULL;
426
427   if (errno != 0)
428     {
429       save = errno;
430       (void) closedir (dp);
431       while (i > 0)
432         free (v[--i]);
433       free (v);
434       errno = save;
435       return -1;
436     }
437
438   (void) closedir (dp);
439   errno = save;
440
441   /* Sort the list if we have a comparison function to sort with.  */
442   if (cmp != NULL)
443     qsort (v, i, sizeof (struct dirent *), cmp);
444
445   *namelist = v;
446   return i;
447 }
448 // end of new stuff from glibc
449 #endif /* !HAVE_SCANDIR */
450
451
452 int main(int argc, char ** argv) 
453 {
454    string rpmsdir;
455    string pkglist_path;
456    FD_t outfd, fd;
457    struct dirent **dirEntries;
458    int entry_no, entry_cur;
459    map<string,UpdateInfo> updateInfo;
460    CachedMD5 *md5cache;
461    char *op_dir;
462    char *op_suf;
463    char *op_index = NULL;
464    char *op_update = NULL;
465    FILE *idxfile;
466    int i;
467    bool fullFileList = false;
468    bool progressBar = false;
469    const char *pkgListSuffix = NULL;
470    bool pkgListAppend = false;
471    
472    putenv("LC_ALL="); // Is this necessary yet (after i18n was supported)?
473    for (i = 1; i < argc; i++) {
474       if (strcmp(argv[i], "--index") == 0) {
475          i++;
476          if (i < argc) {
477             op_index = argv[i];
478          } else {
479             cout << "genpkglist: filename missing for option --index"<<endl;
480             exit(1);
481          }
482       } else if (strcmp(argv[i], "--info") == 0) {
483          i++;
484          if (i < argc) {
485             op_update = argv[i];
486          } else {
487             cout << "genpkglist: filename missing for option --info"<<endl;
488             exit(1);
489          }
490       } else if (strcmp(argv[i], "--bloat") == 0) {
491          fullFileList = true;
492       } else if (strcmp(argv[i], "--progress") == 0) {
493          progressBar = true;
494       } else if (strcmp(argv[i], "--append") == 0) {
495          pkgListAppend = true;
496       } else if (strcmp(argv[i], "--meta") == 0) {
497          i++;
498          if (i < argc) {
499             pkgListSuffix = argv[i];
500          } else {
501             cout << "genpkglist: argument missing for option --meta"<<endl;
502             exit(1);
503          }
504       } else if (strcmp(argv[i], "--cachedir") == 0) {
505          i++;
506          if (i < argc) {
507             _config->Set("Dir::Cache", argv[i]);
508          } else {
509             cout << "genpkglist: argument missing for option --cachedir"<<endl;
510             exit(1);
511          }
512       } else {
513          break;
514       }
515    }
516    if (argc - i > 0)
517        op_dir = argv[i++];
518    else {
519       usage();
520       exit(1);
521    }
522    if (argc - i > 0)
523        op_suf = argv[i++];
524    else {
525       usage();
526       exit(1);
527    }
528    if (argc != i) {
529       usage();
530    }
531    
532    if (op_update) {
533       if (!loadUpdateInfo(op_update, updateInfo)) {
534          cerr << "genpkglist: error reading update info from file " << op_update << endl;
535          _error->DumpErrors();
536          exit(1);
537       }
538    }
539    if (op_index) {
540       idxfile = fopen(op_index, "w+");
541       if (!idxfile) {
542          cerr << "genpkglist: could not open " << op_index << " for writing";
543          perror("");
544          exit(1);
545       }
546    } else {
547       idxfile = NULL;
548    }
549    
550    {
551       char cwd[200];
552       
553       getcwd(cwd, 200);
554       if (*op_dir != '/') {
555          rpmsdir = string(cwd) + "/" + string(op_dir);
556       } else {
557          rpmsdir = string(op_dir);
558       }
559    }
560    pkglist_path = string(rpmsdir);
561    rpmsdir = rpmsdir + "/RPMS." + string(op_suf);
562
563    string dirtag = "RPMS." + string(op_suf);
564
565    entry_no = scandir(rpmsdir.c_str(), &dirEntries, selectDirent, alphasort);
566    if (entry_no < 0) {
567       cerr << "genpkglist: error opening directory " << rpmsdir << ":"
568           << strerror(errno);
569       return 1;
570    }
571    
572    chdir(rpmsdir.c_str());
573    
574    if (pkgListSuffix != NULL)
575            pkglist_path = pkglist_path + "/base/pkglist." + pkgListSuffix;
576    else
577            pkglist_path = pkglist_path + "/base/pkglist." + op_suf;
578    
579    
580    if (pkgListAppend == true && FileExists(pkglist_path)) {
581       outfd = fdOpen(pkglist_path.c_str(), O_WRONLY|O_APPEND, 0644);
582    } else {
583       unlink(pkglist_path.c_str());
584       outfd = fdOpen(pkglist_path.c_str(), O_WRONLY|O_TRUNC|O_CREAT, 0644);
585    }
586    if (!outfd) {
587       cerr << "genpkglist: error creating file" << pkglist_path << ":"
588           << strerror(errno);
589       return 1;
590    }
591
592    md5cache = new CachedMD5(string(op_dir) + string(op_suf), "genpkglist");
593
594 #if RPM_VERSION >= 0x040100
595    rpmReadConfigFiles(NULL, NULL);
596    rpmts ts = rpmtsCreate();
597    rpmtsSetVSFlags(ts, (rpmVSFlags_e)-1);
598 #else
599    int isSource;
600 #endif   
601
602    for (entry_cur = 0; entry_cur < entry_no; entry_cur++) {
603       struct stat sb;
604
605       if (progressBar) {
606          if (entry_cur)
607             printf("\b\b\b\b\b\b\b\b\b\b");
608          printf(" %04i/%04i", entry_cur + 1, entry_no);
609          fflush(stdout);
610       }
611
612       if (stat(dirEntries[entry_cur]->d_name, &sb) < 0) {
613             cerr << "\nWarning: " << strerror(errno) << ": " << 
614                     dirEntries[entry_cur]->d_name << endl;
615             continue;
616       }
617
618       {
619          Header h;
620          int rc;
621          
622          fd = fdOpen(dirEntries[entry_cur]->d_name, O_RDONLY, 0666);
623
624          if (!fd) {
625             cerr << "\nWarning: " << strerror(errno) << ": " << 
626                     dirEntries[entry_cur]->d_name << endl;
627             continue;
628          }
629          
630 #if RPM_VERSION >= 0x040100
631          rc = rpmReadPackageFile(ts, fd, dirEntries[entry_cur]->d_name, &h);
632          if (rc == RPMRC_OK || rc == RPMRC_NOTTRUSTED || rc == RPMRC_NOKEY) {
633 #else
634          rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
635          if (rc == 0) {
636 #endif
637             Header newHeader;
638             char md5[34];
639             
640             newHeader = headerNew();
641             
642             copyFields(h, newHeader, idxfile, dirtag.c_str(),
643                        dirEntries[entry_cur]->d_name,
644                        sb.st_size, updateInfo, fullFileList);
645
646             md5cache->MD5ForFile(string(dirEntries[entry_cur]->d_name), 
647                                  sb.st_mtime, md5);
648             headerAddEntry(newHeader, CRPMTAG_MD5, RPM_STRING_TYPE, md5, 1);
649
650             headerWrite(outfd, newHeader, HEADER_MAGIC_YES);
651             
652             headerFree(newHeader);
653             headerFree(h);
654          } else {
655             cerr << "\nWarning: Skipping malformed RPM: " << 
656                     dirEntries[entry_cur]->d_name << endl;
657          }
658          Fclose(fd);
659       }
660    }
661
662    Fclose(outfd);
663
664 #if RPM_VERSION >= 0x040100
665    ts = rpmtsFree(ts);
666 #endif
667    
668    delete md5cache;
669
670    return 0;
671 }