- calculate the total amount of rpm operations including repackages and all
[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             upgrade.push_back(I->File.c_str());
312          }
313          install_or_upgrade.push_back(I->File.c_str());
314          pkgs_install.push_back(I->Pkg);
315          break;
316           
317        default:
318          return _error->Error(_("Unknown pkgRPMPM operation."));
319       }
320    }
321
322    bool Ret = true;
323
324 #ifdef WITH_LUA
325    if (_lua->HasScripts("Scripts::PM::Pre") == true) {
326       _lua->SetGlobal("files_install", install_or_upgrade);
327       _lua->SetGlobal("names_remove", uninstall);
328       _lua->SetGlobal("pkgs_install", pkgs_install);
329       _lua->SetGlobal("pkgs_remove", pkgs_uninstall);
330       _lua->SetDepCache(&Cache);
331       _lua->RunScripts("Scripts::PM::Pre");
332       _lua->ResetCaches();
333       _lua->ResetGlobals();
334       if (_error->PendingError() == true) {
335          _error->DumpErrors();
336          Ret = false;
337          goto exit;
338       }
339    }
340 #endif
341
342    if (Process(install, upgrade, uninstall) == false)
343       Ret = false;
344
345 #ifdef WITH_LUA
346    if (_lua->HasScripts("Scripts::PM::Post") == true) {
347       _lua->SetGlobal("files_install", install_or_upgrade);
348       _lua->SetGlobal("names_remove", uninstall);
349       _lua->SetGlobal("pkgs_install", pkgs_install);
350       _lua->SetGlobal("pkgs_remove", pkgs_uninstall);
351       _lua->SetGlobal("transaction_success", Ret);
352       _lua->SetDepCache(&Cache);
353       _lua->RunScripts("Scripts::PM::Post");
354       _lua->ResetCaches();
355       _lua->ResetGlobals();
356       if (_error->PendingError() == true) {
357          _error->DumpErrors();
358          Ret = false;
359          goto exit;
360       }
361    }
362 #endif
363
364    
365    if (Ret == true)
366       Ret = RunScripts("RPM::Post-Invoke");
367
368 exit:
369    for (vector<char *>::iterator I = unalloc.begin(); I != unalloc.end(); I++)
370       free(*I);
371
372    return Ret;
373 }
374                                                                         /*}}}*/
375 // pkgRPMPM::Reset - Dump the contents of the command list              /*{{{*/
376 // ---------------------------------------------------------------------
377 /* */
378 void pkgRPMPM::Reset() 
379 {
380    List.erase(List.begin(),List.end());
381 }
382                                                                         /*}}}*/
383 // RPMExtPM::pkgRPMExtPM - Constructor                                  /*{{{*/
384 // ---------------------------------------------------------------------
385 /* */
386 pkgRPMExtPM::pkgRPMExtPM(pkgDepCache *Cache) : pkgRPMPM(Cache)
387 {
388 }
389                                                                         /*}}}*/
390 // RPMExtPM::pkgRPMExtPM - Destructor                                   /*{{{*/
391 // ---------------------------------------------------------------------
392 /* */
393 pkgRPMExtPM::~pkgRPMExtPM()
394 {
395 }
396                                                                         /*}}}*/
397
398 bool pkgRPMExtPM::ExecRPM(Item::RPMOps op, vector<const char*> &files)
399 {
400    const char *Args[10000];      
401    const char *operation = NULL;
402    unsigned int n = 0;
403    bool Interactive = _config->FindB("RPM::Interactive",true);
404    
405    Args[n++] = _config->Find("Dir::Bin::rpm","rpm").c_str();
406
407    bool nodeps = false;
408
409    switch (op)
410    {
411       case Item::RPMInstall:
412          if (Interactive)
413             operation = "-ivh";
414          else
415             operation = "-iv";
416          nodeps = true;
417          break;
418
419       case Item::RPMUpgrade:
420          if (Interactive)
421             operation = "-Uvh";
422          else
423             operation = "-Uv";
424          break;
425
426       case Item::RPMErase:
427          operation = "-e";
428          nodeps = true;
429          break;
430    }
431    Args[n++] = operation;
432
433    if (Interactive == false && op != Item::RPMErase)
434       Args[n++] = "--percent";
435     
436    string rootdir = _config->Find("RPM::RootDir", "");
437    if (!rootdir.empty()) 
438    {
439        Args[n++] = "-r";
440        Args[n++] = rootdir.c_str();
441    }
442
443    Configuration::Item const *Opts;
444    if (op == Item::RPMErase)
445    {
446       Opts = _config->Tree("RPM::Erase-Options");
447       if (Opts != 0)
448       {
449          Opts = Opts->Child;
450          for (; Opts != 0; Opts = Opts->Next)
451          {
452             if (Opts->Value == "--nodeps")
453                nodeps = false;
454             else if (Opts->Value.empty() == true)
455                continue;
456             Args[n++] = Opts->Value.c_str();
457          }
458       }
459    }
460    else
461    {
462       bool oldpackage = _config->FindB("RPM::OldPackage",
463                                        (op == Item::RPMUpgrade));
464       bool replacepkgs = _config->FindB("APT::Get::ReInstall",false);
465       bool replacefiles = _config->FindB("APT::Get::ReInstall",false);
466       Opts = _config->Tree("RPM::Install-Options");
467       if (Opts != 0)
468       {
469          Opts = Opts->Child;
470          for (; Opts != 0; Opts = Opts->Next)
471          {
472             if (Opts->Value == "--oldpackage")
473                oldpackage = false;
474             else if (Opts->Value == "--replacepkgs")
475                replacepkgs = false;
476             else if (Opts->Value == "--replacefiles")
477                replacefiles = false;
478             else if (Opts->Value == "--nodeps")
479                nodeps = false;
480             else if (Opts->Value.empty() == true)
481                continue;
482             Args[n++] = Opts->Value.c_str();
483          }       
484       }
485       if (oldpackage == true)
486          Args[n++] = "--oldpackage";
487       if (replacepkgs == true)
488          Args[n++] = "--replacepkgs";
489       if (replacefiles == true)
490          Args[n++] = "--replacefiles";
491    }
492
493    if (nodeps == true)
494       Args[n++] = "--nodeps";
495
496    Opts = _config->Tree("RPM::Options");
497    if (Opts != 0)
498    {
499       Opts = Opts->Child;
500       for (; Opts != 0; Opts = Opts->Next)
501       {
502          if (Opts->Value.empty() == true)
503             continue;
504          Args[n++] = Opts->Value.c_str();
505       }  
506    }
507
508    if (_config->FindB("RPM::Order", true) == false)
509       Args[n++] = "--noorder";
510     
511    bool FilesInArgs = true;
512    char *ArgsFileName = NULL;
513 #if RPM_VERSION >= 0x040000
514    if (op != Item::RPMErase && files.size() > 50) {
515       string FileName = _config->FindDir("Dir::Cache", "/tmp/") +
516                         "filelist.XXXXXX";
517       ArgsFileName = strdup(FileName.c_str());
518       if (ArgsFileName) {
519          int fd = mkstemp(ArgsFileName);
520          if (fd != -1) {
521             FileFd File(fd);
522             for (vector<const char*>::iterator I = files.begin();
523                  I != files.end(); I++) {
524                File.Write(*I, strlen(*I));
525                File.Write("\n", 1);
526             }
527             File.Close();
528             FilesInArgs = false;
529             Args[n++] = ArgsFileName;
530          }
531       }
532    }
533 #endif
534
535    if (FilesInArgs == true) {
536       for (vector<const char*>::iterator I = files.begin();
537            I != files.end(); I++)
538          Args[n++] = *I;
539    }
540    
541    Args[n++] = 0;
542
543    if (_config->FindB("Debug::pkgRPMPM",false) == true)
544    {
545       for (unsigned int k = 0; k < n; k++)
546           clog << Args[k] << ' ';
547       clog << endl;
548       if (ArgsFileName) {
549          unlink(ArgsFileName);
550          free(ArgsFileName);
551       }
552       return true;
553    }
554
555    cout << _("Executing RPM (")<<operation<<")..." << endl;
556
557    cout << flush;
558    clog << flush;
559    cerr << flush;
560
561    /* Mask off sig int/quit. We do this because dpkg also does when 
562     it forks scripts. What happens is that when you hit ctrl-c it sends
563     it to all processes in the group. Since dpkg ignores the signal 
564     it doesn't die but we do! So we must also ignore it */
565    //akk ??
566    signal(SIGQUIT,SIG_IGN);
567    signal(SIGINT,SIG_IGN);
568
569    // Fork rpm
570    pid_t Child = ExecFork();
571             
572    // This is the child
573    if (Child == 0)
574    {
575       if (chdir(_config->FindDir("RPM::Run-Directory","/").c_str()) != 0)
576           _exit(100);
577          
578       if (_config->FindB("RPM::FlushSTDIN",true) == true)
579       {
580          int Flags,dummy;
581          if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
582              _exit(100);
583          
584          // Discard everything in stdin before forking dpkg
585          if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
586              _exit(100);
587          
588          while (read(STDIN_FILENO,&dummy,1) == 1);
589          
590          if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
591              _exit(100);
592       }
593
594       execvp(Args[0],(char **)Args);
595       cerr << _("Could not exec ") << Args[0] << endl;
596       _exit(100);
597    }      
598    
599    // Wait for rpm
600    int Status = 0;
601    while (waitpid(Child,&Status,0) != Child)
602    {
603       if (errno == EINTR)
604           continue;
605       RunScripts("RPM::Post-Invoke");
606       if (ArgsFileName) {
607          unlink(ArgsFileName);
608          free(ArgsFileName);
609       }
610       return _error->Errno("waitpid",_("Couldn't wait for subprocess"));
611    }
612    if (ArgsFileName) {
613       unlink(ArgsFileName);
614       free(ArgsFileName);
615    }
616
617    // Restore sig int/quit
618    signal(SIGQUIT,SIG_DFL);
619    signal(SIGINT,SIG_DFL);
620        
621    // Check for an error code.
622    if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
623    {
624       RunScripts("RPM::Post-Invoke");
625       if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
626           return _error->Error(_("Sub-process %s recieved a segmentation fault."),Args[0]);
627       
628       if (WIFEXITED(Status) != 0)
629           return _error->Error(_("Sub-process %s returned an error code (%u)"),Args[0],
630                                WEXITSTATUS(Status));
631       
632       return _error->Error(_("Sub-process %s exited unexpectedly"),Args[0]);
633    }
634
635    if (Interactive == true)
636       cout << _("Done.") << endl;
637
638    return true;
639 }
640
641 bool pkgRPMExtPM::Process(vector<const char*> &install, 
642                        vector<const char*> &upgrade,
643                        vector<const char*> &uninstall)
644 {
645    if (uninstall.empty() == false)
646        ExecRPM(Item::RPMErase, uninstall);
647    if (install.empty() == false)
648        ExecRPM(Item::RPMInstall, install);
649    if (upgrade.empty() == false)
650        ExecRPM(Item::RPMUpgrade, upgrade);
651    return true;
652 }
653
654 // RPMLibPM::pkgRPMLibPM - Constructor                                  /*{{{*/
655 // ---------------------------------------------------------------------
656 /* */
657 pkgRPMLibPM::pkgRPMLibPM(pkgDepCache *Cache) : pkgRPMPM(Cache)
658 {
659 }
660                                                                         /*}}}*/
661 // RPMLibPM::pkgRPMLibPM - Destructor                                   /*{{{*/
662 // ---------------------------------------------------------------------
663 /* */
664 pkgRPMLibPM::~pkgRPMLibPM()
665 {
666 }
667                                                                         /*}}}*/
668
669 bool pkgRPMLibPM::AddToTransaction(Item::RPMOps op, vector<const char*> &files)
670 {
671    int rc;
672    FD_t fd;
673    rpmHeader hdr;
674
675    for (vector<const char*>::iterator I = files.begin(); I != files.end(); I++)
676    {
677       int upgrade = 0;
678
679       switch (op)
680       {
681          case Item::RPMUpgrade:
682             upgrade = 1;
683          case Item::RPMInstall:
684             fd = Fopen(*I, "r.ufdio");
685             if (fd == NULL)
686                _error->Error(_("Failed opening %s"), *I);
687 #if RPM_VERSION >= 0x040100
688             rc = rpmReadPackageFile(TS, fd, *I, &hdr);
689             if (rc != RPMRC_OK && rc != RPMRC_NOTTRUSTED && rc != RPMRC_NOKEY)
690                _error->Error(_("Failed reading file %s"), *I);
691             rc = rpmtsAddInstallElement(TS, hdr, *I, upgrade, 0);
692 #else
693             rc = rpmReadPackageHeader(fd, &hdr, 0, NULL, NULL);
694             if (rc)
695                _error->Error(_("Failed reading file %s"), *I);
696             rc = rpmtransAddPackage(TS, hdr, NULL, *I, upgrade, 0);
697 #endif
698             if (rc)
699                _error->Error(_("Failed adding %s to transaction %s"),
700                              *I, "(install)");
701             headerFree(hdr);
702             Fclose(fd);
703             break;
704
705          case Item::RPMErase:
706 #if RPM_VERSION >= 0x040000
707             rpmdbMatchIterator MI;
708 #if RPM_VERSION >= 0x040100
709             MI = rpmtsInitIterator(TS, (rpmTag)RPMDBI_LABEL, *I, 0);
710 #else
711             MI = rpmdbInitIterator(DB, RPMDBI_LABEL, *I, 0);
712 #endif
713             while ((hdr = rpmdbNextIterator(MI)) != NULL) 
714             {
715                unsigned int recOffset = rpmdbGetIteratorOffset(MI);
716                if (recOffset) {
717 #if RPM_VERSION >= 0x040100
718                   rc = rpmtsAddEraseElement(TS, hdr, recOffset);
719 #else
720                   rc = rpmtransRemovePackage(TS, recOffset);
721 #endif
722                   if (rc)
723                      _error->Error(_("Failed adding %s to transaction %s"),
724                                    *I, "(erase)");
725                }
726             }
727             MI = rpmdbFreeIterator(MI);
728 #else // RPM 3.X
729             dbiIndexSet matches;
730             rc = rpmdbFindByLabel(DB, *I, &matches);
731             if (rc == 0) {
732                for (int i = 0; i < dbiIndexSetCount(matches); i++) {
733                   unsigned int recOffset = dbiIndexRecordOffset(matches, i);
734                   if (recOffset)
735                      rpmtransRemovePackage(TS, recOffset);
736                }
737             }
738 #endif
739             break;
740       }
741    }
742    return true;
743 }
744
745 bool pkgRPMLibPM::Process(vector<const char*> &install, 
746                           vector<const char*> &upgrade,
747                           vector<const char*> &uninstall)
748 {
749    int rc = 0;
750    bool Success = false;
751    bool Interactive = _config->FindB("RPM::Interactive",true);
752    string Dir = _config->Find("RPM::RootDir");
753    rpmReadConfigFiles(NULL, NULL);
754
755    int probFilter = 0;
756    int notifyFlags = 0;
757    int tsFlags = 0;
758
759    if (uninstall.empty() == false)
760       ParseRpmOpts("RPM::Erase-Options", &tsFlags, &probFilter);
761    if (install.empty() == false || upgrade.empty() == false)
762       ParseRpmOpts("RPM::Install-Options", &tsFlags, &probFilter);
763    ParseRpmOpts("RPM::Options", &tsFlags, &probFilter);
764
765 #if RPM_VERSION >= 0x040100
766    rpmps probs;
767    TS = rpmtsCreate();
768    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
769    // 4.1 needs this always set even if NULL,
770    // otherwise all scriptlets fail
771    rpmtsSetRootDir(TS, Dir.c_str());
772 #else
773    rpmProblemSet probs;
774    const char *RootDir = NULL;
775    if (!Dir.empty())
776       RootDir = Dir.c_str();
777    if (rpmdbOpen(RootDir, &DB, O_RDWR, 0644) != 0)
778    {
779       _error->Error(_("Could not open RPM database"));
780       goto exit;
781    }
782    TS = rpmtransCreateSet(DB, Dir.c_str());
783 #endif
784
785 #if RPM_VERSION >= 0x040000
786    if (rpmExpandNumeric("%{?_repackage_all_erasures}"))
787       tsFlags |= RPMTRANS_FLAG_REPACKAGE;
788 #endif
789                      
790 #if RPM_VERSION >= 0x040300
791 #ifdef WITH_SELINUX
792    /* Initialize security context patterns for SELinux */
793    if (!(tsFlags & RPMTRANS_FLAG_NOCONTEXTS)) {
794       rpmsx sx = rpmtsREContext(TS);
795       if (sx == NULL) {
796          const char *fn = rpmGetPath("%{?_install_file_context_path}", NULL);
797          if (fn != NULL && *fn != '\0') {
798             sx = rpmsxNew(fn);
799             (void) rpmtsSetREContext(TS, sx);
800          }
801          fn = (const char *) _free(fn);
802       }
803       sx = rpmsxFree(sx);
804    }
805 #endif
806 #endif
807
808    if (_config->FindB("RPM::OldPackage", true) || !upgrade.empty()) {
809       probFilter |= RPMPROB_FILTER_OLDPACKAGE;
810    }
811    if (_config->FindB("APT::Get::ReInstall", false)) {
812       probFilter |= RPMPROB_FILTER_REPLACEPKG;
813       probFilter |= RPMPROB_FILTER_REPLACEOLDFILES;
814       probFilter |= RPMPROB_FILTER_REPLACENEWFILES;
815    }
816
817    notifyFlags |= INSTALL_LABEL;
818    if (Interactive == false) {
819        notifyFlags |= INSTALL_PERCENT;
820    } else if (_config->FindI("quiet",0) < 1) {
821        notifyFlags |= INSTALL_HASH;
822    }
823
824    if (uninstall.empty() == false)
825        AddToTransaction(Item::RPMErase, uninstall);
826    if (install.empty() == false)
827        AddToTransaction(Item::RPMInstall, install);
828    if (upgrade.empty() == false)
829        AddToTransaction(Item::RPMUpgrade, upgrade);
830
831    packagesTotal = 0;
832    // Figure out exactly how many rpm operations we're going to process,
833    // repackages and all.
834    int repackage = (tsFlags & RPMTRANS_FLAG_REPACKAGE) ? 1 : 0;
835    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) {
836       if (Cache[I].NewInstall() == true) {
837          packagesTotal++;
838       } else if (Cache[I].Upgrade() == true ||
839                  Cache[I].Downgrade() == true) {
840          packagesTotal += 2 + repackage;
841       } else if (Cache[I].Delete() == true) {
842          packagesTotal += 1 + repackage;
843       }
844    }
845          
846 #if RPM_VERSION >= 0x040100
847    if (_config->FindB("RPM::NoDeps", false) == false) {
848       rc = rpmtsCheck(TS);
849       probs = rpmtsProblems(TS);
850       if (rc || probs->numProblems > 0) {
851          rpmpsPrint(NULL, probs);
852          rpmpsFree(probs);
853          _error->Error(_("Transaction set check failed"));
854          goto exit;
855       }
856    }
857 #else
858 #if RPM_VERSION < 0x040000
859    rpmDependencyConflict *conflicts;
860 #else
861    rpmDependencyConflict conflicts;
862 #endif
863    if (_config->FindB("RPM::NoDeps", false) == false) {
864       int numConflicts;
865       if (rpmdepCheck(TS, &conflicts, &numConflicts)) {
866          _error->Error(_("Transaction set check failed"));
867          if (conflicts) {
868             printDepProblems(stderr, conflicts, numConflicts);
869             rpmdepFreeConflicts(conflicts, numConflicts);
870          }
871          goto exit;
872       }
873    }
874 #endif
875
876    rc = 0;
877 #if RPM_VERSION >= 0x040100
878    if (_config->FindB("RPM::Order", true) == true)
879       rc = rpmtsOrder(TS);
880 #else
881    if (_config->FindB("RPM::Order", true) == true)
882       rc = rpmdepOrder(TS);
883 #endif
884
885    if (rc > 0) {
886       _error->Error(_("Ordering failed for %d packages"), rc);
887       goto exit;
888    }
889
890    cout << _("Committing changes...") << endl << flush;
891
892 #if RPM_VERSION >= 0x040100
893    probFilter |= rpmtsFilterFlags(TS);
894    rpmtsSetFlags(TS, (rpmtransFlags)(rpmtsFlags(TS) | tsFlags));
895    rpmtsClean(TS);
896    rc = rpmtsSetNotifyCallback(TS, rpmpmShowProgress, (void *)notifyFlags);
897    rc = rpmtsRun(TS, NULL, (rpmprobFilterFlags)probFilter);
898    probs = rpmtsProblems(TS);
899 #else
900    rc = rpmRunTransactions(TS, rpmpmShowProgress, (void *)notifyFlags, NULL,
901                            &probs, (rpmtransFlags)tsFlags,
902                            (rpmprobFilterFlags)probFilter);
903 #endif
904
905    if (rc > 0) {
906       _error->Error(_("Error while running transaction"));
907       if (probs->numProblems > 0)
908          rpmpsPrint(stderr, probs);
909    } else {
910       Success = true;
911       if (rc < 0)
912          _error->Warning(_("Some errors occurred while running transaction"));
913       else if (Interactive == true)
914          cout << _("Done.") << endl;
915    }
916    rpmpsFree(probs);
917
918 exit:
919
920 #if RPM_VERSION >= 0x040100
921    rpmtsFree(TS);
922 #else
923    rpmdbClose(DB);
924 #endif
925
926    return Success;
927 }
928
929 bool pkgRPMLibPM::ParseRpmOpts(const char *Cnf, int *tsFlags, int *probFilter)
930 {
931    Configuration::Item const *Opts = _config->Tree(Cnf);
932    
933    if (Opts != 0)
934    {
935       Opts = Opts->Child;
936       for (; Opts != 0; Opts = Opts->Next)
937       {
938          if (Opts->Value.empty() == true)
939             continue;
940          // Transaction set flags
941          if (Opts->Value == "--noscripts")
942             *tsFlags |= RPMTRANS_FLAG_NOSCRIPTS;
943          else if (Opts->Value == "--notriggers")
944             *tsFlags |= RPMTRANS_FLAG_NOTRIGGERS;
945          else if (Opts->Value == "--nodocs" ||
946                   Opts->Value == "--excludedocs")
947             *tsFlags |= RPMTRANS_FLAG_NODOCS;
948          else if (Opts->Value == "--allfiles")
949             *tsFlags |= RPMTRANS_FLAG_ALLFILES;
950          else if (Opts->Value == "--justdb")
951             *tsFlags |= RPMTRANS_FLAG_JUSTDB;
952          else if (Opts->Value == "--test")
953             *tsFlags |= RPMTRANS_FLAG_TEST;
954 #if RPM_VERSION >= 0x040000
955          else if (Opts->Value == "--nomd5")
956             *tsFlags |= RPMTRANS_FLAG_NOMD5;
957          else if (Opts->Value == "--repackage")
958             *tsFlags |= RPMTRANS_FLAG_REPACKAGE;
959 #endif
960 #if RPM_VERSION >= 0x040200
961          else if (Opts->Value == "--noconfigs" ||
962                   Opts->Value == "--excludeconfigs")
963             *tsFlags |= RPMTRANS_FLAG_NOCONFIGS;
964 #endif
965 #if RPM_VERSION >= 0x040300
966          else if (Opts->Value == "--nocontexts")
967             *tsFlags |= RPMTRANS_FLAG_NOCONTEXTS;
968 #endif
969
970          // Problem filter flags
971          else if (Opts->Value == "--replacefiles")
972          {
973             *probFilter |= RPMPROB_FILTER_REPLACEOLDFILES;
974             *probFilter |= RPMPROB_FILTER_REPLACENEWFILES;
975          }
976          else if (Opts->Value == "--replacepkgs")
977             *probFilter |= RPMPROB_FILTER_REPLACEPKG;
978          else if (Opts->Value == "--ignoresize")
979          {
980             *probFilter |= RPMPROB_FILTER_DISKSPACE;
981 #if RPM_VERSION >= 0x040000
982             *probFilter |= RPMPROB_FILTER_DISKNODES;
983 #endif
984          }
985          else if (Opts->Value == "--badreloc")
986             *probFilter |= RPMPROB_FILTER_FORCERELOCATE;
987
988          // Misc things having apt config counterparts
989          else if (Opts->Value == "--force")
990             _config->Set("APT::Get::ReInstall", true);
991          else if (Opts->Value == "--oldpackage")
992             _config->Set("RPM::OldPackage", true);
993          else if (Opts->Value == "--nodeps")
994             _config->Set("RPM::NoDeps", true);
995          else if (Opts->Value == "--noorder")
996             _config->Set("RPM::Order", false);
997          else if (Opts->Value == "-v") {
998             rpmIncreaseVerbosity();
999          } else if (Opts->Value == "-vv") {
1000             rpmIncreaseVerbosity();
1001             rpmIncreaseVerbosity();
1002          } else if (Opts->Value == "-vvv") {
1003             rpmIncreaseVerbosity();
1004             rpmIncreaseVerbosity();
1005             rpmIncreaseVerbosity();
1006          }
1007          // TODO: --root, --relocate, --prefix, --excludepath etc...
1008
1009       }
1010    }
1011    return true;
1012
1013 #endif /* HAVE_RPM */
1014
1015 // vim:sts=3:sw=3