4a25c5acff9aac4a7c0fe3ac1e719f2ddb967724
[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 Arch = "";
263       string RealName = Name;
264       string::size_type loc;
265
266       switch (I->Op)
267       {
268       case Item::Purge:
269       case Item::Remove:
270          // Unmunge our package names so rpm can find them...
271          Arch = I->Pkg.CurrentVer().Arch();
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", true) == 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 #else
768    rpmProblemSet probs;
769    const char *RootDir = NULL;
770    if (!Dir.empty())
771       RootDir = Dir.c_str();
772    if (rpmdbOpen(RootDir, &DB, O_RDWR, 0644) != 0)
773    {
774       _error->Error(_("Could not open RPM database"));
775       goto exit;
776    }
777    TS = rpmtransCreateSet(DB, Dir.c_str());
778 #endif
779
780 #if RPM_VERSION >= 0x040000
781    if (rpmExpandNumeric("%{?_repackage_all_erasures}"))
782       tsFlags |= RPMTRANS_FLAG_REPACKAGE;
783 #endif
784                      
785 #if RPM_VERSION >= 0x040300
786    /* Initialize security context patterns for SELinux */
787    if (!(tsFlags & RPMTRANS_FLAG_NOCONTEXTS)) {
788       rpmsx sx = rpmtsREContext(TS);
789       if (sx == NULL) {
790          const char *fn = rpmGetPath("%{?_install_file_context_path}", NULL);
791          if (fn != NULL && *fn != '\0') {
792             sx = rpmsxNew(fn);
793             (void) rpmtsSetREContext(TS, sx);
794          }
795          fn = (const char *) _free(fn);
796       }
797       sx = rpmsxFree(sx);
798    }
799 #endif
800
801    if (_config->FindB("RPM::OldPackage", true) || !upgrade.empty()) {
802       probFilter |= RPMPROB_FILTER_OLDPACKAGE;
803    }
804    if (_config->FindB("APT::Get::ReInstall", false)) {
805       probFilter |= RPMPROB_FILTER_REPLACEPKG;
806       probFilter |= RPMPROB_FILTER_REPLACEOLDFILES;
807       probFilter |= RPMPROB_FILTER_REPLACENEWFILES;
808    }
809
810    if (_config->FindI("quiet",0) >= 1)
811        notifyFlags |= INSTALL_LABEL;
812    else if (Interactive == true) 
813        notifyFlags |= INSTALL_LABEL | INSTALL_HASH;
814    else 
815        notifyFlags |= INSTALL_LABEL | INSTALL_PERCENT;
816
817    if (uninstall.empty() == false)
818        AddToTransaction(Item::RPMErase, uninstall);
819    if (install.empty() == false)
820        AddToTransaction(Item::RPMInstall, install);
821    if (upgrade.empty() == false)
822        AddToTransaction(Item::RPMUpgrade, upgrade);
823
824    // FIXME: This ain't right because most things show up in upgrade 
825    // even if they're really just installs, and repackaging isn't taken
826    // into account either.
827    packagesTotal = install.size() + upgrade.size() * 2 + uninstall.size();
828
829 #if RPM_VERSION >= 0x040100
830    if (_config->FindB("RPM::NoDeps", false) == false) {
831       rc = rpmtsCheck(TS);
832       probs = rpmtsProblems(TS);
833       if (rc || probs->numProblems > 0) {
834          rpmpsPrint(NULL, probs);
835          rpmpsFree(probs);
836          _error->Error(_("Transaction set check failed"));
837          goto exit;
838       }
839    }
840 #else
841 #if RPM_VERSION < 0x040000
842    rpmDependencyConflict *conflicts;
843 #else
844    rpmDependencyConflict conflicts;
845 #endif
846    if (_config->FindB("RPM::NoDeps", false) == false) {
847       int numConflicts;
848       if (rpmdepCheck(TS, &conflicts, &numConflicts)) {
849          _error->Error(_("Transaction set check failed"));
850          if (conflicts) {
851             printDepProblems(stderr, conflicts, numConflicts);
852             rpmdepFreeConflicts(conflicts, numConflicts);
853          }
854          goto exit;
855       }
856    }
857 #endif
858
859    rc = 0;
860 #if RPM_VERSION >= 0x040100
861    if (_config->FindB("RPM::Order", true) == true)
862       rc = rpmtsOrder(TS);
863 #else
864    if (_config->FindB("RPM::Order", true) == true)
865       rc = rpmdepOrder(TS);
866 #endif
867
868    if (rc > 0) {
869       _error->Error(_("Ordering failed for %d packages"), rc);
870       goto exit;
871    }
872
873    cout << _("Committing changes...") << endl << flush;
874
875 #if RPM_VERSION >= 0x040100
876    probFilter |= rpmtsFilterFlags(TS);
877    rpmtsSetFlags(TS, (rpmtransFlags)(rpmtsFlags(TS) | tsFlags));
878    rpmtsClean(TS);
879    rc = rpmtsSetNotifyCallback(TS, rpmpmShowProgress, (void *)notifyFlags);
880    rc = rpmtsRun(TS, NULL, (rpmprobFilterFlags)probFilter);
881    probs = rpmtsProblems(TS);
882 #else
883    rc = rpmRunTransactions(TS, rpmpmShowProgress, (void *)notifyFlags, NULL,
884                            &probs, (rpmtransFlags)tsFlags,
885                            (rpmprobFilterFlags)probFilter);
886 #endif
887
888    if (rc > 0) {
889       _error->Error(_("Error while running transaction"));
890       if (probs->numProblems > 0)
891          rpmpsPrint(stderr, probs);
892    } else {
893       Success = true;
894       if (rc < 0)
895          _error->Warning(_("Some errors occurred while running transaction"));
896       else if (Interactive == true)
897          cout << _("Done.") << endl;
898    }
899    rpmpsFree(probs);
900
901 exit:
902
903 #if RPM_VERSION >= 0x040100
904    rpmtsFree(TS);
905 #else
906    rpmdbClose(DB);
907 #endif
908
909    return Success;
910 }
911
912 bool pkgRPMLibPM::ParseRpmOpts(const char *Cnf, int *tsFlags, int *probFilter)
913 {
914    Configuration::Item const *Opts = _config->Tree(Cnf);
915    
916    if (Opts != 0)
917    {
918       Opts = Opts->Child;
919       for (; Opts != 0; Opts = Opts->Next)
920       {
921          if (Opts->Value.empty() == true)
922             continue;
923          // Transaction set flags
924          if (Opts->Value == "--noscripts")
925             *tsFlags |= RPMTRANS_FLAG_NOSCRIPTS;
926          else if (Opts->Value == "--notriggers")
927             *tsFlags |= RPMTRANS_FLAG_NOTRIGGERS;
928          else if (Opts->Value == "--nodocs" ||
929                   Opts->Value == "--excludedocs")
930             *tsFlags |= RPMTRANS_FLAG_NODOCS;
931          else if (Opts->Value == "--allfiles")
932             *tsFlags |= RPMTRANS_FLAG_ALLFILES;
933          else if (Opts->Value == "--justdb")
934             *tsFlags |= RPMTRANS_FLAG_JUSTDB;
935          else if (Opts->Value == "--test")
936             *tsFlags |= RPMTRANS_FLAG_TEST;
937 #if RPM_VERSION >= 0x040000
938          else if (Opts->Value == "--nomd5")
939             *tsFlags |= RPMTRANS_FLAG_NOMD5;
940          else if (Opts->Value == "--repackage")
941             *tsFlags |= RPMTRANS_FLAG_REPACKAGE;
942 #endif
943 #if RPM_VERSION >= 0x040200
944          else if (Opts->Value == "--noconfigs" ||
945                   Opts->Value == "--excludeconfigs")
946             *tsFlags |= RPMTRANS_FLAG_NOCONFIGS;
947 #endif
948 #if RPM_VERSION >= 0x040300
949          else if (Opts->Value == "--nocontexts")
950             *tsFlags |= RPMTRANS_FLAG_NOCONTEXTS;
951 #endif
952
953          // Problem filter flags
954          else if (Opts->Value == "--replacefiles")
955          {
956             *probFilter |= RPMPROB_FILTER_REPLACEOLDFILES;
957             *probFilter |= RPMPROB_FILTER_REPLACENEWFILES;
958          }
959          else if (Opts->Value == "--replacepkgs")
960             *probFilter |= RPMPROB_FILTER_REPLACEPKG;
961          else if (Opts->Value == "--ignoresize")
962          {
963             *probFilter |= RPMPROB_FILTER_DISKSPACE;
964 #if RPM_VERSION >= 0x040000
965             *probFilter |= RPMPROB_FILTER_DISKNODES;
966 #endif
967          }
968          else if (Opts->Value == "--badreloc")
969             *probFilter |= RPMPROB_FILTER_FORCERELOCATE;
970
971          // Misc things having apt config counterparts
972          else if (Opts->Value == "--force")
973             _config->Set("APT::Get::ReInstall", true);
974          else if (Opts->Value == "--oldpackage")
975             _config->Set("RPM::OldPackage", true);
976          else if (Opts->Value == "--nodeps")
977             _config->Set("RPM::NoDeps", true);
978          else if (Opts->Value == "--noorder")
979             _config->Set("RPM::Order", false);
980          else if (Opts->Value == "-v") {
981             rpmIncreaseVerbosity();
982          } else if (Opts->Value == "-vv") {
983             rpmIncreaseVerbosity();
984             rpmIncreaseVerbosity();
985          } else if (Opts->Value == "-vvv") {
986             rpmIncreaseVerbosity();
987             rpmIncreaseVerbosity();
988             rpmIncreaseVerbosity();
989          }
990          // TODO: --root, --relocate, --prefix, --excludepath etc...
991
992       }
993    }
994    return true;
995
996 #endif /* HAVE_RPM */
997
998 // vim:sts=3:sw=3