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