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