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