@@ -17,11 +17,14 @@ limitations under the License.
17
17
package differs
18
18
19
19
import (
20
+ "bufio"
20
21
"bytes"
21
22
"context"
23
+ "errors"
22
24
"fmt"
23
25
"math/rand"
24
26
"os"
27
+ "os/exec"
25
28
"path/filepath"
26
29
"strconv"
27
30
"strings"
@@ -40,6 +43,11 @@ import (
40
43
"github.com/sirupsen/logrus"
41
44
)
42
45
46
+ //RPM command to extract packages from the rpm database
47
+ var rpmCmd = []string {
48
+ "rpm" , "--nodigest" , "--nosignature" ,
49
+ "-qa" , "--qf" , "%{NAME}\t %{VERSION}\t %{SIZE}\n " ,
50
+ }
43
51
var letters = []rune ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" )
44
52
45
53
// daemonMutex is required to protect against other go-routines, as
@@ -87,7 +95,65 @@ func (a RPMAnalyzer) getPackages(image pkgutil.Image) (map[string]util.PackageIn
87
95
}
88
96
}
89
97
90
- return rpmDataFromContainer (image )
98
+ packages , err := rpmDataFromImageFS (image )
99
+ if err != nil {
100
+ logrus .Warn ("Trying to run the RPM binary of the image in a container" )
101
+ return rpmDataFromContainer (image )
102
+ }
103
+ return packages , err
104
+ }
105
+
106
+ // rpmDataFromImageFS runs a local rpm binary, if any, to query the image
107
+ // rpmdb and returns a map of installed packages.
108
+ func rpmDataFromImageFS (image pkgutil.Image ) (map [string ]util.PackageInfo , error ) {
109
+ packages := make (map [string ]util.PackageInfo )
110
+ // Check there is an executable rpm tool in host
111
+ if err := exec .Command ("rpm" , "--version" ).Run (); err != nil {
112
+ logrus .Warn ("No RPM binary in host" )
113
+ return packages , err
114
+ }
115
+ dbPath , err := rpmDBPath (image .FSPath )
116
+ if err != nil {
117
+ logrus .Warnf ("Couldn't find RPM database: %s" , err .Error ())
118
+ return packages , err
119
+ }
120
+ cmdArgs := append ([]string {"--root" , image .FSPath , "--dbpath" , dbPath }, rpmCmd [1 :]... )
121
+ out , err := exec .Command (rpmCmd [0 ], cmdArgs ... ).Output ()
122
+ if err != nil {
123
+ logrus .Warnf ("RPM call failed: %s" , err .Error ())
124
+ return packages , err
125
+ }
126
+ output := strings .Split (string (out ), "\n " )
127
+ return parsePackageData (output )
128
+ }
129
+
130
+ // rpmDBPath tries to get the RPM database path from the /usr/lib/rpm/macros
131
+ // file in the image rootfs.
132
+ func rpmDBPath (rootFSPath string ) (string , error ) {
133
+ rpmMacros , err := os .Open (filepath .Join (rootFSPath , "usr/lib/rpm/macros" ))
134
+ if err != nil {
135
+ return "" , err
136
+ }
137
+ defer rpmMacros .Close ()
138
+
139
+ scanner := bufio .NewScanner (rpmMacros )
140
+ for scanner .Scan () {
141
+ line := strings .TrimSpace (scanner .Text ())
142
+ if strings .HasPrefix (line , "%_dbpath" ) {
143
+ fields := strings .Fields (line )
144
+ if len (fields ) < 2 {
145
+ break
146
+ }
147
+ out , err := exec .Command ("rpm" , "-E" , fields [1 ]).Output ()
148
+ if err != nil {
149
+ return "" , err
150
+ }
151
+ dbPath := strings .TrimRight (string (out ), "\r \n " )
152
+ _ , err = os .Stat (filepath .Join (rootFSPath , dbPath ))
153
+ return dbPath , err
154
+ }
155
+ }
156
+ return "" , errors .New ("Failed parsing macros file" )
91
157
}
92
158
93
159
// rpmDataFromContainer runs image in a container, queries the data of
@@ -114,7 +180,7 @@ func rpmDataFromContainer(image pkgutil.Image) (map[string]util.PackageInfo, err
114
180
defer logrus .Infof ("Removing image %s" , imageName )
115
181
116
182
contConf := godocker.Config {
117
- Entrypoint : [] string { "rpm" , "--nodigest" , "--nosignature" , "-qa" , "--qf" , "%{NAME} \t %{VERSION} \t %{SIZE} \n " } ,
183
+ Entrypoint : rpmCmd ,
118
184
Image : imageName ,
119
185
}
120
186
0 commit comments