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