- count installs vs upgrades + repackages correctly for progress bar usage
[apt.git] / apt-pkg / rpm / rpmpm.cc
1 // -*- mode: c++; mode: fold -*-
2 // Description                                                          /*{{{*/
3 /* ######################################################################
4
5    RPM Package Manager - Provide an interface to rpm
6    
7    ##################################################################### 
8  */
9                                                                         /*}}}*/
10 // Includes                                                             /*{{{*/
11 #ifdef __GNUG__
12 #pragma implementation "apt-pkg/rpmpm.h"
13 #endif
14
15 #include <config.h>
16
17 #ifdef HAVE_RPM
18
19 #include <apt-pkg/rpmpm.h>
20 #include <apt-pkg/error.h>
21 #include <apt-pkg/configuration.h>
22 #include <apt-pkg/luaiface.h>
23 #include <apt-pkg/depcache.h>
24
25 #include <apti18n.h>
26
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <signal.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <iostream>
36
37 #if RPM_VERSION >= 0x040100
38 #include <rpm/rpmdb.h>
39 #else
40 #define rpmpsPrint(a,b) rpmProblemSetPrint(a,b)
41 #define rpmpsFree(a) rpmProblemSetFree(a)
42 #define rpmReadPackageFile(a,b,c,d) rpmReadPackageHeader(b,d,0,NULL,NULL)
43 #if RPM_VERSION < 0x040000
44 #define rpmtransFlags int
45 #define rpmprobFilterFlags int
46 #endif
47 #endif
48 #include "rpmshowprogress.h"
49
50 // RPMPM::pkgRPMPM - Constructor                                        /*{{{*/
51 // ---------------------------------------------------------------------
52 /* */
53 pkgRPMPM::pkgRPMPM(pkgDepCache *Cache) : pkgPackageManager(Cache)
54 {
55 }
56                                                                         /*}}}*/
57 // RPMPM::pkgRPMPM - Destructor                                 /*{{{*/
58 // ---------------------------------------------------------------------
59 /* */
60 pkgRPMPM::~pkgRPMPM()
61 {
62 }
63                                                                         /*}}}*/
64 // RPMPM::Install - Install a package                                   /*{{{*/
65 // ---------------------------------------------------------------------
66 /* Add an install operation to the sequence list */
67 bool pkgRPMPM::Install(PkgIterator Pkg,string File)
68 {
69    if (File.empty() == true || Pkg.end() == true)
70       return _error->Error(_("Internal Error, No file name for %s"),Pkg.Name());
71
72    List.push_back(Item(Item::Install,Pkg,File));
73    return true;
74 }
75                                                                         /*}}}*/
76 // RPMPM::Configure - Configure a package                               /*{{{*/
77 // ---------------------------------------------------------------------
78 /* Add a configure operation to the sequence list */
79 bool pkgRPMPM::Configure(PkgIterator Pkg)
80 {
81    if (Pkg.end() == true) {
82       return false;
83    }
84    
85    List.push_back(Item(Item::Configure,Pkg));
86    return true;
87 }
88                                                                         /*}}}*/
89 // RPMPM::Remove - Remove a package                                     /*{{{*/
90 // ---------------------------------------------------------------------
91 /* Add a remove operation to the sequence list */
92 bool pkgRPMPM::Remove(PkgIterator Pkg,bool Purge)
93 {
94    if (Pkg.end() == true)
95       return false;
96    
97    if (Purge == true)
98       List.push_back(Item(Item::Purge,Pkg));
99    else
100       List.push_back(Item(Item::Remove,Pkg));
101    return true;
102 }
103                                                                         /*}}}*/
104 // RPMPM::RunScripts - Run a set of scripts                             /*{{{*/
105 // ---------------------------------------------------------------------
106 /* This looks for a list of script sto run from the configuration file,
107    each one is run with system from a forked child. */
108 bool pkgRPMPM::RunScripts(const char *Cnf)
109 {
110    Configuration::Item const *Opts = _config->Tree(Cnf);
111    if (Opts == 0 || Opts->Child == 0)
112       return true;
113    Opts = Opts->Child;
114
115    bool error = false;
116    for (; Opts != 0; Opts = Opts->Next)
117    {
118       if (Opts->Value.empty() == true)
119          continue;
120                 
121       // Purified Fork for running the script
122       pid_t Process = ExecFork();      
123       if (Process == 0)
124       {
125          if (chdir("/tmp") != 0)
126             _exit(100);
127
128          const char *Args[4];
129          Args[0] = "/bin/sh";
130          Args[1] = "-c";
131          Args[2] = Opts->Value.c_str();
132          Args[3] = 0;
133          execv(Args[0],(char **)Args);
134          _exit(100);
135       }
136       
137       // Clean up the sub process
138       if (ExecWait(Process,Opts->Value.c_str()) == false) {
139          _error->Error(_("Problem executing scripts %s '%s'"),Cnf,
140                        Opts->Value.c_str());
141          error = true;
142       }
143    }
144  
145    // Restore sig int/quit
146    signal(SIGQUIT,SIG_DFL);
147    signal(SIGINT,SIG_DFL);   
148
149    if (error)
150       return _error->Error(_("Sub-process returned an error code"));
151    
152    return true;
153 }
154
155                                                                         /*}}}*/
156 // RPMPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This looks for a list of scripts to run from the configuration file
159    each one is run and is fed on standard input a list of all .deb files
160    that are due to be installed. */
161 bool pkgRPMPM::RunScriptsWithPkgs(const char *Cnf)
162 {
163    Configuration::Item const *Opts = _config->Tree(Cnf);
164    if (Opts == 0 || Opts->Child == 0)
165       return true;
166    Opts = Opts->Child;
167    
168    for (; Opts != 0; Opts = Opts->Next)
169    {
170       if (Opts->Value.empty() == true)
171          continue;
172                 
173       // Create the pipes
174       int Pipes[2];
175       if (pipe(Pipes) != 0)
176          return _error->Errno("pipe",_("Failed to create IPC pipe to subprocess"));
177       SetCloseExec(Pipes[0],true);
178       SetCloseExec(Pipes[1],true);
179       
180       // Purified Fork for running the script
181       pid_t Process = ExecFork();      
182       if (Process == 0)
183       {
184          // Setup the FDs
185          dup2(Pipes[0],STDIN_FILENO);
186          SetCloseExec(STDOUT_FILENO,false);
187          SetCloseExec(STDIN_FILENO,false);      
188          SetCloseExec(STDERR_FILENO,false);
189
190          const char *Args[4];
191          Args[0] = "/bin/sh";
192          Args[1] = "-c";
193          Args[2] = Opts->Value.c_str();
194          Args[3] = 0;
195          execv(Args[0],(char **)Args);
196          _exit(100);
197       }
198       close(Pipes[0]);
199       FileFd Fd(Pipes[1]);
200
201       // Feed it the filenames.
202       for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
203       {
204          // Only deal with packages to be installed from .rpm
205          if (I->Op != Item::Install)
206             continue;
207
208          // No errors here..
209          if (I->File[0] != '/')
210             continue;
211          
212          /* Feed the filename of each package that is pending install
213             into the pipe. */
214          if (Fd.Write(I->File.c_str(),I->File.length()) == false || 
215              Fd.Write("\n",1) == false)
216          {
217             kill(Process,SIGINT);           
218             Fd.Close();   
219             ExecWait(Process,Opts->Value.c_str(),true);
220             return _error->Error(_("Failure running script %s"),Opts->Value.c_str());
221          }
222       }
223       Fd.Close();
224       
225       // Clean up the sub process
226       if (ExecWait(Process,Opts->Value.c_str()) == false)
227          return _error->Error(_("Failure running script %s"),Opts->Value.c_str());
228    }
229
230    return true;
231 }
232
233                                                                         /*}}}*/
234
235
236 // RPMPM::Go - Run the sequence                                         /*{{{*/
237 // ---------------------------------------------------------------------
238 /* This globs the operations and calls rpm */
239 bool pkgRPMPM::Go()
240 {
241    if (List.empty() == true)
242       return true;
243
244    if (RunScripts("RPM::Pre-Invoke") == false)
245       return false;
246
247    if (RunScriptsWithPkgs("RPM::Pre-Install-Pkgs") == false)
248       return false;
249    
250    vector<const char*> install_or_upgrade;
251    vector<const char*> install;
252    vector<const char*> upgrade;
253    vector<const char*> uninstall;
254    vector<pkgCache::Package*> pkgs_install;
255    vector<pkgCache::Package*> pkgs_uninstall;
256
257    vector<char*> unalloc;
258    
259    for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
260    {
261       string Name = I->Pkg.Name();
262       string RealName = Name;
263       string::size_type loc;
264
265       switch (I->Op)
266       {
267       case Item::Purge:
268       case Item::Remove:
269          // Unmunge our package names so rpm can find them...
270          if ((loc = Name.rfind(".32bit", Name.length())) != string::npos) {
271             RealName = Name.substr(0,loc);
272          } else if ((loc = Name.rfind("#", Name.length())) != string::npos) {
273             RealName = Name.substr(0,loc) + "-" + I->Pkg.CurrentVer().VerStr();
274          }
275 #if RPM_VERSION >= 0x040202
276          // This is needed for removal to work on multilib packages, but old
277          // rpm versions don't support name.arch in RPMDBI_LABEL, oh well...
278          RealName = RealName + "." + I->Pkg.CurrentVer().Arch();
279 #endif
280          uninstall.push_back(strdup(RealName.c_str()));
281          unalloc.push_back(strdup(RealName.c_str()));
282          pkgs_uninstall.push_back(I->Pkg);
283          break;
284
285        case Item::Configure:
286          break;
287
288        case Item::Install:
289          if ((loc = Name.rfind("#", Name.length())) != string::npos) {
290             RealName = Name.substr(0,loc);
291          } 
292          if (Name != RealName) {
293             PkgIterator Pkg = Cache.FindPkg(RealName);
294             PrvIterator Prv = Pkg.ProvidesList();
295             bool Installed = false;
296             for (; Prv.end() == false; Prv++) {
297                if (Prv.OwnerPkg().CurrentVer().end() == false) {
298                   Installed = true;
299                   break;
300                }
301             }
302             // This looks backwards but it's supposed to "fix problems where a 
303             // package with a different name is being installed with 
304             // Allow-Duplicated and requires additional dependencies, but there's 
305             // no other package with the same name in the system."
306             if (Installed)
307                install.push_back(I->File.c_str());
308             else
309                upgrade.push_back(I->File.c_str());
310          } else {
311             // perform pure installs on non-installed normal packages, not upgrades
312             if (I->Pkg->CurrentVer != NULL) {
313                upgrade.push_back(I->File.c_str());
314             } else {
315                install.push_back(I->File.c_str());
316             }
317          }
318          install_or_upgrade.push_back(I->File.c_str());
319          pkgs_install.push_back(I->Pkg);
320          break;
321           
322        default:
323          return _error->Error(_("Unknown pkgRPMPM operation."));
324       }
325    }
326
327    bool Ret = true;
328
329 #ifdef WITH_LUA
330    if (_lua->HasScripts("Scripts::PM::Pre") == true) {
331       _lua->SetGlobal("files_install", install_or_upgrade);
332       _lua->SetGlobal("names_remove", uninstall);
333       _lua->SetGlobal("pkgs_install", pkgs_install);
334       _lua->SetGlobal("pkgs_remove", pkgs_uninstall);
335       _lua->SetDepCache(&Cache);
336       _lua->RunScripts("Scripts::PM::Pre");
337       _lua->ResetCaches();
338       _lua->ResetGlobals();
339       if (_error->PendingError() == true) {
340          _error->DumpErrors();
341          Ret = false;
342          goto exit;
343       }
344    }
345 #endif
346
347    if (Process(install, upgrade, uninstall) == false)
348       Ret = false;
349
350 #ifdef WITH_LUA
351    if (_lua->HasScripts("Scripts::PM::Post") == true) {
352       _lua->SetGlobal("files_install", install_or_upgrade);
353       _lua->SetGlobal("names_remove", uninstall);
354       _lua->SetGlobal("pkgs_install", pkgs_install);
355       _lua->SetGlobal("pkgs_remove", pkgs_uninstall);
356       _lua->SetGlobal("transaction_success", Ret);
357       _lua->SetDepCache(&Cache);
358       _lua->RunScripts("Scripts::PM::Post");
359       _lua->ResetCaches();
360       _lua->ResetGlobals();
361       if (_error->PendingError() == true) {
362          _error->DumpErrors();
363          Ret = false;
364          goto exit;
365       }
366    }
367 #endif
368
369    
370    if (Ret == true)
371       Ret = RunScripts("RPM::Post-Invoke");
372
373 exit:
374    for (vector<char *>::iterator I = unalloc.begin(); I != unalloc.end(); I++)
375       free(*I);
376
377    return Ret;
378 }
379                                                                         /*}}}*/
380 // pkgRPMPM::Reset - Dump the contents of the command list              /*{{{*/
381 // ---------------------------------------------------------------------
382 /* */
383 void pkgRPMPM::Reset() 
384 {
385    List.erase(List.begin(),List.end());
386 }
387                                                                         /*}}}*/
388 // RPMExtPM::pkgRPMExtPM - Constructor                                  /*{{{*/
389 // ---------------------------------------------------------------------
390 /* */
391 pkgRPMExtPM::pkgRPMExtPM(pkgDepCache *Cache) : pkgRPMPM(Cache)
392 {
393 }
394                                                                         /*}}}*/
395 // RPMExtPM::pkgRPMExtPM - Destructor                                   /*{{{*/
396 // ---------------------------------------------------------------------
397 /* */
398 pkgRPMExtPM::~pkgRPMExtPM()
399 {
400 }
401                                                                         /*}}}*/
402
403 bool pkgRPMExtPM::ExecRPM(Item::RPMOps op, vector<const char*> &files)
404 {
405    const char *Args[10000];      
406    const char *operation = NULL;
407    unsigned int n = 0;
408    bool Interactive = _config->FindB("RPM::Interactive",true);
409    
410    Args[n++] = _config->Find("Dir::Bin::rpm","rpm").c_str();
411
412    bool nodeps = false;
413
414    switch (op)
415    {
416       case Item::RPMInstall:
417          if (Interactive)
418             operation = "-ivh";
419          else
420             operation = "-iv";
421          nodeps = true;
422          break;
423
424       case Item::RPMUpgrade:
425          if (Interactive)
426             operation = "-Uvh";
427          else
428             operation = "-Uv";
429          break;
430
431       case Item::RPMErase:
432          operation = "-e";
433          nodeps = true;
434          break;
435    }
436    Args[n++] = operation;
437
438    if (Interactive == false && op != Item::RPMErase)
439       Args[n++] = "--percent";
440     
441    string rootdir = _config->Find("RPM::RootDir", "");
442    if (!rootdir.empty()) 
443    {
444        Args[n++] = "-r";
445        Args[n++] = rootdir.c_str();
446    }
447
448    Configuration::Item const *Opts;
449    if (op == Item::RPMErase)
450    {
451       Opts = _config->Tree("RPM::Erase-Options");
452       if (Opts != 0)
453       {
454          Opts = Opts->Child;
455          for (; Opts != 0; Opts = Opts->Next)
456          {
457             if (Opts->Value == "--nodeps")
458                nodeps = false;
459             else if (Opts->Value.empty() == true)
460                continue;
461             Args[n++] = Opts->Value.c_str();
462          }
463       }
464    }
465    else
466    {
467       bool oldpackage = _config->FindB("RPM::OldPackage",
468                                        (op == Item::RPMUpgrade));
469       bool replacepkgs = _config->FindB("APT::Get::ReInstall",false);
470       bool replacefiles = _config->FindB("APT::Get::ReInstall",false);
471       Opts = _config->Tree("RPM::Install-Options");
472       if (Opts != 0)
473       {
474          Opts = Opts->Child;
475          for (; Opts != 0; Opts = Opts->Next)
476          {
477             if (Opts->Value == "--oldpackage")
478                oldpackage = false;
479             else if (Opts->Value == "--replacepkgs")
480                replacepkgs = false;
481             else if (Opts->Value == "--replacefiles")
482                replacefiles = false;
483             else if (Opts->Value == "--nodeps")
484                nodeps = false;
485             else if (Opts->Value.empty() == true)
486                continue;
487             Args[n++] = Opts->Value.c_str();
488          }       
489       }
490       if (oldpackage == true)
491          Args[n++] = "--oldpackage";
492       if (replacepkgs == true)
493          Args[n++] = "--replacepkgs";
494       if (replacefiles == true)
495          Args[n++] = "--replacefiles";
496    }
497
498    if (nodeps == true)
499       Args[n++] = "--nodeps";
500
501    Opts = _config->Tree("RPM::Options");
502    if (Opts != 0)
503    {
504       Opts = Opts->Child;
505       for (; Opts != 0; Opts = Opts->Next)
506       {
507          if (Opts->Value.empty() == true)
508             continue;
509          Args[n++] = Opts->Value.c_str();
510       }  
511    }
512
513    if (_config->FindB("RPM::Order", true) == false)
514       Args[n++] = "--noorder";
515     
516    bool FilesInArgs = true;
517    char *ArgsFileName = NULL;
518 #if RPM_VERSION >= 0x040000
519    if (op != Item::RPMErase && files.size() > 50) {
520       string FileName = _config->FindDir("Dir::Cache", "/tmp/") +
521                         "filelist.XXXXXX";
522       ArgsFileName = strdup(FileName.c_str());
523       if (ArgsFileName) {
524          int fd = mkstemp(ArgsFileName);
525          if (fd != -1) {
526             FileFd File(fd);
527             for (vector<const char*>::iterator I = files.begin();
528                  I != files.end(); I++) {
529                File.Write(*I, strlen(*I));
530                File.Write("\n", 1);
531             }
532             File.Close();
533             FilesInArgs = false;
534             Args[n++] = ArgsFileName;
535          }
536       }
537    }
538 #endif
539
540    if (FilesInArgs == true) {
541       for (vector<const char*>::iterator I = files.begin();
542            I != files.end(); I++)
543          Args[n++] = *I;
544    }
545    
546    Args[n++] = 0;
547
548    if (_config->FindB("Debug::pkgRPMPM",false) == true)
549    {
550       for (unsigned int k = 0; k < n; k++)
551           clog << Args[k] << ' ';
552       clog << endl;
553       if (ArgsFileName) {
554          unlink(ArgsFileName);
555          free(ArgsFileName);
556       }
557       return true;
558    }
559
560    cout << _("Executing RPM (")<<operation<<")..." << endl;
561
562    cout << flush;
563    clog << flush;
564    cerr << flush;
565
566    /* Mask off sig int/quit. We do this because dpkg also does when 
567     it forks scripts. What happens is that when you hit ctrl-c it sends
568     it to all processes in the group. Since dpkg ignores the signal 
569     it doesn't die but we do! So we must also ignore it */
570    //akk ??
571    signal(SIGQUIT,SIG_IGN);
572    signal(SIGINT,SIG_IGN);
573
574    // Fork rpm
575    pid_t Child = ExecFork();
576             
577    // This is the child
578    if (Child == 0)
579    {
580       if (chdir(_config->FindDir("RPM::Run-Directory","/").c_str()) != 0)
581           _exit(100);
582          
583       if (_config->FindB("RPM::FlushSTDIN",true) == true)
584       {
585          int Flags,dummy;
586          if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
587              _exit(100);
588          
589          // Discard everything in stdin before forking dpkg
590          if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
591              _exit(100);
592          
593          while (read(STDIN_FILENO,&dummy,1) == 1);
594          
595          if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
596              _exit(100);
597       }
598
599       execvp(Args[0],(char **)Args);
600       cerr << _("Could not exec ") << Args[0] << endl;
601       _exit(100);
602    }      
603    
604    // Wait for rpm
605    int Status = 0;
606    while (waitpid(Child,&Status,0) != Child)
607    {
608       if (errno == EINTR)
609           continue;
610       RunScripts("RPM::Post-Invoke");
611       if (ArgsFileName) {
612          unlink(ArgsFileName);
613          free(ArgsFileName);
614       }
615       return _error->Errno("waitpid",_("Couldn't wait for subprocess"));
616    }
617    if (ArgsFileName) {
618       unlink(ArgsFileName);
619       free(ArgsFileName);
620    }
621
622    // Restore sig int/quit
623    signal(SIGQUIT,SIG_DFL);
624    signal(SIGINT,SIG_DFL);
625        
626    // Check for an error code.
627    if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
628    {
629       RunScripts("RPM::Post-Invoke");
630       if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
631           return _error->Error(_("Sub-process %s recieved a segmentation fault."),Args[0]);
632       
633       if (WIFEXITED(Status) != 0)
634           return _error->Error(_("Sub-process %s returned an error code (%u)"),Args[0],
635                                WEXITSTATUS(Status));
636       
637       return _error->Error(_("Sub-process %s exited unexpectedly"),Args[0]);
638    }
639
640    if (Interactive == true)
641       cout << _("Done.") << endl;
642
643    return true;
644 }
645
646 bool pkgRPMExtPM::Process(vector<const char*> &install, 
647                        vector<const char*> &upgrade,
648                        vector<const char*> &uninstall)
649 {
650    if (uninstall.empty() == false)
651        ExecRPM(Item::RPMErase, uninstall);
652    if (install.empty() == false)
653        ExecRPM(Item::RPMInstall, install);
654    if (upgrade.empty() == false)
655        ExecRPM(Item::RPMUpgrade, upgrade);
656    return true;
657 }
658
659 // RPMLibPM::pkgRPMLibPM - Constructor                                  /*{{{*/
660 // ---------------------------------------------------------------------
661 /* */
662 pkgRPMLibPM::pkgRPMLibPM(pkgDepCache *Cache) : pkgRPMPM(Cache)
663 {
664 }
665                                                                         /*}}}*/
666 // RPMLibPM::pkgRPMLibPM - Destructor                                   /*{{{*/
667 // ---------------------------------------------------------------------
668 /* */
669 pkgRPMLibPM::~pkgRPMLibPM()
670 {
671 }
672                                                                         /*}}}*/
673
674 bool pkgRPMLibPM::AddToTransaction(Item::RPMOps op, vector<const char*> &files)
675 {
676    int rc;
677    FD_t fd;
678    rpmHeader hdr;
679
680    for (vector<const char*>::iterator I = files.begin(); I != files.end(); I++)
681    {
682       int upgrade = 0;
683
684       switch (op)
685       {
686          case Item::RPMUpgrade:
687             upgrade = 1;
688          case Item::RPMInstall:
689             fd = Fopen(*I, "r.ufdio");
690             if (fd == NULL)
691                _error->Error(_("Failed opening %s"), *I);
692 #if RPM_VERSION >= 0x040100
693             rc = rpmReadPackageFile(TS, fd, *I, &hdr);
694             if (rc != RPMRC_OK && rc != RPMRC_NOTTRUSTED && rc != RPMRC_NOKEY)
695                _error->Error(_("Failed reading file %s"), *I);
696             rc = rpmtsAddInstallElement(TS, hdr, *I, upgrade, 0);
697 #else
698             rc = rpmReadPackageHeader(fd, &hdr, 0, NULL, NULL);
699             if (rc)
700                _error->Error(_("Failed reading file %s"), *I);
701             rc = rpmtransAddPackage(TS, hdr, NULL, *I, upgrade, 0);
702 #endif
703             if (rc)
704                _error->Error(_("Failed adding %s to transaction %s"),
705                              *I, "(install)");
706             headerFree(hdr);
707             Fclose(fd);
708             break;
709
710          case Item::RPMErase:
711 #if RPM_VERSION >= 0x040000
712             rpmdbMatchIterator MI;
713 #if RPM_VERSION >= 0x040100
714             MI = rpmtsInitIterator(TS, (rpmTag)RPMDBI_LABEL, *I, 0);
715 #else
716             MI = rpmdbInitIterator(DB, RPMDBI_LABEL, *I, 0);
717 #endif
718             while ((hdr = rpmdbNextIterator(MI)) != NULL) 
719             {
720                unsigned int recOffset = rpmdbGetIteratorOffset(MI);
721                if (recOffset) {
722 #if RPM_VERSION >= 0x040100
723                   rc = rpmtsAddEraseElement(TS, hdr, recOffset);
724 #else
725                   rc = rpmtransRemovePackage(TS, recOffset);
726 #endif
727                   if (rc)
728                      _error->Error(_("Failed adding %s to transaction %s"),
729                                    *I, "(erase)");
730                }
731             }
732             MI = rpmdbFreeIterator(MI);
733 #else // RPM 3.X
734             dbiIndexSet matches;
735             rc = rpmdbFindByLabel(DB, *I, &matches);
736             if (rc == 0) {
737                for (int i = 0; i < dbiIndexSetCount(matches); i++) {
738                   unsigned int recOffset = dbiIndexRecordOffset(matches, i);
739                   if (recOffset)
740                      rpmtransRemovePackage(TS, recOffset);
741                }
742             }
743 #endif
744             break;
745       }
746    }
747    return true;
748 }
749
750 bool pkgRPMLibPM::Process(vector<const char*> &install, 
751                           vector<const char*> &upgrade,
752                           vector<const char*> &uninstall)
753 {
754    int rc = 0;
755    bool Success = false;
756    bool Interactive = _config->FindB("RPM::Interactive",true);
757    string Dir = _config->Find("RPM::RootDir");
758    rpmReadConfigFiles(NULL, NULL);
759
760    int probFilter = 0;
761    int notifyFlags = 0;
762    int tsFlags = 0;
763
764    if (uninstall.empty() == false)
765       ParseRpmOpts("RPM::Erase-Options", &tsFlags, &probFilter);
766    if (install.empty() == false || upgrade.empty() == false)
767       ParseRpmOpts("RPM::Install-Options", &tsFlags, &probFilter);
768    ParseRpmOpts("RPM::Options", &tsFlags, &probFilter);
769
770 #if RPM_VERSION >= 0x040100
771    rpmps probs;
772    TS = rpmtsCreate();
773    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
774    // 4.1 needs this always set even if NULL,
775    // otherwise all scriptlets fail
776    rpmtsSetRootDir(TS, Dir.c_str());
777 #else
778    rpmProblemSet probs;
779    const char *RootDir = NULL;
780    if (!Dir.empty())
781       RootDir = Dir.c_str();
782    if (rpmdbOpen(RootDir, &DB, O_RDWR, 0644) != 0)
783    {
784       _error->Error(_("Could not open RPM database"));
785       goto exit;
786    }
787    TS = rpmtransCreateSet(DB, Dir.c_str());
788 #endif
789
790 #if RPM_VERSION >= 0x040000
791    if (rpmExpandNumeric("%{?_repackage_all_erasures}"))
792       tsFlags |= RPMTRANS_FLAG_REPACKAGE;
793 #endif
794                      
795 #if RPM_VERSION >= 0x040300
796    /* Initialize security context patterns for SELinux */
797    if (!(tsFlags & RPMTRANS_FLAG_NOCONTEXTS)) {
798       rpmsx sx = rpmtsREContext(TS);
799       if (sx == NULL) {
800          const char *fn = rpmGetPath("%{?_install_file_context_path}", NULL);
801          if (fn != NULL && *fn != '\0') {
802             sx = rpmsxNew(fn);
803             (void) rpmtsSetREContext(TS, sx);
804          }
805          fn = (const char *) _free(fn);
806       }
807       sx = rpmsxFree(sx);
808    }
809 #endif
810
811    if (_config->FindB("RPM::OldPackage", true) || !upgrade.empty()) {
812       probFilter |= RPMPROB_FILTER_OLDPACKAGE;
813    }
814    if (_config->FindB("APT::Get::ReInstall", false)) {
815       probFilter |= RPMPROB_FILTER_REPLACEPKG;
816       probFilter |= RPMPROB_FILTER_REPLACEOLDFILES;
817       probFilter |= RPMPROB_FILTER_REPLACENEWFILES;
818    }
819
820    if (_config->FindI("quiet",0) >= 1)
821        notifyFlags |= INSTALL_LABEL;
822    else if (Interactive == true) 
823        notifyFlags |= INSTALL_LABEL | INSTALL_HASH;
824    else 
825        notifyFlags |= INSTALL_LABEL | INSTALL_PERCENT;
826
827    if (uninstall.empty() == false)
828        AddToTransaction(Item::RPMErase, uninstall);
829    if (install.empty() == false)
830        AddToTransaction(Item::RPMInstall, install);
831    if (upgrade.empty() == false)
832        AddToTransaction(Item::RPMUpgrade, upgrade);
833
834    packagesTotal = install.size() + upgrade.size() * 2 + uninstall.size();
835    if (tsFlags & RPMTRANS_FLAG_REPACKAGE) {
836       packagesTotal += upgrade.size() + uninstall.size();
837    }
838
839 #if RPM_VERSION >= 0x040100
840    if (_config->FindB("RPM::NoDeps", false) == false) {
841       rc = rpmtsCheck(TS);
842       probs = rpmtsProblems(TS);
843       if (rc || probs->numProblems > 0) {
844          rpmpsPrint(NULL, probs);
845          rpmpsFree(probs);
846          _error->Error(_("Transaction set check failed"));
847          goto exit;
848       }
849    }
850 #else
851 #if RPM_VERSION < 0x040000
852    rpmDependencyConflict *conflicts;
853 #else
854    rpmDependencyConflict conflicts;
855 #endif
856    if (_config->FindB("RPM::NoDeps", false) == false) {
857       int numConflicts;
858       if (rpmdepCheck(TS, &conflicts, &numConflicts)) {
859          _error->Error(_("Transaction set check failed"));
860          if (conflicts) {
861             printDepProblems(stderr, conflicts, numConflicts);
862             rpmdepFreeConflicts(conflicts, numConflicts);
863          }
864          goto exit;
865       }
866    }
867 #endif
868
869    rc = 0;
870 #if RPM_VERSION >= 0x040100
871    if (_config->FindB("RPM::Order", true) == true)
872       rc = rpmtsOrder(TS);
873 #else
874    if (_config->FindB("RPM::Order", true) == true)
875       rc = rpmdepOrder(TS);
876 #endif
877
878    if (rc > 0) {
879       _error->Error(_("Ordering failed for %d packages"), rc);
880       goto exit;
881    }
882
883    cout << _("Committing changes...") << endl << flush;
884
885 #if RPM_VERSION >= 0x040100
886    probFilter |= rpmtsFilterFlags(TS);
887    rpmtsSetFlags(TS, (rpmtransFlags)(rpmtsFlags(TS) | tsFlags));
888    rpmtsClean(TS);
889    rc = rpmtsSetNotifyCallback(TS, rpmpmShowProgress, (void *)notifyFlags);
890    rc = rpmtsRun(TS, NULL, (rpmprobFilterFlags)probFilter);
891    probs = rpmtsProblems(TS);
892 #else
893    rc = rpmRunTransactions(TS, rpmpmShowProgress, (void *)notifyFlags, NULL,
894                            &probs, (rpmtransFlags)tsFlags,
895                            (rpmprobFilterFlags)probFilter);
896 #endif
897
898    if (rc > 0) {
899       _error->Error(_("Error while running transaction"));
900       if (probs->numProblems > 0)
901          rpmpsPrint(stderr, probs);
902    } else {
903       Success = true;
904       if (rc < 0)
905          _error->Warning(_("Some errors occurred while running transaction"));
906       else if (Interactive == true)
907          cout << _("Done.") << endl;
908    }
909    rpmpsFree(probs);
910
911 exit:
912
913 #if RPM_VERSION >= 0x040100
914    rpmtsFree(TS);
915 #else
916    rpmdbClose(DB);
917 #endif
918
919    return Success;
920 }
921
922 bool pkgRPMLibPM::ParseRpmOpts(const char *Cnf, int *tsFlags, int *probFilter)
923 {
924    Configuration::Item const *Opts = _config->Tree(Cnf);
925    
926    if (Opts != 0)
927    {
928       Opts = Opts->Child;
929       for (; Opts != 0; Opts = Opts->Next)
930       {
931          if (Opts->Value.empty() == true)
932             continue;
933          // Transaction set flags
934          if (Opts->Value == "--noscripts")
935             *tsFlags |= RPMTRANS_FLAG_NOSCRIPTS;
936          else if (Opts->Value == "--notriggers")
937             *tsFlags |= RPMTRANS_FLAG_NOTRIGGERS;
938          else if (Opts->Value == "--nodocs" ||
939                   Opts->Value == "--excludedocs")
940             *tsFlags |= RPMTRANS_FLAG_NODOCS;
941          else if (Opts->Value == "--allfiles")
942             *tsFlags |= RPMTRANS_FLAG_ALLFILES;
943          else if (Opts->Value == "--justdb")
944             *tsFlags |= RPMTRANS_FLAG_JUSTDB;
945          else if (Opts->Value == "--test")
946             *tsFlags |= RPMTRANS_FLAG_TEST;
947 #if RPM_VERSION >= 0x040000
948          else if (Opts->Value == "--nomd5")
949             *tsFlags |= RPMTRANS_FLAG_NOMD5;
950          else if (Opts->Value == "--repackage")
951             *tsFlags |= RPMTRANS_FLAG_REPACKAGE;
952 #endif
953 #if RPM_VERSION >= 0x040200
954          else if (Opts->Value == "--noconfigs" ||
955                   Opts->Value == "--excludeconfigs")
956             *tsFlags |= RPMTRANS_FLAG_NOCONFIGS;
957 #endif
958 #if RPM_VERSION >= 0x040300
959          else if (Opts->Value == "--nocontexts")
960             *tsFlags |= RPMTRANS_FLAG_NOCONTEXTS;
961 #endif
962
963          // Problem filter flags
964          else if (Opts->Value == "--replacefiles")
965          {
966             *probFilter |= RPMPROB_FILTER_REPLACEOLDFILES;
967             *probFilter |= RPMPROB_FILTER_REPLACENEWFILES;
968          }
969          else if (Opts->Value == "--replacepkgs")
970             *probFilter |= RPMPROB_FILTER_REPLACEPKG;
971          else if (Opts->Value == "--ignoresize")
972          {
973             *probFilter |= RPMPROB_FILTER_DISKSPACE;
974 #if RPM_VERSION >= 0x040000
975             *probFilter |= RPMPROB_FILTER_DISKNODES;
976 #endif
977          }
978          else if (Opts->Value == "--badreloc")
979             *probFilter |= RPMPROB_FILTER_FORCERELOCATE;
980
981          // Misc things having apt config counterparts
982          else if (Opts->Value == "--force")
983             _config->Set("APT::Get::ReInstall", true);
984          else if (Opts->Value == "--oldpackage")
985             _config->Set("RPM::OldPackage", true);
986          else if (Opts->Value == "--nodeps")
987             _config->Set("RPM::NoDeps", true);
988          else if (Opts->Value == "--noorder")
989             _config->Set("RPM::Order", false);
990          else if (Opts->Value == "-v") {
991             rpmIncreaseVerbosity();
992          } else if (Opts->Value == "-vv") {
993             rpmIncreaseVerbosity();
994             rpmIncreaseVerbosity();
995          } else if (Opts->Value == "-vvv") {
996             rpmIncreaseVerbosity();
997             rpmIncreaseVerbosity();
998             rpmIncreaseVerbosity();
999          }
1000          // TODO: --root, --relocate, --prefix, --excludepath etc...
1001
1002       }
1003    }
1004    return true;
1005
1006 #endif /* HAVE_RPM */
1007
1008 // vim:sts=3:sw=3