Avoid using legacy rpmio interfaces
[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    void *dirnameval = NULL, *basenameval = NULL, *dirindexval = NULL;
108    char **dnames, **bnames;
109    int_32 *dindexes;
110    int res1, res2, res3;
111    
112 #define FREE(a) if (a) free(a);
113    
114    res1 = headerGetEntry(header, RPMTAG_DIRNAMES, &type1, 
115                          (void**)&dirnameval, &count1);
116    res2 = headerGetEntry(header, RPMTAG_BASENAMES, &type2, 
117                          (void**)&basenameval, &count2);
118    res3 = headerGetEntry(header, RPMTAG_DIRINDEXES, &type3, 
119                          (void**)&dirindexval, &count3);
120    dirnames = (char **)dirnameval;
121    basenames = (char **)basenameval;
122    dirindexes = (int_32 *)dirindexval;
123    
124    if (res1 != 1 || res2 != 1 || res3 != 1) {
125       FREE(dirnames);
126       FREE(basenames);
127       return;
128    }
129
130    dnames = dirnames;
131    bnames = basenames;
132    dindexes = (int_32*)malloc(sizeof(int_32)*count3);
133    
134    i1 = 0;
135    i2 = 0;
136    for (i = 0; i < count2 ; i++) 
137    {
138       int ok = 0;
139       
140       ok = usefullFile(basenames[i]);
141       if (!ok) 
142           ok = usefullFile(dirnames[dirindexes[i]]);
143       
144       if (!ok) {
145          int k = i;
146          while (dirindexes[i] == dirindexes[k] && i < count2)
147              i++;
148          i--;
149          continue;
150       }
151       
152       
153       if (ok)
154       {
155          int j;
156          
157          bnames[i1] = basenames[i];
158          for (j = 0; j < i2; j++)
159          {
160             if (dnames[j] == dirnames[dirindexes[i]])
161             {
162                dindexes[i1] = j;
163                break;
164             }
165          }
166          if (j == i2) 
167          {
168             dnames[i2] = dirnames[dirindexes[i]];
169             dindexes[i1] = i2;
170             i2++;
171          }
172          assert(i2 <= count1);
173          i1++;
174       } 
175    }
176    
177    if (i1 == 0) {
178       FREE(dirnames);
179       FREE(basenames);
180       FREE(dindexes);
181       return;
182    }
183    
184    headerAddEntry(newHeader, RPMTAG_DIRNAMES, type1, dnames, i2);
185    
186    headerAddEntry(newHeader, RPMTAG_BASENAMES, type2, bnames, i1);
187    
188    headerAddEntry(newHeader, RPMTAG_DIRINDEXES, type3, dindexes, i1);
189    
190    FREE(dirnames);
191    FREE(basenames);
192    FREE(dindexes);
193 }
194
195
196
197
198
199 bool loadUpdateInfo(char *path, map<string,UpdateInfo> &map)
200 {
201    FileFd F(path, FileFd::ReadOnly);
202    if (_error->PendingError()) 
203    {
204       return false;
205    }
206    
207    pkgTagFile Tags(&F);
208    pkgTagSection Section;
209    
210    while (Tags.Step(Section)) 
211    {
212       string file = Section.FindS("File");
213       UpdateInfo info;
214
215       info.importance = Section.FindS("Importance");
216       info.date = Section.FindS("Date");
217       info.summary = Section.FindS("Summary");
218       info.url = Section.FindS("URL");
219
220       map[file] = info;
221    }
222    return true;
223 }
224
225 #if RPM_VERSION >= 0x040000
226 // No prototype from rpm after 4.0.
227 extern "C" {
228 int headerGetRawEntry(Header h, int_32 tag, int_32 * type,
229                       void *p, int_32 *c);
230 }
231 #endif
232
233 bool copyFields(Header h, Header newHeader,
234                 FILE *idxfile, const char *directory, char *filename,
235                 unsigned filesize, map<string,UpdateInfo> &updateInfo,
236                 bool fullFileList)
237 {
238    int i;
239    int_32 size[1];
240
241    size[0] = filesize;
242    
243    // the std tags
244    for (i = 0; i < numTags; i++) {
245       int_32 type, count;
246       void *data;
247       int res;
248       
249       // Copy raw entry, so that internationalized strings
250       // will get copied correctly.
251       res = headerGetRawEntry(h, tags[i], &type, &data, &count);
252       if (res != 1)
253          continue;
254       headerAddEntry(newHeader, tags[i], type, data, count);
255    }
256  
257    if (fullFileList) {
258       int type1, type2, type3;
259       int count1, count2, count3;
260       char **dnames, **bnames, **dindexes;
261       void *dnameval, *bnameval, *dindexval;
262       int res;
263    
264       res = headerGetEntry(h, RPMTAG_DIRNAMES, &type1, 
265                            (void**)&dnameval, &count1);
266       res = headerGetEntry(h, RPMTAG_BASENAMES, &type2, 
267                            (void**)&bnameval, &count2);
268       res = headerGetEntry(h, RPMTAG_DIRINDEXES, &type3, 
269                            (void**)&dindexval, &count3);
270
271       dnames = (char **)dnameval;
272       bnames = (char **)bnameval;
273       dindexes = (char **)dindexval;
274
275       if (res == 1) {
276          headerAddEntry(newHeader, RPMTAG_DIRNAMES, type1, dnames, count1);
277          headerAddEntry(newHeader, RPMTAG_BASENAMES, type2, bnames, count2);
278          headerAddEntry(newHeader, RPMTAG_DIRINDEXES, type3, dindexes, count3);
279       }
280    } else {
281        copyStrippedFileList(h, newHeader);
282    }
283    
284    // update index of srpms
285    if (idxfile) {
286       int_32 type, count;
287       char *srpm, *name;
288       void *srpmval, *nameval;
289       int res;
290       
291       res = headerGetEntry(h, RPMTAG_NAME, &type, 
292                            (void**)&nameval, &count);
293       res = headerGetEntry(h, RPMTAG_SOURCERPM, &type, 
294                            (void**)&srpmval, &count);
295       name = (char *)nameval;
296       srpm = (char *)srpmval;
297
298       if (res == 1) {
299          fprintf(idxfile, "%s %s\n", srpm, name);
300       }
301    }
302    // our additional tags
303    headerAddEntry(newHeader, CRPMTAG_DIRECTORY, RPM_STRING_TYPE,
304                   directory, 1);
305    headerAddEntry(newHeader, CRPMTAG_FILENAME, RPM_STRING_TYPE, 
306                   filename, 1);
307    headerAddEntry(newHeader, CRPMTAG_FILESIZE, RPM_INT32_TYPE,
308                   size, 1);
309       
310    // update description tags
311    if (updateInfo.find(string(filename)) != updateInfo.end()) {
312       const char *tmp;
313       string name = string(filename);
314       
315       tmp = updateInfo[name].summary.c_str();
316       headerAddEntry(newHeader, CRPMTAG_UPDATE_SUMMARY,
317                      RPM_STRING_TYPE,
318                      tmp, 1);
319       tmp = updateInfo[name].url.c_str();
320       headerAddEntry(newHeader, CRPMTAG_UPDATE_URL,
321                      RPM_STRING_TYPE,
322                      tmp, 1);
323       tmp = updateInfo[name].date.c_str();
324       headerAddEntry(newHeader, CRPMTAG_UPDATE_DATE,
325                      RPM_STRING_TYPE,
326                      tmp, 1);
327       tmp = updateInfo[name].importance.c_str();
328       headerAddEntry(newHeader, CRPMTAG_UPDATE_IMPORTANCE,
329                      RPM_STRING_TYPE,
330                      tmp, 1);
331    }
332    
333    return true;
334 }
335
336
337 #if defined(__APPLE__) || defined(__FREEBSD__)
338 int selectDirent(struct dirent *ent)
339 #else
340 int selectDirent(const struct dirent *ent)
341 #endif
342 {
343    int state = 0;
344    const char *p = ent->d_name;
345    
346    while (1) {
347       if (*p == '.') {
348           state = 1;
349       } else if (state == 1 && *p == 'r')
350           state++;
351       else if (state == 2 && *p == 'p')
352           state++;
353       else if (state == 3 && *p == 'm')
354           state++;
355       else if (state == 4 && *p == '\0')
356           return 1;
357       else if (*p == '\0')
358           return 0;
359       else
360           state = 0;
361       p++;
362    }
363 }
364
365
366 void usage()
367 {
368    cerr << "genpkglist " << VERSION << endl;
369    cerr << "usage: genpkglist [<options>] <dir> <suffix>" << endl;
370    cerr << "options:" << endl;
371    cerr << " --index <file>  file to write srpm index data to" << endl;
372    cerr << " --info <file>   file to read update info from" << endl;
373    cerr << " --meta <suffix> create package file list with given suffix" << endl;
374    cerr << " --bloat         do not strip the package file list. Needed for some" << endl;
375    cerr << "                 distributions that use non-automatically generated" << endl;
376    cerr << "                 file dependencies" << endl;
377    cerr << " --append        append to the package file list, don't overwrite" << endl;
378    cerr << " --progress      show a progress bar" << endl;
379    cerr << " --cachedir=DIR  use a custom directory for package md5sum cache"<<endl;
380 }
381
382
383 int main(int argc, char ** argv) 
384 {
385    string rpmsdir;
386    string pkglist_path;
387    FD_t outfd, fd;
388    struct dirent **dirEntries;
389    int entry_no, entry_cur;
390    map<string,UpdateInfo> updateInfo;
391    CachedMD5 *md5cache;
392    char *op_dir;
393    char *op_suf;
394    char *op_index = NULL;
395    char *op_update = NULL;
396    FILE *idxfile;
397    int i;
398    bool fullFileList = false;
399    bool progressBar = false;
400    const char *pkgListSuffix = NULL;
401    bool pkgListAppend = false;
402    
403    putenv("LC_ALL="); // Is this necessary yet (after i18n was supported)?
404    for (i = 1; i < argc; i++) {
405       if (strcmp(argv[i], "--index") == 0) {
406          i++;
407          if (i < argc) {
408             op_index = argv[i];
409          } else {
410             cout << "genpkglist: filename missing for option --index"<<endl;
411             exit(1);
412          }
413       } else if (strcmp(argv[i], "--info") == 0) {
414          i++;
415          if (i < argc) {
416             op_update = argv[i];
417          } else {
418             cout << "genpkglist: filename missing for option --info"<<endl;
419             exit(1);
420          }
421       } else if (strcmp(argv[i], "--bloat") == 0) {
422          fullFileList = true;
423       } else if (strcmp(argv[i], "--progress") == 0) {
424          progressBar = true;
425       } else if (strcmp(argv[i], "--append") == 0) {
426          pkgListAppend = true;
427       } else if (strcmp(argv[i], "--meta") == 0) {
428          i++;
429          if (i < argc) {
430             pkgListSuffix = argv[i];
431          } else {
432             cout << "genpkglist: argument missing for option --meta"<<endl;
433             exit(1);
434          }
435       } else if (strcmp(argv[i], "--cachedir") == 0) {
436          i++;
437          if (i < argc) {
438             _config->Set("Dir::Cache", argv[i]);
439          } else {
440             cout << "genpkglist: argument missing for option --cachedir"<<endl;
441             exit(1);
442          }
443       } else {
444          break;
445       }
446    }
447    if (argc - i > 0)
448        op_dir = argv[i++];
449    else {
450       usage();
451       exit(1);
452    }
453    if (argc - i > 0)
454        op_suf = argv[i++];
455    else {
456       usage();
457       exit(1);
458    }
459    if (argc != i) {
460       usage();
461    }
462    
463    if (op_update) {
464       if (!loadUpdateInfo(op_update, updateInfo)) {
465          cerr << "genpkglist: error reading update info from file " << op_update << endl;
466          _error->DumpErrors();
467          exit(1);
468       }
469    }
470    if (op_index) {
471       idxfile = fopen(op_index, "w+");
472       if (!idxfile) {
473          cerr << "genpkglist: could not open " << op_index << " for writing";
474          perror("");
475          exit(1);
476       }
477    } else {
478       idxfile = NULL;
479    }
480    
481    {
482       char cwd[PATH_MAX];
483       
484       if (getcwd(cwd, PATH_MAX) == 0)
485       {
486          cerr << argv[0] << strerror(errno) << endl;
487          exit(1);
488       }
489       if (*op_dir != '/') {
490          rpmsdir = string(cwd) + "/" + string(op_dir);
491       } else {
492          rpmsdir = string(op_dir);
493       }
494    }
495    pkglist_path = string(rpmsdir);
496    rpmsdir = rpmsdir + "/RPMS." + string(op_suf);
497
498    string dirtag = "RPMS." + string(op_suf);
499
500    entry_no = scandir(rpmsdir.c_str(), &dirEntries, selectDirent, alphasort);
501    if (entry_no < 0) {
502       cerr << "genpkglist: error opening directory " << rpmsdir << ":"
503           << strerror(errno);
504       return 1;
505    }
506    
507    if (chdir(rpmsdir.c_str()) != 0)
508    {
509       cerr << argv[0] << strerror(errno) << endl;
510       return 1;
511    }
512    
513    if (pkgListSuffix != NULL)
514            pkglist_path = pkglist_path + "/base/pkglist." + pkgListSuffix;
515    else
516            pkglist_path = pkglist_path + "/base/pkglist." + op_suf;
517    
518    
519    if (pkgListAppend == true && FileExists(pkglist_path)) {
520       outfd = Fopen(pkglist_path.c_str(), "a");
521    } else {
522       unlink(pkglist_path.c_str());
523       outfd = Fopen(pkglist_path.c_str(), "w+");
524    }
525    if (!outfd) {
526       cerr << "genpkglist: error creating file" << pkglist_path << ":"
527           << strerror(errno);
528       return 1;
529    }
530
531    md5cache = new CachedMD5(string(op_dir) + string(op_suf), "genpkglist");
532
533 #if RPM_VERSION >= 0x040100
534    rpmReadConfigFiles(NULL, NULL);
535    rpmts ts = rpmtsCreate();
536    rpmtsSetVSFlags(ts, (rpmVSFlags_e)-1);
537 #else
538    int isSource;
539 #endif   
540
541    for (entry_cur = 0; entry_cur < entry_no; entry_cur++) {
542       struct stat sb;
543
544       if (progressBar) {
545          if (entry_cur)
546             printf("\b\b\b\b\b\b\b\b\b\b");
547          printf(" %04i/%04i", entry_cur + 1, entry_no);
548          fflush(stdout);
549       }
550
551       if (stat(dirEntries[entry_cur]->d_name, &sb) < 0) {
552             cerr << "\nWarning: " << strerror(errno) << ": " << 
553                     dirEntries[entry_cur]->d_name << endl;
554             continue;
555       }
556
557       {
558          Header h;
559          int rc;
560          
561          fd = Fopen(dirEntries[entry_cur]->d_name, "r");
562
563          if (!fd) {
564             cerr << "\nWarning: " << strerror(errno) << ": " << 
565                     dirEntries[entry_cur]->d_name << endl;
566             continue;
567          }
568          
569 #if RPM_VERSION >= 0x040100
570          rc = rpmReadPackageFile(ts, fd, dirEntries[entry_cur]->d_name, &h);
571          if (rc == RPMRC_OK || rc == RPMRC_NOTTRUSTED || rc == RPMRC_NOKEY) {
572 #else
573          rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
574          if (rc == 0) {
575 #endif
576             Header newHeader;
577             char md5[34];
578             
579             newHeader = headerNew();
580             
581             copyFields(h, newHeader, idxfile, dirtag.c_str(),
582                        dirEntries[entry_cur]->d_name,
583                        sb.st_size, updateInfo, fullFileList);
584
585             md5cache->MD5ForFile(string(dirEntries[entry_cur]->d_name), 
586                                  sb.st_mtime, md5);
587             headerAddEntry(newHeader, CRPMTAG_MD5, RPM_STRING_TYPE, md5, 1);
588
589             headerWrite(outfd, newHeader, HEADER_MAGIC_YES);
590             
591             headerFree(newHeader);
592             headerFree(h);
593          } else {
594             cerr << "\nWarning: Skipping malformed RPM: " << 
595                     dirEntries[entry_cur]->d_name << endl;
596          }
597          Fclose(fd);
598       }
599    }
600
601    Fclose(outfd);
602
603 #if RPM_VERSION >= 0x040100
604    ts = rpmtsFree(ts);
605 #endif
606    
607    delete md5cache;
608
609    return 0;
610 }