- add method to find a package by name from rpmdb
[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()
250 {
251    string DistroVerPkg = _config->Find("APT::DistroVerPkg", "");
252    if (DistroVerPkg.empty() || LockRead() == false)
253       return "";
254
255    string DistroVersion = "";
256    if (RpmDB->JumpByName(DistroVerPkg) == true) {
257       DistroVersion = RpmDB->Version();
258    } else {
259       _error->Error(_("Unable to determine version for package %s"),
260                       DistroVerPkg.c_str());
261    }
262    UnLock(true);
263
264    return DistroVersion;
265 }
266
267                                                                         /*}}}*/
268 // System::AddStatusFiles - Register the status files                   /*{{{*/
269 // ---------------------------------------------------------------------
270 /* */
271 bool rpmSystem::AddStatusFiles(vector<pkgIndexFile *> &List)
272 {
273    if (StatusFile == NULL)
274       StatusFile = new rpmDatabaseIndex();
275    List.push_back(StatusFile);
276    return true;
277 }
278                                                                         /*}}}*/
279 // System::AddSourceFiles - Register aditional source files             /*{{{*/
280 // ---------------------------------------------------------------------
281 /* */
282 bool rpmSystem::AddSourceFiles(vector<pkgIndexFile *> &List)
283 {
284    const Configuration::Item *Top;
285    Top = _config->Tree("APT::Arguments");
286    if (Top != 0)
287    {
288       for (Top = Top->Child; Top != 0; Top = Top->Next) {
289          const string &S = Top->Value;
290          if (FileExists(S) && flExtension(S) == "rpm")
291          {
292             if (S.length() > 8 && string(S, S.length()-8) == ".src.rpm")
293                List.push_back(new rpmSingleSrcIndex(S));
294             else
295                List.push_back(new rpmSinglePkgIndex(S));
296          }
297       }
298    }
299    return true;
300 }
301                                                                         /*}}}*/
302 #ifdef OLD_FILEDEPS
303 static void gatherFileDependencies(map<string,int> &filedeps, Header header)
304 {
305    int type, count;
306    char **namel;
307    //char **verl;
308    //int *flagl;
309    int res;
310    
311    res = headerGetEntry(header, RPMTAG_REQUIRENAME, &type,
312                         (void **)&namel, &count);
313    /*
314    res = headerGetEntry(header, RPMTAG_REQUIREVERSION, &type, 
315                         (void **)&verl, &count);
316    res = headerGetEntry(header, RPMTAG_REQUIREFLAGS, &type,
317                         (void **)&flagl, &count);
318    */
319    
320    while (count--) 
321    {
322       if (*namel[count] == '/')
323          filedeps[string(namel[count])] = 1;
324    }
325 }
326 #endif
327
328
329 #ifdef OLD_BESTARCH
330 bool rpmSystem::processIndexFile(rpmIndexFile *Index,OpProgress &Progress)
331 {
332    Header hdr;
333    map<string,string> archmap;
334    
335    RPMHandler *Handler = Index->CreateHandler();
336    if (_error->PendingError() == true)
337       return false;
338
339    Progress.SubProgress(0, Index->Describe());
340    
341    Handler->Rewind();
342
343    while (Handler->Skip() == true)
344    {
345       int type, count, res;
346       char *arch;
347
348       hdr = Handler->GetHeader();
349       if (!hdr)
350           break;
351
352 #ifdef OLD_FILEDEPS
353       gatherFileDependencies(FileDeps, hdr);
354 #endif
355       
356       /*
357        * Make it so that for each version, we keep track of the best
358        * architecture.
359        */
360       res = headerGetEntry(hdr, RPMTAG_ARCH, &type,
361                            (void **)&arch, &count);
362       assert(type == RPM_STRING_TYPE);
363       if (res) 
364       {
365          char *name;
366          char *version;
367          char *release;
368          int_32 *epoch;
369          int res;
370          char buf[256];
371          
372          headerGetEntry(hdr, RPMTAG_NAME, &type,
373                         (void **)&name, &count);
374          headerGetEntry(hdr, RPMTAG_VERSION, &type,
375                         (void **)&version, &count);
376          headerGetEntry(hdr, RPMTAG_RELEASE, &type,
377                         (void **)&release, &count);
378          res = headerGetEntry(hdr, RPMTAG_EPOCH, &type,
379                               (void **)&epoch, &count);
380          
381          if (res == 1)
382             snprintf(buf, sizeof(buf), "%i:%s-%s", *epoch, version, release);
383          else
384             snprintf(buf, sizeof(buf), "%s-%s", version, release);
385          string n = string(name)+"#"+string(buf);
386          
387          if (archmap.find(n) != archmap.end()) 
388          {
389             if (strcmp(archmap[n].c_str(), arch) != 0) 
390             {
391                int a = rpmMachineScore(RPM_MACHTABLE_INSTARCH, archmap[n].c_str());
392                int b = rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch);
393                
394                if (b < a) 
395                {
396                   MultiArchPkgs[n] = string(arch);
397                   // this is a multiarch pkg
398                   archmap[n] = MultiArchPkgs[n];
399                }
400                else 
401                {
402                   MultiArchPkgs[n] = archmap[n];
403                }
404             }
405          }
406          else 
407          {
408             archmap[n] = string(arch);
409          }
410       }
411    }
412    
413    if (Handler->IsDatabase() == false)
414        delete Handler;
415
416    return true;
417 }
418
419
420 bool rpmSystem::PreProcess(pkgIndexFile **Start,pkgIndexFile **End,
421                            OpProgress &Progress)
422 {
423    string ListDir = _config->FindDir("Dir::State::lists");
424    unsigned total, complete;
425    unsigned long TotalSize = 0, CurrentSize = 0;
426
427    // calculate size of files
428    
429    for (; Start != End; Start++)
430    {
431       if ((*Start)->HasPackages() == false)
432           continue;
433       
434       if ((*Start)->Exists() == false)
435           continue;
436       
437       unsigned long Size = (*Start)->Size();
438       Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading Package Lists"));
439       CurrentSize += Size;
440
441       if (processIndexFile((rpmIndexFile*)*Start,Progress) == false)
442          return false;
443    }
444   
445    return true;
446 }
447
448 string rpmSystem::BestArchForPackage(string Pkg)
449 {
450    if (MultiArchPkgs.find(Pkg) != MultiArchPkgs.end())
451       return MultiArchPkgs[Pkg];
452    else
453        return string();
454 }
455 #endif
456
457 #ifdef OLD_FILEDEPS
458 bool rpmSystem::IsFileDep(string File)
459 {
460    return (FileDeps.find(File) != FileDeps.end());
461 }
462 #endif
463
464 // System::FindIndex - Get an index file for status files               /*{{{*/
465 // ---------------------------------------------------------------------
466 /* */
467 bool rpmSystem::FindIndex(pkgCache::PkgFileIterator File,
468                           pkgIndexFile *&Found) const
469 {
470    if (StatusFile == 0)
471       return false;
472    if (StatusFile->FindInCache(*File.Cache()) == File)
473    {
474       Found = StatusFile;
475       return true;
476    }
477    
478    return false;
479 }
480                                                                         /*}}}*/
481
482 // System::ProcessCache - Do specific changes in the cache              /*{{{*/
483 // ---------------------------------------------------------------------
484 /* */
485 bool rpmSystem::ProcessCache(pkgDepCache &Cache,pkgProblemResolver &Fix)
486 {
487    RPMPackageData *rpmdata = RPMPackageData::Singleton();
488    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
489    {
490       // Ignore virtual packages
491       if (I->VersionList == 0)
492          continue;
493          
494       // Do package holding
495       if (I->CurrentVer != 0)
496       {
497          if (rpmdata->HoldPackage(I.Name()))
498          {
499             Cache.MarkKeep(I);
500             Fix.Protect(I);
501          }
502       }
503    }
504    return true;
505 }
506                                                                         /*}}}*/
507
508 // System::IgnoreDep - Check if this dependency should be ignored       /*{{{*/
509 // ---------------------------------------------------------------------
510 /* For strong hearts only */
511 bool rpmSystem::IgnoreDep(pkgVersioningSystem &VS,pkgCache::DepIterator &Dep)
512 {
513    RPMPackageData *rpmdata = RPMPackageData::Singleton();
514    return rpmdata->IgnoreDep(VS,Dep);
515 }
516                                                                         /*}}}*/
517
518 // System::CacheBuilt - free caches used during cache build             /*{{{*/
519 // ---------------------------------------------------------------------
520 /* */
521 void rpmSystem::CacheBuilt()
522 {
523    RPMPackageData *rpmdata = RPMPackageData::Singleton();
524    rpmdata->CacheBuilt();
525 }
526                                                                         /*}}}*/
527
528 // System::OptionsHash - Identify options which change the cache        /*{{{*/
529 // ---------------------------------------------------------------------
530 /* */
531 static void HashString(unsigned long &Hash, const char *Str)
532 {
533    for (const char *I = Str; *I != 0; I++)
534       Hash = 5*Hash + *I;
535 }
536 static void HashEnv(unsigned long &Hash, const char *Name)
537 {
538    const char *Value = getenv(Name);
539    if (Value)
540       HashString(Hash, Value);
541 }
542 static void HashOption(unsigned long &Hash, const char *Name)
543 {
544    const Configuration::Item *Top = _config->Tree(Name);
545    if (Top != 0)
546       HashString(Hash, Top->Value.c_str());
547 }
548 static void HashOptionTree(unsigned long &Hash, const char *Name)
549 {
550    const Configuration::Item *Top = _config->Tree(Name);
551    if (Top != 0)
552       for (Top = Top->Child; Top != 0; Top = Top->Next)
553          HashString(Hash, Top->Value.c_str());
554 }
555 static void HashOptionFile(unsigned long &Hash, const char *Name)
556 {
557    string FileName = _config->FindFile(Name);
558    struct stat st;
559    stat(FileName.c_str(), &st);
560    Hash += st.st_mtime;
561 }
562
563 #if RPM_VERSION >= 0x040404
564 static void HashTime(unsigned long &Hash)
565 {
566    Hash += time(NULL);
567 }
568 #endif
569
570 unsigned long rpmSystem::OptionsHash() const
571 {
572    unsigned long Hash = 0;
573    HashOption(Hash, "RPM::Architecture");
574    HashOptionTree(Hash, "RPM::Allow-Duplicated");
575    HashOptionTree(Hash, "RPM::MultiArch");
576    HashOptionTree(Hash, "RPM::Ignore");
577    HashOptionFile(Hash, "Dir::Etc::rpmpriorities");
578    HashEnv(Hash, "LANG");
579    HashEnv(Hash, "LC_ALL");
580    HashEnv(Hash, "LC_MESSAGES");
581 #if RPM_VERSION >= 0x040404
582    // This is really draconian but until apt can made somehow deal with
583    // runtime dependencies the cache has to be rebuilt for each run for
584    // accuracy. Allow turning it off via configuration if not needed.
585    if (_config->FindB("RPM::RuntimeDeps", true) == true)
586       HashTime(Hash);
587 #endif
588    return Hash;
589 }
590                                                                         /*}}}*/
591
592 #endif /* HAVE_RPM */
593
594 // vim:sts=3:sw=3