-
Notifications
You must be signed in to change notification settings - Fork 237
Try to use local rpm binary to query rpmdb #244
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,11 +17,14 @@ limitations under the License. | |
package differs | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"context" | ||
"errors" | ||
"fmt" | ||
"math/rand" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
|
@@ -40,6 +43,11 @@ import ( | |
"github.com/sirupsen/logrus" | ||
) | ||
|
||
//RPM command to extract packages from the rpm database | ||
var rpmCmd = []string{ | ||
"rpm", "--nodigest", "--nosignature", | ||
"-qa", "--qf", "%{NAME}\t%{VERSION}\t%{SIZE}\n", | ||
} | ||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") | ||
|
||
// 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 | |
} | ||
} | ||
|
||
return rpmDataFromContainer(image) | ||
packages, err := rpmDataFromImageFS(image) | ||
if err != nil { | ||
logrus.Warn("Trying to run the RPM binary of the image in a container") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: "Running RPM binary from image in a container" |
||
return rpmDataFromContainer(image) | ||
} | ||
return packages, err | ||
} | ||
|
||
// rpmDataFromImageFS runs a local rpm binary, if any, to query the image | ||
// rpmdb and returns a map of installed packages. | ||
func rpmDataFromImageFS(image pkgutil.Image) (map[string]util.PackageInfo, error) { | ||
packages := make(map[string]util.PackageInfo) | ||
// Check there is an executable rpm tool in host | ||
if err := exec.Command("rpm", "--version").Run(); err != nil { | ||
logrus.Warn("No RPM binary in host") | ||
return packages, err | ||
} | ||
dbPath, err := rpmDBPath(image.FSPath) | ||
if err != nil { | ||
logrus.Warnf("Couldn't find RPM database: %s", err.Error()) | ||
return packages, err | ||
} | ||
cmdArgs := append([]string{"--root", image.FSPath, "--dbpath", dbPath}, rpmCmd[1:]...) | ||
out, err := exec.Command(rpmCmd[0], cmdArgs...).Output() | ||
if err != nil { | ||
logrus.Warnf("RPM call failed: %s", err.Error()) | ||
return packages, err | ||
} | ||
output := strings.Split(string(out), "\n") | ||
return parsePackageData(output) | ||
} | ||
|
||
// rpmDBPath tries to get the RPM database path from the /usr/lib/rpm/macros | ||
// file in the image rootfs. | ||
func rpmDBPath(rootFSPath string) (string, error) { | ||
rpmMacros, err := os.Open(filepath.Join(rootFSPath, "usr/lib/rpm/macros")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make this path a constant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it possible that the RPM database could have been put in a different location by the creator of the image? are there other common locations that we might want to check here as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we are looking for the value of %_dbpath macro which sets the path of the database.
Yes, it could be the case that the user sets its own %_dbpath in a custom user defined macro. As far as I saw in fedora, opensuse and centos, the %_dbpath defined in /usr/lib/rpm/macros is not overwritten in any of them. According to the man pages RPM looks for macros in:
The macros from the image could be loaded by using the That's the reason to manually parse the |
||
if err != nil { | ||
return "", err | ||
} | ||
defer rpmMacros.Close() | ||
|
||
scanner := bufio.NewScanner(rpmMacros) | ||
for scanner.Scan() { | ||
line := strings.TrimSpace(scanner.Text()) | ||
if strings.HasPrefix(line, "%_dbpath") { | ||
fields := strings.Fields(line) | ||
if len(fields) < 2 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you add a comment with an example of what you're trying to match for here? maybe a regex would be more clear? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will add a comment with the example, I think this is the clearest. |
||
break | ||
} | ||
out, err := exec.Command("rpm", "-E", fields[1]).Output() | ||
if err != nil { | ||
return "", err | ||
} | ||
dbPath := strings.TrimRight(string(out), "\r\n") | ||
_, err = os.Stat(filepath.Join(rootFSPath, dbPath)) | ||
return dbPath, err | ||
} | ||
} | ||
return "", errors.New("Failed parsing macros file") | ||
} | ||
|
||
// rpmDataFromContainer runs image in a container, queries the data of | ||
|
@@ -114,7 +180,7 @@ func rpmDataFromContainer(image pkgutil.Image) (map[string]util.PackageInfo, err | |
defer logrus.Infof("Removing image %s", imageName) | ||
|
||
contConf := godocker.Config{ | ||
Entrypoint: []string{"rpm", "--nodigest", "--nosignature", "-qa", "--qf", "%{NAME}\t%{VERSION}\t%{SIZE}\n"}, | ||
Entrypoint: rpmCmd, | ||
Image: imageName, | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's log this at
Info
level