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