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