- clean up install/remove processing in rpmpm::Go()
[apt.git] / apt-pkg / rpm / rpmpm.cc
1 // -*- mode: cpp; 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 #define packagesTotal rpmcliPackagesTotal 
40 #else
41 #define rpmpsPrint(a,b) rpmProblemSetPrint(a,b)
42 #define rpmpsFree(a) rpmProblemSetFree(a)
43 #define rpmReadPackageFile(a,b,c,d) rpmReadPackageHeader(b,d,0,NULL,NULL)
44 #if RPM_VERSION < 0x040000
45 #define rpmtransFlags int
46 #define rpmprobFilterFlags int
47 #include "rpmshowprogress.h"
48 #endif
49 #endif
50
51 // RPMPM::pkgRPMPM - Constructor                                        /*{{{*/
52 // ---------------------------------------------------------------------
53 /* */
54 pkgRPMPM::pkgRPMPM(pkgDepCache *Cache) : pkgPackageManager(Cache)
55 {
56 }
57                                                                         /*}}}*/
58 // RPMPM::pkgRPMPM - Destructor                                 /*{{{*/
59 // ---------------------------------------------------------------------
60 /* */
61 pkgRPMPM::~pkgRPMPM()
62 {
63 }
64                                                                         /*}}}*/
65 // RPMPM::Install - Install a package                                   /*{{{*/
66 // ---------------------------------------------------------------------
67 /* Add an install operation to the sequence list */
68 bool pkgRPMPM::Install(PkgIterator Pkg,string File)
69 {
70    if (File.empty() == true || Pkg.end() == true)
71       return _error->Error(_("Internal Error, No file name for %s"),Pkg.Name());
72
73    List.push_back(Item(Item::Install,Pkg,File));
74    return true;
75 }
76                                                                         /*}}}*/
77 // RPMPM::Configure - Configure a package                               /*{{{*/
78 // ---------------------------------------------------------------------
79 /* Add a configure operation to the sequence list */
80 bool pkgRPMPM::Configure(PkgIterator Pkg)
81 {
82    if (Pkg.end() == true) {
83       return false;
84    }
85    
86    List.push_back(Item(Item::Configure,Pkg));
87    return true;
88 }
89                                                                         /*}}}*/
90 // RPMPM::Remove - Remove a package                                     /*{{{*/
91 // ---------------------------------------------------------------------
92 /* Add a remove operation to the sequence list */
93 bool pkgRPMPM::Remove(PkgIterator Pkg,bool Purge)
94 {
95    if (Pkg.end() == true)
96       return false;
97    
98    if (Purge == true)
99       List.push_back(Item(Item::Purge,Pkg));
100    else
101       List.push_back(Item(Item::Remove,Pkg));
102    return true;
103 }
104                                                                         /*}}}*/
105 // RPMPM::RunScripts - Run a set of scripts                             /*{{{*/
106 // ---------------------------------------------------------------------
107 /* This looks for a list of script sto run from the configuration file,
108    each one is run with system from a forked child. */
109 bool pkgRPMPM::RunScripts(const char *Cnf)
110 {
111    Configuration::Item const *Opts = _config->Tree(Cnf);
112    if (Opts == 0 || Opts->Child == 0)
113       return true;
114    Opts = Opts->Child;
115
116    bool error = false;
117    for (; Opts != 0; Opts = Opts->Next)
118    {
119       if (Opts->Value.empty() == true)
120          continue;
121                 
122       // Purified Fork for running the script
123       pid_t Process = ExecFork();      
124       if (Process == 0)
125       {
126          if (chdir("/tmp") != 0)
127             _exit(100);
128
129          const char *Args[4];
130          Args[0] = "/bin/sh";
131          Args[1] = "-c";
132          Args[2] = Opts->Value.c_str();
133          Args[3] = 0;
134          execv(Args[0],(char **)Args);
135          _exit(100);
136       }
137       
138       // Clean up the sub process
139       if (ExecWait(Process,Opts->Value.c_str()) == false) {
140          _error->Error(_("Problem executing scripts %s '%s'"),Cnf,
141                        Opts->Value.c_str());
142          error = true;
143       }
144    }
145  
146    // Restore sig int/quit
147    signal(SIGQUIT,SIG_DFL);
148    signal(SIGINT,SIG_DFL);   
149
150    if (error)
151       return _error->Error(_("Sub-process returned an error code"));
152    
153    return true;
154 }
155
156                                                                         /*}}}*/
157 // RPMPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
158 // ---------------------------------------------------------------------
159 /* This looks for a list of scripts to run from the configuration file
160    each one is run and is fed on standard input a list of all .deb files
161    that are due to be installed. */
162 bool pkgRPMPM::RunScriptsWithPkgs(const char *Cnf)
163 {
164    Configuration::Item const *Opts = _config->Tree(Cnf);
165    if (Opts == 0 || Opts->Child == 0)
166       return true;
167    Opts = Opts->Child;
168    
169    for (; Opts != 0; Opts = Opts->Next)
170    {
171       if (Opts->Value.empty() == true)
172          continue;
173                 
174       // Create the pipes
175       int Pipes[2];
176       if (pipe(Pipes) != 0)
177          return _error->Errno("pipe",_("Failed to create IPC pipe to subprocess"));
178       SetCloseExec(Pipes[0],true);
179       SetCloseExec(Pipes[1],true);
180       
181       // Purified Fork for running the script
182       pid_t Process = ExecFork();      
183       if (Process == 0)
184       {
185          // Setup the FDs
186          dup2(Pipes[0],STDIN_FILENO);
187          SetCloseExec(STDOUT_FILENO,false);
188          SetCloseExec(STDIN_FILENO,false);      
189          SetCloseExec(STDERR_FILENO,false);
190
191          const char *Args[4];
192          Args[0] = "/bin/sh";
193          Args[1] = "-c";
194          Args[2] = Opts->Value.c_str();
195          Args[3] = 0;
196          execv(Args[0],(char **)Args);
197          _exit(100);
198       }
199       close(Pipes[0]);
200       FileFd Fd(Pipes[1]);
201
202       // Feed it the filenames.
203       for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
204       {
205          // Only deal with packages to be installed from .rpm
206          if (I->Op != Item::Install)
207             continue;
208
209          // No errors here..
210          if (I->File[0] != '/')
211             continue;
212          
213          /* Feed the filename of each package that is pending install
214             into the pipe. */
215          if (Fd.Write(I->File.c_str(),I->File.length()) == false || 
216              Fd.Write("\n",1) == false)
217          {
218             kill(Process,SIGINT);           
219             Fd.Close();   
220             ExecWait(Process,Opts->Value.c_str(),true);
221             return _error->Error(_("Failure running script %s"),Opts->Value.c_str());
222          }
223       }
224       Fd.Close();
225       
226       // Clean up the sub process
227       if (ExecWait(Process,Opts->Value.c_str()) == false)
228          return _error->Error(_("Failure running script %s"),Opts->Value.c_str());
229    }
230
231    return true;
232 }
233
234                                                                         /*}}}*/
235
236
237 // RPMPM::Go - Run the sequence                                         /*{{{*/
238 // ---------------------------------------------------------------------
239 /* This globs the operations and calls rpm */
240 bool pkgRPMPM::Go()
241 {
242    if (List.empty() == true)
243       return true;
244
245    if (RunScripts("RPM::Pre-Invoke") == false)
246       return false;
247
248    if (RunScriptsWithPkgs("RPM::Pre-Install-Pkgs") == false)
249       return false;
250    
251    vector<const char*> install_or_upgrade;
252    vector<const char*> install;
253    vector<const char*> upgrade;
254    vector<const char*> uninstall;
255    vector<pkgCache::Package*> pkgs_install;
256    vector<pkgCache::Package*> pkgs_uninstall;
257
258    vector<char*> unalloc;
259    
260    for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
261    {
262       string Name = I->Pkg.Name();
263       string Arch = I->Pkg.CurrentVer().Arch();
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) + "." + Arch;
274          } else if ((loc = Name.rfind("#", Name.length())) != string::npos) {
275             RealName = Name.substr(0,loc) + "-" + I->Pkg.CurrentVer().VerStr() + "." + Arch;
276          } else {
277             RealName = Name + "." + Arch;
278          }
279          uninstall.push_back(strdup(RealName.c_str()));
280          unalloc.push_back(strdup(RealName.c_str()));
281          pkgs_uninstall.push_back(I->Pkg);
282          break;
283
284        case Item::Configure:
285          break;
286
287        case Item::Install:
288          if ((loc = Name.rfind("#", Name.length())) != string::npos) {
289             RealName = Name.substr(0,loc);
290          } 
291          if (Name != RealName) {
292             PkgIterator Pkg = Cache.FindPkg(RealName);
293             PrvIterator Prv = Pkg.ProvidesList();
294             bool Installed = false;
295             for (; Prv.end() == false; Prv++) {
296                if (Prv.OwnerPkg().CurrentVer().end() == false) {
297                   Installed = true;
298                   break;
299                }
300             }
301             if (Installed)
302                install.push_back(I->File.c_str());
303             else
304                upgrade.push_back(I->File.c_str());
305          } else {
306             upgrade.push_back(I->File.c_str());
307          }
308          install_or_upgrade.push_back(I->File.c_str());
309          pkgs_install.push_back(I->Pkg);
310          break;
311           
312        default:
313          return _error->Error(_("Unknown pkgRPMPM operation."));
314       }
315    }
316
317    bool Ret = true;
318
319 #ifdef WITH_LUA
320    if (_lua->HasScripts("Scripts::PM::Pre") == true) {
321       _lua->SetGlobal("files_install", install_or_upgrade);
322       _lua->SetGlobal("names_remove", uninstall);
323       _lua->SetGlobal("pkgs_install", pkgs_install);
324       _lua->SetGlobal("pkgs_remove", pkgs_uninstall);
325       _lua->SetDepCache(&Cache);
326       _lua->RunScripts("Scripts::PM::Pre");
327       _lua->ResetCaches();
328       _lua->ResetGlobals();
329       if (_error->PendingError() == true) {
330          _error->DumpErrors();
331          Ret = false;
332          goto exit;
333       }
334    }
335 #endif
336
337    if (Process(install, upgrade, uninstall) == false)
338       Ret = false;
339
340 #ifdef WITH_LUA
341    if (_lua->HasScripts("Scripts::PM::Post") == true) {
342       _lua->SetGlobal("files_install", install_or_upgrade);
343       _lua->SetGlobal("names_remove", uninstall);
344       _lua->SetGlobal("pkgs_install", pkgs_install);
345       _lua->SetGlobal("pkgs_remove", pkgs_uninstall);
346       _lua->SetGlobal("transaction_success", Ret);
347       _lua->SetDepCache(&Cache);
348       _lua->RunScripts("Scripts::PM::Post");
349       _lua->ResetCaches();
350       _lua->ResetGlobals();
351       if (_error->PendingError() == true) {
352          _error->DumpErrors();
353          Ret = false;
354          goto exit;
355       }
356    }
357 #endif
358
359    
360    if (Ret == true)
361       Ret = RunScripts("RPM::Post-Invoke");
362
363 exit:
364    for (vector<char *>::iterator I = unalloc.begin(); I != unalloc.end(); I++)
365       free(*I);
366
367    return Ret;
368 }
369                                                                         /*}}}*/
370 // pkgRPMPM::Reset - Dump the contents of the command list              /*{{{*/
371 // ---------------------------------------------------------------------
372 /* */
373 void pkgRPMPM::Reset() 
374 {
375    List.erase(List.begin(),List.end());
376 }
377                                                                         /*}}}*/
378 // RPMExtPM::pkgRPMExtPM - Constructor                                  /*{{{*/
379 // ---------------------------------------------------------------------
380 /* */
381 pkgRPMExtPM::pkgRPMExtPM(pkgDepCache *Cache) : pkgRPMPM(Cache)
382 {
383 }
384                                                                         /*}}}*/
385 // RPMExtPM::pkgRPMExtPM - Destructor                                   /*{{{*/
386 // ---------------------------------------------------------------------
387 /* */
388 pkgRPMExtPM::~pkgRPMExtPM()
389 {
390 }
391                                                                         /*}}}*/
392
393 bool pkgRPMExtPM::ExecRPM(Item::RPMOps op, vector<const char*> &files)
394 {
395    const char *Args[10000];      
396    const char *operation = NULL;
397    unsigned int n = 0;
398    bool Interactive = _config->FindB("RPM::Interactive",true);
399    
400    Args[n++] = _config->Find("Dir::Bin::rpm","rpm").c_str();
401
402    bool nodeps = false;
403
404    switch (op)
405    {
406       case Item::RPMInstall:
407          if (Interactive)
408             operation = "-ivh";
409          else
410             operation = "-iv";
411          nodeps = true;
412          break;
413
414       case Item::RPMUpgrade:
415          if (Interactive)
416             operation = "-Uvh";
417          else
418             operation = "-Uv";
419          break;
420
421       case Item::RPMErase:
422          operation = "-e";
423          nodeps = true;
424          break;
425    }
426    Args[n++] = operation;
427
428    if (Interactive == false && op != Item::RPMErase)
429       Args[n++] = "--percent";
430     
431    string rootdir = _config->Find("RPM::RootDir", "");
432    if (!rootdir.empty()) 
433    {
434        Args[n++] = "-r";
435        Args[n++] = rootdir.c_str();
436    }
437
438    Configuration::Item const *Opts;
439    if (op == Item::RPMErase)
440    {
441       Opts = _config->Tree("RPM::Erase-Options");
442       if (Opts != 0)
443       {
444          Opts = Opts->Child;
445          for (; Opts != 0; Opts = Opts->Next)
446          {
447             if (Opts->Value == "--nodeps")
448                nodeps = false;
449             else if (Opts->Value.empty() == true)
450                continue;
451             Args[n++] = Opts->Value.c_str();
452          }
453       }
454    }
455    else
456    {
457       bool oldpackage = _config->FindB("RPM::OldPackage",
458                                        (op == Item::RPMUpgrade));
459       bool replacepkgs = _config->FindB("APT::Get::ReInstall",false);
460       bool replacefiles = _config->FindB("APT::Get::ReInstall",false);
461       Opts = _config->Tree("RPM::Install-Options");
462       if (Opts != 0)
463       {
464          Opts = Opts->Child;
465          for (; Opts != 0; Opts = Opts->Next)
466          {
467             if (Opts->Value == "--oldpackage")
468                oldpackage = false;
469             else if (Opts->Value == "--replacepkgs")
470                replacepkgs = false;
471             else if (Opts->Value == "--replacefiles")
472                replacefiles = false;
473             else if (Opts->Value == "--nodeps")
474                nodeps = false;
475             else if (Opts->Value.empty() == true)
476                continue;
477             Args[n++] = Opts->Value.c_str();
478          }       
479       }
480       if (oldpackage == true)
481          Args[n++] = "--oldpackage";
482       if (replacepkgs == true)
483          Args[n++] = "--replacepkgs";
484       if (replacefiles == true)
485          Args[n++] = "--replacefiles";
486    }
487
488    if (nodeps == true)
489       Args[n++] = "--nodeps";
490
491    Opts = _config->Tree("RPM::Options");
492    if (Opts != 0)
493    {
494       Opts = Opts->Child;
495       for (; Opts != 0; Opts = Opts->Next)
496       {
497          if (Opts->Value.empty() == true)
498             continue;
499          Args[n++] = Opts->Value.c_str();
500       }  
501    }
502
503    if (_config->FindB("RPM::Order", false) == false)
504       Args[n++] = "--noorder";
505     
506    bool FilesInArgs = true;
507    char *ArgsFileName = NULL;
508 #if RPM_VERSION >= 0x040000
509    if (op != Item::RPMErase && files.size() > 50) {
510       string FileName = _config->FindDir("Dir::Cache", "/tmp/") +
511                         "filelist.XXXXXX";
512       ArgsFileName = strdup(FileName.c_str());
513       if (ArgsFileName) {
514          int fd = mkstemp(ArgsFileName);
515          if (fd != -1) {
516             FileFd File(fd);
517             for (vector<const char*>::iterator I = files.begin();
518                  I != files.end(); I++) {
519                File.Write(*I, strlen(*I));
520                File.Write("\n", 1);
521             }
522             File.Close();
523             FilesInArgs = false;
524             Args[n++] = ArgsFileName;
525          }
526       }
527    }
528 #endif
529
530    if (FilesInArgs == true) {
531       for (vector<const char*>::iterator I = files.begin();
532            I != files.end(); I++)
533          Args[n++] = *I;
534    }
535    
536    Args[n++] = 0;
537
538    if (_config->FindB("Debug::pkgRPMPM",false) == true)
539    {
540       for (unsigned int k = 0; k < n; k++)
541           clog << Args[k] << ' ';
542       clog << endl;
543       if (ArgsFileName) {
544          unlink(ArgsFileName);
545          free(ArgsFileName);
546       }
547       return true;
548    }
549
550    cout << _("Executing RPM (")<<operation<<")..." << endl;
551
552    cout << flush;
553    clog << flush;
554    cerr << flush;
555
556    /* Mask off sig int/quit. We do this because dpkg also does when 
557     it forks scripts. What happens is that when you hit ctrl-c it sends
558     it to all processes in the group. Since dpkg ignores the signal 
559     it doesn't die but we do! So we must also ignore it */
560    //akk ??
561    signal(SIGQUIT,SIG_IGN);
562    signal(SIGINT,SIG_IGN);
563
564    // Fork rpm
565    pid_t Child = ExecFork();
566             
567    // This is the child
568    if (Child == 0)
569    {
570       if (chdir(_config->FindDir("RPM::Run-Directory","/").c_str()) != 0)
571           _exit(100);
572          
573       if (_config->FindB("RPM::FlushSTDIN",true) == true)
574       {
575          int Flags,dummy;
576          if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
577              _exit(100);
578          
579          // Discard everything in stdin before forking dpkg
580          if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
581              _exit(100);
582          
583          while (read(STDIN_FILENO,&dummy,1) == 1);
584          
585          if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
586              _exit(100);
587       }
588
589       execvp(Args[0],(char **)Args);
590       cerr << _("Could not exec ") << Args[0] << endl;
591       _exit(100);
592    }      
593    
594    // Wait for rpm
595    int Status = 0;
596    while (waitpid(Child,&Status,0) != Child)
597    {
598       if (errno == EINTR)
599           continue;
600       RunScripts("RPM::Post-Invoke");
601       if (ArgsFileName) {
602          unlink(ArgsFileName);
603          free(ArgsFileName);
604       }
605       return _error->Errno("waitpid",_("Couldn't wait for subprocess"));
606    }
607    if (ArgsFileName) {
608       unlink(ArgsFileName);
609       free(ArgsFileName);
610    }
611
612    // Restore sig int/quit
613    signal(SIGQUIT,SIG_DFL);
614    signal(SIGINT,SIG_DFL);
615        
616    // Check for an error code.
617    if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
618    {
619       RunScripts("RPM::Post-Invoke");
620       if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
621           return _error->Error(_("Sub-process %s recieved a segmentation fault."),Args[0]);
622       
623       if (WIFEXITED(Status) != 0)
624           return _error->Error(_("Sub-process %s returned an error code (%u)"),Args[0],
625                                WEXITSTATUS(Status));
626       
627       return _error->Error(_("Sub-process %s exited unexpectedly"),Args[0]);
628    }
629
630    if (Interactive == true)
631       cout << _("Done.") << endl;
632
633    return true;
634 }
635
636 bool pkgRPMExtPM::Process(vector<const char*> &install, 
637                        vector<const char*> &upgrade,
638                        vector<const char*> &uninstall)
639 {
640    if (uninstall.empty() == false)
641        ExecRPM(Item::RPMErase, uninstall);
642    if (install.empty() == false)
643        ExecRPM(Item::RPMInstall, install);
644    if (upgrade.empty() == false)
645        ExecRPM(Item::RPMUpgrade, upgrade);
646    return true;
647 }
648
649 // RPMLibPM::pkgRPMLibPM - Constructor                                  /*{{{*/
650 // ---------------------------------------------------------------------
651 /* */
652 pkgRPMLibPM::pkgRPMLibPM(pkgDepCache *Cache) : pkgRPMPM(Cache)
653 {
654 }
655                                                                         /*}}}*/
656 // RPMLibPM::pkgRPMLibPM - Destructor                                   /*{{{*/
657 // ---------------------------------------------------------------------
658 /* */
659 pkgRPMLibPM::~pkgRPMLibPM()
660 {
661 }
662                                                                         /*}}}*/
663
664 bool pkgRPMLibPM::AddToTransaction(Item::RPMOps op, vector<const char*> &files)
665 {
666    int rc;
667    FD_t fd;
668    rpmHeader hdr;
669
670    for (vector<const char*>::iterator I = files.begin(); I != files.end(); I++)
671    {
672       int upgrade = 0;
673
674       switch (op)
675       {
676          case Item::RPMUpgrade:
677             upgrade = 1;
678          case Item::RPMInstall:
679             fd = Fopen(*I, "r.ufdio");
680             if (fd == NULL)
681                _error->Error(_("Failed opening %s"), *I);
682 #if RPM_VERSION >= 0x040100
683             rc = rpmReadPackageFile(TS, fd, *I, &hdr);
684             if (rc != RPMRC_OK && rc != RPMRC_NOTTRUSTED && rc != RPMRC_NOKEY)
685                _error->Error(_("Failed reading file %s"), *I);
686             rc = rpmtsAddInstallElement(TS, hdr, *I, upgrade, 0);
687 #else
688             rc = rpmReadPackageHeader(fd, &hdr, 0, NULL, NULL);
689             if (rc)
690                _error->Error(_("Failed reading file %s"), *I);
691             rc = rpmtransAddPackage(TS, hdr, NULL, *I, upgrade, 0);
692 #endif
693             if (rc)
694                _error->Error(_("Failed adding %s to transaction %s"),
695                              *I, "(install)");
696             headerFree(hdr);
697             Fclose(fd);
698             break;
699
700          case Item::RPMErase:
701 #if RPM_VERSION >= 0x040000
702             rpmdbMatchIterator MI;
703 #if RPM_VERSION >= 0x040100
704             MI = rpmtsInitIterator(TS, (rpmTag)RPMDBI_LABEL, *I, 0);
705 #else
706             MI = rpmdbInitIterator(DB, RPMDBI_LABEL, *I, 0);
707 #endif
708             while ((hdr = rpmdbNextIterator(MI)) != NULL) 
709             {
710                unsigned int recOffset = rpmdbGetIteratorOffset(MI);
711                if (recOffset) {
712 #if RPM_VERSION >= 0x040100
713                   rc = rpmtsAddEraseElement(TS, hdr, recOffset);
714 #else
715                   rc = rpmtransRemovePackage(TS, recOffset);
716 #endif
717                   if (rc)
718                      _error->Error(_("Failed adding %s to transaction %s"),
719                                    *I, "(erase)");
720                }
721             }
722             MI = rpmdbFreeIterator(MI);
723 #else // RPM 3.X
724             dbiIndexSet matches;
725             rc = rpmdbFindByLabel(DB, *I, &matches);
726             if (rc == 0) {
727                for (int i = 0; i < dbiIndexSetCount(matches); i++) {
728                   unsigned int recOffset = dbiIndexRecordOffset(matches, i);
729                   if (recOffset)
730                      rpmtransRemovePackage(TS, recOffset);
731                }
732             }
733 #endif
734             break;
735       }
736    }
737    return true;
738 }
739
740 bool pkgRPMLibPM::Process(vector<const char*> &install, 
741                           vector<const char*> &upgrade,
742                           vector<const char*> &uninstall)
743 {
744    int rc = 0;
745    bool Success = false;
746    bool Interactive = _config->FindB("RPM::Interactive",true);
747    string Dir = _config->Find("RPM::RootDir");
748    rpmReadConfigFiles(NULL, NULL);
749
750    int probFilter = 0;
751    int notifyFlags = 0;
752    int tsFlags = 0;
753
754    if (uninstall.empty() == false)
755       ParseRpmOpts("RPM::Erase-Options", &tsFlags, &probFilter);
756    if (install.empty() == false || upgrade.empty() == false)
757       ParseRpmOpts("RPM::Install-Options", &tsFlags, &probFilter);
758    ParseRpmOpts("RPM::Options", &tsFlags, &probFilter);
759
760 #if RPM_VERSION >= 0x040100
761    rpmps probs;
762    TS = rpmtsCreate();
763    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
764    // 4.1 needs this always set even if NULL,
765    // otherwise all scriptlets fail
766    rpmtsSetRootDir(TS, Dir.c_str());
767    if (rpmtsOpenDB(TS, O_RDWR) != 0)
768    {
769       _error->Error(_("Could not open RPM database"));
770       goto exit;
771    }
772 #else
773    rpmProblemSet probs;
774    const char *RootDir = NULL;
775    if (!Dir.empty())
776       RootDir = Dir.c_str();
777    if (rpmdbOpen(RootDir, &DB, O_RDWR, 0644) != 0)
778    {
779       _error->Error(_("Could not open RPM database"));
780       goto exit;
781    }
782    TS = rpmtransCreateSet(DB, Dir.c_str());
783 #endif
784
785 #if RPM_VERSION >= 0x040000
786    if (rpmExpandNumeric("%{?_repackage_all_erasures}"))
787       tsFlags |= RPMTRANS_FLAG_REPACKAGE;
788 #endif
789                      
790 #if RPM_VERSION >= 0x040300
791    /* Initialize security context patterns for SELinux */
792    if (!(tsFlags & RPMTRANS_FLAG_NOCONTEXTS)) {
793       rpmsx sx = rpmtsREContext(TS);
794       if (sx == NULL) {
795          const char *fn = rpmGetPath("%{?_install_file_context_path}", NULL);
796          if (fn != NULL && *fn != '\0') {
797             sx = rpmsxNew(fn);
798             (void) rpmtsSetREContext(TS, sx);
799          }
800          fn = (const char *) _free(fn);
801       }
802       sx = rpmsxFree(sx);
803    }
804 #endif
805
806    if (_config->FindB("RPM::OldPackage", true) || !upgrade.empty()) {
807       probFilter |= RPMPROB_FILTER_OLDPACKAGE;
808    }
809    if (_config->FindB("APT::Get::ReInstall", false)) {
810       probFilter |= RPMPROB_FILTER_REPLACEPKG;
811       probFilter |= RPMPROB_FILTER_REPLACEOLDFILES;
812       probFilter |= RPMPROB_FILTER_REPLACENEWFILES;
813    }
814
815    if (Interactive == true)
816        notifyFlags |= INSTALL_LABEL | INSTALL_HASH;
817    else
818        notifyFlags |= INSTALL_LABEL | INSTALL_PERCENT;
819
820    if (uninstall.empty() == false)
821        AddToTransaction(Item::RPMErase, uninstall);
822    if (install.empty() == false)
823        AddToTransaction(Item::RPMInstall, install);
824    if (upgrade.empty() == false)
825        AddToTransaction(Item::RPMUpgrade, upgrade);
826
827    // Setup the gauge used by rpmShowProgress.
828    packagesTotal = install.size()+upgrade.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", false) == true)
863       rc = rpmtsOrder(TS);
864 #else
865    if (_config->FindB("RPM::Order", false) == 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, rpmShowProgress, (void *)notifyFlags);
881    rc = rpmtsRun(TS, NULL, (rpmprobFilterFlags)probFilter);
882    probs = rpmtsProblems(TS);
883 #else
884    rc = rpmRunTransactions(TS, rpmShowProgress, (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