- allow turning off cache regeneration on each run for rpm >= 4.4.4
[apt.git] / apt-pkg / rpm / rpmsystem.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: rpmsystem.cc,v 1.9 2002/11/25 18:25:28 niemeyer Exp $
4 /* ######################################################################
5
6    System - Abstraction for running on different systems.
7
8    RPM version of the system stuff
9    
10    ##################################################################### 
11  */
12                                                                         /*}}}*/
13 // Include Files                                                        /*{{{*/
14 #ifdef __GNUG__
15 #pragma implementation "apt-pkg/rpmsystem.h"
16 #endif
17
18 #include <config.h>
19
20 #ifdef HAVE_RPM
21
22 #include <apt-pkg/rpmsystem.h>
23 #include <apt-pkg/rpmversion.h>
24 #include <apt-pkg/rpmindexfile.h>
25 #include <apt-pkg/rpmpm.h>
26 #include <apt-pkg/rpmhandler.h>
27 #include <apt-pkg/configuration.h>
28 #include <apt-pkg/error.h>
29 #include <apt-pkg/fileutl.h>
30 #include <apt-pkg/rpmpackagedata.h>
31
32 #include <apti18n.h>
33     
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <dirent.h>
38 #include <fcntl.h>
39 #include <rpm/rpmlib.h>
40 #include <assert.h>
41 #include <time.h>
42                                                                         /*}}}*/
43 // for distrover
44 #if RPM_VERSION >= 0x040101
45 #include <rpmdb.h>
46 #endif
47
48 #if RPM_VERSION >= 0x040201
49 extern int _rpmds_nopromote;
50 #endif
51
52 rpmSystem rpmSys;
53
54 // System::rpmSystem - Constructor                                      /*{{{*/
55 // ---------------------------------------------------------------------
56 /* */
57 rpmSystem::rpmSystem()
58 {
59    LockCount = 0;
60    RpmDB = NULL;
61    StatusFile = NULL;
62    Label = "rpm interface";
63    VS = &rpmVS;
64 }
65                                                                         /*}}}*/
66 rpmSystem::~rpmSystem()
67 {
68    delete StatusFile;
69    delete RpmDB;
70 }
71
72 RPMDBHandler *rpmSystem::GetDBHandler()
73 {
74    if (RpmDB == NULL)
75       RpmDB = new RPMDBHandler();
76    return RpmDB;
77 }
78
79 bool rpmSystem::LockRead()
80 {
81    GetDBHandler();
82    if (_error->PendingError() == true)
83       return false;
84    return true;
85 }
86
87 //
88 // System::Lock - Get the lock                                          /*{{{*/
89 // ---------------------------------------------------------------------
90 /* this will open the rpm database through rpmlib, which will lock the db */
91 bool rpmSystem::Lock()
92 {
93    if (RpmDB != NULL && RpmDB->HasWriteLock() == false)
94    {
95       delete RpmDB;
96       RpmDB = NULL;
97    }
98    if (RpmDB == NULL)
99       RpmDB = new RPMDBHandler(true);
100    if (_error->PendingError() == true)
101       return false;
102    LockCount++;
103    return true;
104 }
105                                                                         /*}}}*/
106 // System::UnLock - Drop a lock                                         /*{{{*/
107 // ---------------------------------------------------------------------
108 /* Close the rpmdb, effectively dropping it's lock */
109 bool rpmSystem::UnLock(bool NoErrors)
110 {
111    if (LockCount == 0 && NoErrors == true)
112       return false;
113    if (LockCount < 1)
114       return _error->Error("Not locked");
115    if (--LockCount == 0)
116    {
117       delete RpmDB;
118       RpmDB = NULL;
119    }
120    return true;
121 }
122                                                                         /*}}}*/
123 // System::CreatePM - Create the underlying package manager             /*{{{*/
124 // ---------------------------------------------------------------------
125 /* */
126 pkgPackageManager *rpmSystem::CreatePM(pkgDepCache *Cache) const
127 {
128    if (_config->Find("RPM::PM", "internal") == "internal")
129       return new pkgRPMLibPM(Cache);
130    else
131       return new pkgRPMExtPM(Cache);
132 }
133                                                                         /*}}}*/
134 // System::Initialize - Setup the configuration space..                 /*{{{*/
135 // ---------------------------------------------------------------------
136 /* These are the rpm specific configuration variables.. */
137 bool rpmSystem::Initialize(Configuration &Cnf)
138 {
139    Cnf.CndSet("Dir::Bin::rpm","/bin/rpm");
140    Cnf.CndSet("Dir::Etc::rpmpriorities", "rpmpriorities");
141    Cnf.CndSet("Dir::Etc::translatelist", "translate.list");
142    Cnf.CndSet("Dir::Etc::translateparts", "translate.list.d");
143    Cnf.CndSet("Dir::State::prefetch", "prefetch");
144    Cnf.CndSet("Dir::Locale","/usr/share/locale");
145    Cnf.CndSet("Acquire::DistroID","Conectiva"); // hee hee
146    Cnf.CndSet("Acquire::CDROM::Mount", "/mnt/cdrom");
147    Cnf.CndSet("Acquire::CDROM::Copy-All", "true");
148
149    // Compatibility with obsoleted options
150    if (Cnf.Exists("APT::PostInstall"))
151    {
152       _error->Warning("Rename obsoleted option APT::PostInstall to APT::Post-Install");
153       Cnf.CndSet("APT::Post-Install::Clean",
154                  Cnf.Find("APT::PostInstall::Clean","false"));
155       Cnf.CndSet("APT::Post-Install::AutoClean",
156                  Cnf.Find("APT::PostInstall::AutoClean","false"));
157    }
158    const Configuration::Item *Top;
159    Top = _config->Tree("RPM::HoldPkgs");
160    if (Top != 0)
161    {
162       _error->Warning("Rename obsoleted option RPM::HoldPkgs to RPM::Hold");
163       for (Top = Top->Child; Top != 0; Top = Top->Next)
164          Cnf.Set("RPM::Hold::", Top->Value.c_str());
165    }
166    Top = _config->Tree("RPM::AllowedDupPkgs");
167    if (Top != 0)
168    {
169       _error->Warning("Rename obsoleted option RPM::AllowedDupPkgs to RPM::Allow-Duplicated");
170       for (Top = Top->Child; Top != 0; Top = Top->Next)
171          Cnf.Set("RPM::Allow-Duplicated::", Top->Value.c_str());
172    }
173    Top = _config->Tree("RPM::IgnorePkgs");
174    if (Top != 0)
175    {
176       _error->Warning("Rename obsoleted option RPM::IgnorePkgs to RPM::Ignore");
177       for (Top = (Top == 0?0:Top->Child); Top != 0; Top = Top->Next)
178          Cnf.Set("RPM::Ignore::", Top->Value.c_str());
179    }
180    if (Cnf.Exists("RPM::Force"))
181    {
182       _error->Warning("RPM::Force is obsoleted. Add \"--force\" to RPM::Options instead.");
183       if (Cnf.FindB("RPM::Force",false))
184          Cnf.Set("RPM::Options::", "--force");
185    }
186    if (Cnf.Exists("RPM::NoDeps"))
187    {
188       _error->Warning("RPM::NoDeps is obsoleted. Add \"--nodeps\" to RPM::Options and RPM::Erase-Options instead.");
189       if (Cnf.FindB("RPM::NoDeps",false))
190          Cnf.Set("RPM::Options::", "--nodeps");
191    }
192
193 #if RPM_VERSION >= 0x040201
194    const char *RPMOptions[] =
195    {
196       "RPM::Options",
197       "RPM::Install-Options",
198       "RPM::Erase-Options",
199       NULL,
200    };
201    int NoPromote = 1;
202    const char **Opt = RPMOptions;
203    while (*Opt && NoPromote)
204    {
205       Top = _config->Tree(*Opt);
206       if (Top != 0)
207       {
208          for (Top = Top->Child; Top != 0; Top = Top->Next)
209             if (Top->Value == "--promoteepoch") {
210                NoPromote = 0;
211                break;
212             }
213       }
214       Opt++;
215    }
216    _rpmds_nopromote = NoPromote;
217 #endif
218
219    return true;
220 }
221                                                                         /*}}}*/
222 // System::ArchiveSupported - Is a file format supported                /*{{{*/
223 // ---------------------------------------------------------------------
224 /* The standard name for a rpm is 'rpm'.. There are no seperate versions
225    of .rpm to worry about.. */
226 bool rpmSystem::ArchiveSupported(const char *Type)
227 {
228    if (strcmp(Type,"rpm") == 0)
229       return true;
230    return false;
231 }
232                                                                         /*}}}*/
233 // System::Score - Determine how Re**at'ish this sys is..               /*{{{*/
234 // ---------------------------------------------------------------------
235 /* Check some symptoms that this is a Re**at like system */
236 signed rpmSystem::Score(Configuration const &Cnf)
237 {
238    signed Score = 0;
239
240    rpmReadConfigFiles(NULL, NULL);
241    if (FileExists(RPMDBHandler::DataPath(false)))
242       Score += 10;
243    if (FileExists(Cnf.FindFile("Dir::Bin::rpm","/bin/rpm")) == true)
244       Score += 10;
245
246    return Score;
247 }
248
249 string rpmSystem::DistroVer(Configuration const &Cnf)
250 {
251    string DistroVerPkg = _config->Find("Apt::DistroVerPkg");
252    string DistroVersion = "";
253
254
255    if (! DistroVerPkg.empty()) {
256       rpmts ts;
257       char *version;
258       int type, count, rc;
259       rpmdbMatchIterator iter;
260
261       ts = rpmtsCreate();
262       rpmtsSetVSFlags(ts, (rpmVSFlags_e)-1);
263       rpmtsSetRootDir(ts, NULL);
264       rc = rpmtsOpenDB(ts, O_RDWR);
265
266       Header hdr;
267       iter = rpmtsInitIterator(ts, (rpmTag)RPMDBI_LABEL, DistroVerPkg.c_str(), 0);
268       while ((hdr = rpmdbNextIterator(iter)) != NULL) {
269          headerGetEntry(hdr, RPMTAG_VERSION, &type, (void **)&version, &count);
270          DistroVersion = version;
271          break;
272       }
273       rpmdbFreeIterator(iter);
274       rpmtsFree(ts);
275    }
276    
277    return DistroVersion;
278 }
279
280                                                                         /*}}}*/
281 // System::AddStatusFiles - Register the status files                   /*{{{*/
282 // ---------------------------------------------------------------------
283 /* */
284 bool rpmSystem::AddStatusFiles(vector<pkgIndexFile *> &List)
285 {
286    if (StatusFile == NULL)
287       StatusFile = new rpmDatabaseIndex();
288    List.push_back(StatusFile);
289    return true;
290 }
291                                                                         /*}}}*/
292 // System::AddSourceFiles - Register aditional source files             /*{{{*/
293 // ---------------------------------------------------------------------
294 /* */
295 bool rpmSystem::AddSourceFiles(vector<pkgIndexFile *> &List)
296 {
297    const Configuration::Item *Top;
298    Top = _config->Tree("APT::Arguments");
299    if (Top != 0)
300    {
301       for (Top = Top->Child; Top != 0; Top = Top->Next) {
302          const string &S = Top->Value;
303          if (FileExists(S) && flExtension(S) == "rpm")
304          {
305             if (S.length() > 8 && string(S, S.length()-8) == ".src.rpm")
306                List.push_back(new rpmSingleSrcIndex(S));
307             else
308                List.push_back(new rpmSinglePkgIndex(S));
309          }
310       }
311    }
312    return true;
313 }
314                                                                         /*}}}*/
315 #ifdef OLD_FILEDEPS
316 static void gatherFileDependencies(map<string,int> &filedeps, Header header)
317 {
318    int type, count;
319    char **namel;
320    //char **verl;
321    //int *flagl;
322    int res;
323    
324    res = headerGetEntry(header, RPMTAG_REQUIRENAME, &type,
325                         (void **)&namel, &count);
326    /*
327    res = headerGetEntry(header, RPMTAG_REQUIREVERSION, &type, 
328                         (void **)&verl, &count);
329    res = headerGetEntry(header, RPMTAG_REQUIREFLAGS, &type,
330                         (void **)&flagl, &count);
331    */
332    
333    while (count--) 
334    {
335       if (*namel[count] == '/')
336          filedeps[string(namel[count])] = 1;
337    }
338 }
339 #endif
340
341
342 #ifdef OLD_BESTARCH
343 bool rpmSystem::processIndexFile(rpmIndexFile *Index,OpProgress &Progress)
344 {
345    Header hdr;
346    map<string,string> archmap;
347    
348    RPMHandler *Handler = Index->CreateHandler();
349    if (_error->PendingError() == true)
350       return false;
351
352    Progress.SubProgress(0, Index->Describe());
353    
354    Handler->Rewind();
355
356    while (Handler->Skip() == true)
357    {
358       int type, count, res;
359       char *arch;
360
361       hdr = Handler->GetHeader();
362       if (!hdr)
363           break;
364
365 #ifdef OLD_FILEDEPS
366       gatherFileDependencies(FileDeps, hdr);
367 #endif
368       
369       /*
370        * Make it so that for each version, we keep track of the best
371        * architecture.
372        */
373       res = headerGetEntry(hdr, RPMTAG_ARCH, &type,
374                            (void **)&arch, &count);
375       assert(type == RPM_STRING_TYPE);
376       if (res) 
377       {
378          char *name;
379          char *version;
380          char *release;
381          int_32 *epoch;
382          int res;
383          char buf[256];
384          
385          headerGetEntry(hdr, RPMTAG_NAME, &type,
386                         (void **)&name, &count);
387          headerGetEntry(hdr, RPMTAG_VERSION, &type,
388                         (void **)&version, &count);
389          headerGetEntry(hdr, RPMTAG_RELEASE, &type,
390                         (void **)&release, &count);
391          res = headerGetEntry(hdr, RPMTAG_EPOCH, &type,
392                               (void **)&epoch, &count);
393          
394          if (res == 1)
395             snprintf(buf, sizeof(buf), "%i:%s-%s", *epoch, version, release);
396          else
397             snprintf(buf, sizeof(buf), "%s-%s", version, release);
398          string n = string(name)+"#"+string(buf);
399          
400          if (archmap.find(n) != archmap.end()) 
401          {
402             if (strcmp(archmap[n].c_str(), arch) != 0) 
403             {
404                int a = rpmMachineScore(RPM_MACHTABLE_INSTARCH, archmap[n].c_str());
405                int b = rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch);
406                
407                if (b < a) 
408                {
409                   MultiArchPkgs[n] = string(arch);
410                   // this is a multiarch pkg
411                   archmap[n] = MultiArchPkgs[n];
412                }
413                else 
414                {
415                   MultiArchPkgs[n] = archmap[n];
416                }
417             }
418          }
419          else 
420          {
421             archmap[n] = string(arch);
422          }
423       }
424    }
425    
426    if (Handler->IsDatabase() == false)
427        delete Handler;
428
429    return true;
430 }
431
432
433 bool rpmSystem::PreProcess(pkgIndexFile **Start,pkgIndexFile **End,
434                            OpProgress &Progress)
435 {
436    string ListDir = _config->FindDir("Dir::State::lists");
437    unsigned total, complete;
438    unsigned long TotalSize = 0, CurrentSize = 0;
439
440    // calculate size of files
441    
442    for (; Start != End; Start++)
443    {
444       if ((*Start)->HasPackages() == false)
445           continue;
446       
447       if ((*Start)->Exists() == false)
448           continue;
449       
450       unsigned long Size = (*Start)->Size();
451       Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading Package Lists"));
452       CurrentSize += Size;
453
454       if (processIndexFile((rpmIndexFile*)*Start,Progress) == false)
455          return false;
456    }
457   
458    return true;
459 }
460
461 string rpmSystem::BestArchForPackage(string Pkg)
462 {
463    if (MultiArchPkgs.find(Pkg) != MultiArchPkgs.end())
464       return MultiArchPkgs[Pkg];
465    else
466        return string();
467 }
468 #endif
469
470 #ifdef OLD_FILEDEPS
471 bool rpmSystem::IsFileDep(string File)
472 {
473    return (FileDeps.find(File) != FileDeps.end());
474 }
475 #endif
476
477 // System::FindIndex - Get an index file for status files               /*{{{*/
478 // ---------------------------------------------------------------------
479 /* */
480 bool rpmSystem::FindIndex(pkgCache::PkgFileIterator File,
481                           pkgIndexFile *&Found) const
482 {
483    if (StatusFile == 0)
484       return false;
485    if (StatusFile->FindInCache(*File.Cache()) == File)
486    {
487       Found = StatusFile;
488       return true;
489    }
490    
491    return false;
492 }
493                                                                         /*}}}*/
494
495 // System::ProcessCache - Do specific changes in the cache              /*{{{*/
496 // ---------------------------------------------------------------------
497 /* */
498 bool rpmSystem::ProcessCache(pkgDepCache &Cache,pkgProblemResolver &Fix)
499 {
500    RPMPackageData *rpmdata = RPMPackageData::Singleton();
501    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
502    {
503       // Ignore virtual packages
504       if (I->VersionList == 0)
505          continue;
506          
507       // Do package holding
508       if (I->CurrentVer != 0)
509       {
510          if (rpmdata->HoldPackage(I.Name()))
511          {
512             Cache.MarkKeep(I);
513             Fix.Protect(I);
514          }
515       }
516    }
517    return true;
518 }
519                                                                         /*}}}*/
520
521 // System::IgnoreDep - Check if this dependency should be ignored       /*{{{*/
522 // ---------------------------------------------------------------------
523 /* For strong hearts only */
524 bool rpmSystem::IgnoreDep(pkgVersioningSystem &VS,pkgCache::DepIterator &Dep)
525 {
526    RPMPackageData *rpmdata = RPMPackageData::Singleton();
527    return rpmdata->IgnoreDep(VS,Dep);
528 }
529                                                                         /*}}}*/
530
531 // System::CacheBuilt - free caches used during cache build             /*{{{*/
532 // ---------------------------------------------------------------------
533 /* */
534 void rpmSystem::CacheBuilt()
535 {
536    RPMPackageData *rpmdata = RPMPackageData::Singleton();
537    rpmdata->CacheBuilt();
538 }
539                                                                         /*}}}*/
540
541 // System::OptionsHash - Identify options which change the cache        /*{{{*/
542 // ---------------------------------------------------------------------
543 /* */
544 static void HashString(unsigned long &Hash, const char *Str)
545 {
546    for (const char *I = Str; *I != 0; I++)
547       Hash = 5*Hash + *I;
548 }
549 static void HashEnv(unsigned long &Hash, const char *Name)
550 {
551    const char *Value = getenv(Name);
552    if (Value)
553       HashString(Hash, Value);
554 }
555 static void HashOption(unsigned long &Hash, const char *Name)
556 {
557    const Configuration::Item *Top = _config->Tree(Name);
558    if (Top != 0)
559       HashString(Hash, Top->Value.c_str());
560 }
561 static void HashOptionTree(unsigned long &Hash, const char *Name)
562 {
563    const Configuration::Item *Top = _config->Tree(Name);
564    if (Top != 0)
565       for (Top = Top->Child; Top != 0; Top = Top->Next)
566          HashString(Hash, Top->Value.c_str());
567 }
568 static void HashOptionFile(unsigned long &Hash, const char *Name)
569 {
570    string FileName = _config->FindFile(Name);
571    struct stat st;
572    stat(FileName.c_str(), &st);
573    Hash += st.st_mtime;
574 }
575
576 static void HashTime(unsigned long &Hash)
577 {
578    Hash += time(NULL);
579 }
580
581 unsigned long rpmSystem::OptionsHash() const
582 {
583    unsigned long Hash = 0;
584    HashOption(Hash, "RPM::Architecture");
585    HashOptionTree(Hash, "RPM::Allow-Duplicated");
586    HashOptionTree(Hash, "RPM::MultiArch");
587    HashOptionTree(Hash, "RPM::Ignore");
588    HashOptionFile(Hash, "Dir::Etc::rpmpriorities");
589    HashEnv(Hash, "LANG");
590    HashEnv(Hash, "LC_ALL");
591    HashEnv(Hash, "LC_MESSAGES");
592 #if RPM_VERSION >= 0x040404
593    // This is really draconian but until apt can made somehow deal with
594    // runtime dependencies the cache has to be rebuilt for each run for
595    // accuracy. Allow turning it off via configuration if not needed.
596    if (_config->FindB("RPM::RuntimeDeps", true) == true)
597       HashTime(Hash);
598 #endif
599    return Hash;
600 }
601                                                                         /*}}}*/
602
603 #endif /* HAVE_RPM */
604
605 // vim:sts=3:sw=3