feat(fast-root): get running procs for each pkgs (all RHEL, CentOS, AmazonLinux, Ubuntu, Debian) (#855)
* fix(scan): exec yum-plugin-ps on RHEL6 and 7 * feat(yumps): get affected procs on RHEL6 and RHEL8 * feat(scan): get affected processes for each packages * tuning * feat(scan): get running procs for each pkgs on Debian, Ubuntu
This commit is contained in:
		@@ -80,10 +80,11 @@ func (o *amazon) sudoNoPasswdCmdsFast() []cmd {
 | 
			
		||||
 | 
			
		||||
func (o *amazon) sudoNoPasswdCmdsFastRoot() []cmd {
 | 
			
		||||
	return []cmd{
 | 
			
		||||
		{"yum -q ps all --color=never", exitStatusZero},
 | 
			
		||||
		{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
		{"needs-restarting", exitStatusZero},
 | 
			
		||||
		{"which which", exitStatusZero},
 | 
			
		||||
		{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
		{"ls -l /proc/1/exe", exitStatusZero},
 | 
			
		||||
		{"cat /proc/1/maps", exitStatusZero},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -100,3 +101,7 @@ func (o rootPrivAmazon) repoquery() bool {
 | 
			
		||||
func (o rootPrivAmazon) yumMakeCache() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivAmazon) yumPS() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								scan/base.go
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								scan/base.go
									
									
									
									
									
								
							@@ -32,6 +32,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
@@ -722,3 +723,61 @@ func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
 | 
			
		||||
	}
 | 
			
		||||
	return plugins, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) ps() (stdout string, err error) {
 | 
			
		||||
	cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm | awk '{print $1,$2}'`
 | 
			
		||||
	r := l.exec(util.PrependProxyEnv(cmd), noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return r.Stdout, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) parsePs(stdout string) map[string]string {
 | 
			
		||||
	pidNames := map[string]string{}
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(stdout))
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := strings.TrimSpace(scanner.Text())
 | 
			
		||||
		ss := strings.Fields(line)
 | 
			
		||||
		if len(ss) < 2 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		pidNames[ss[0]] = ss[1]
 | 
			
		||||
	}
 | 
			
		||||
	return pidNames
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) lsProcExe(pid string) (stdout string, err error) {
 | 
			
		||||
	cmd := fmt.Sprintf("ls -l /proc/%s/exe", pid)
 | 
			
		||||
	r := l.exec(util.PrependProxyEnv(cmd), sudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return r.Stdout, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) parseLsProcExe(stdout string) (string, error) {
 | 
			
		||||
	ss := strings.Fields(stdout)
 | 
			
		||||
	if len(ss) < 11 {
 | 
			
		||||
		return "", xerrors.Errorf("Unknown format: %s", stdout)
 | 
			
		||||
	}
 | 
			
		||||
	return ss[10], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) grepProcMap(pid string) (stdout string, err error) {
 | 
			
		||||
	cmd := fmt.Sprintf(`cat /proc/%s/maps 2>/dev/null | grep -v " 00:00 " | awk '{print $6}' | sort -n | uniq`, pid)
 | 
			
		||||
	r := l.exec(util.PrependProxyEnv(cmd), sudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return r.Stdout, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) parseGrepProcMap(stdout string) (soPaths []string) {
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(stdout))
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := strings.TrimSpace(scanner.Text())
 | 
			
		||||
		soPaths = append(soPaths, line)
 | 
			
		||||
	}
 | 
			
		||||
	return soPaths
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,13 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/bundler"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/cargo"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/composer"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/npm"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/pipenv"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/poetry"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/yarn"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParseDockerPs(t *testing.T) {
 | 
			
		||||
@@ -178,3 +185,70 @@ func TestParseSystemctlStatus(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_base_parseLsProcExe(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		want    string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "systemd",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: "lrwxrwxrwx 1 root root 0 Jun 29 17:13 /proc/1/exe -> /lib/systemd/systemd",
 | 
			
		||||
			},
 | 
			
		||||
			want:    "/lib/systemd/systemd",
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			l := &base{}
 | 
			
		||||
			got, err := l.parseLsProcExe(tt.args.stdout)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("base.parseLsProcExe() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("base.parseLsProcExe() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_base_parseGrepProcMap(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name        string
 | 
			
		||||
		args        args
 | 
			
		||||
		wantSoPaths []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "systemd",
 | 
			
		||||
			args: args{
 | 
			
		||||
				`/etc/selinux/targeted/contexts/files/file_contexts.bin
 | 
			
		||||
/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin
 | 
			
		||||
/usr/lib64/libdl-2.28.so`,
 | 
			
		||||
			},
 | 
			
		||||
			wantSoPaths: []string{
 | 
			
		||||
				"/etc/selinux/targeted/contexts/files/file_contexts.bin",
 | 
			
		||||
				"/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin",
 | 
			
		||||
				"/usr/lib64/libdl-2.28.so",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			l := &base{}
 | 
			
		||||
			if gotSoPaths := l.parseGrepProcMap(tt.args.stdout); !reflect.DeepEqual(gotSoPaths, tt.wantSoPaths) {
 | 
			
		||||
				t.Errorf("base.parseGrepProcMap() = %v, want %v", gotSoPaths, tt.wantSoPaths)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -88,19 +88,19 @@ func (o *centos) sudoNoPasswdCmdsFast() []cmd {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *centos) sudoNoPasswdCmdsFastRoot() []cmd {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		// yum ps needs internet connection
 | 
			
		||||
	if !o.ServerInfo.IsContainer() {
 | 
			
		||||
		return []cmd{
 | 
			
		||||
			{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"repoquery -h", exitStatusZero},
 | 
			
		||||
			{"needs-restarting", exitStatusZero},
 | 
			
		||||
			{"which which", exitStatusZero},
 | 
			
		||||
			{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"ls -l /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"cat /proc/1/maps", exitStatusZero},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []cmd{
 | 
			
		||||
		{"yum -q ps all --color=never", exitStatusZero},
 | 
			
		||||
		{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
		{"repoquery -h", exitStatusZero},
 | 
			
		||||
		{"needs-restarting", exitStatusZero},
 | 
			
		||||
		{"which which", exitStatusZero},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -117,3 +117,7 @@ func (o rootPrivCentos) repoquery() bool {
 | 
			
		||||
func (o rootPrivCentos) yumMakeCache() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivCentos) yumPS() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -149,6 +149,8 @@ func (o *debian) checkIfSudoNoPasswd() error {
 | 
			
		||||
	cmds := []string{
 | 
			
		||||
		"checkrestart",
 | 
			
		||||
		"stat /proc/1/exe",
 | 
			
		||||
		"ls -l /proc/1/exe",
 | 
			
		||||
		"cat /proc/1/maps",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
@@ -262,6 +264,15 @@ func (o *debian) preCure() error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) postScan() error {
 | 
			
		||||
	if o.getServerInfo().Mode.IsDeep() || o.getServerInfo().Mode.IsFastRoot() {
 | 
			
		||||
		if err := o.dpkgPs(); err != nil {
 | 
			
		||||
			err = xerrors.Errorf("Failed to dpkg-ps: %w", err)
 | 
			
		||||
			o.log.Warnf("err: %+v", err)
 | 
			
		||||
			o.warns = append(o.warns, err)
 | 
			
		||||
			// Only warning this error
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.getServerInfo().Mode.IsDeep() || o.getServerInfo().Mode.IsFastRoot() {
 | 
			
		||||
		if err := o.checkrestart(); err != nil {
 | 
			
		||||
			err = xerrors.Errorf("Failed to scan need-restarting processes: %w", err)
 | 
			
		||||
@@ -1109,3 +1120,90 @@ func (o *debian) parseCheckRestart(stdout string) (models.Packages, []string) {
 | 
			
		||||
	}
 | 
			
		||||
	return packs, unknownServices
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) dpkgPs() error {
 | 
			
		||||
	stdout, err := o.ps()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to ps: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	pidNames := o.parsePs(stdout)
 | 
			
		||||
	pidLoadedFiles := map[string][]string{}
 | 
			
		||||
	// for pid, name := range pidNames {
 | 
			
		||||
	for pid := range pidNames {
 | 
			
		||||
		stdout := ""
 | 
			
		||||
		stdout, err = o.lsProcExe(pid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to exec /proc/%s/exe err: %s", pid, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		s, err := o.parseLsProcExe(stdout)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to parse /proc/%s/exe: %s", pid, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], s)
 | 
			
		||||
 | 
			
		||||
		stdout, err = o.grepProcMap(pid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to exec /proc/%s/maps: %s", pid, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		ss := o.parseGrepProcMap(stdout)
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for pid, loadedFiles := range pidLoadedFiles {
 | 
			
		||||
		o.log.Debugf("dpkg -S %#v", loadedFiles)
 | 
			
		||||
		pkgNames, err := o.getPkgName(loadedFiles)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNames, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		procName := ""
 | 
			
		||||
		if _, ok := pidNames[pid]; ok {
 | 
			
		||||
			procName = pidNames[pid]
 | 
			
		||||
		}
 | 
			
		||||
		proc := models.AffectedProcess{
 | 
			
		||||
			PID:  pid,
 | 
			
		||||
			Name: procName,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, n := range pkgNames {
 | 
			
		||||
			p, ok := o.Packages[n]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return xerrors.Errorf("pkg not found %s", n)
 | 
			
		||||
			}
 | 
			
		||||
			p.AffectedProcs = append(p.AffectedProcs, proc)
 | 
			
		||||
			o.Packages[p.Name] = p
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) getPkgName(paths []string) (pkgNames []string, err error) {
 | 
			
		||||
	cmd := "dpkg -S " + strings.Join(paths, " ")
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), noSudo)
 | 
			
		||||
	if !r.isSuccess(0, 1) {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return o.parseGetPkgName(r.Stdout), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) parseGetPkgName(stdout string) (pkgNames []string) {
 | 
			
		||||
	uniq := map[string]struct{}{}
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(stdout))
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := scanner.Text()
 | 
			
		||||
		ss := strings.Fields(line)
 | 
			
		||||
		if len(ss) < 2 || ss[1] == "no" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		s := strings.Split(ss[0], ":")[0]
 | 
			
		||||
		uniq[s] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
	for n := range uniq {
 | 
			
		||||
		pkgNames = append(pkgNames, n)
 | 
			
		||||
	}
 | 
			
		||||
	return pkgNames
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -726,3 +726,38 @@ util-linux:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_debian_parseGetPkgName(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name         string
 | 
			
		||||
		args         args
 | 
			
		||||
		wantPkgNames []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "success",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `udev: /lib/systemd/systemd-udevd
 | 
			
		||||
dpkg-query: no path found matching pattern /lib/modules/3.16.0-6-amd64/modules.alias.bin
 | 
			
		||||
udev: /lib/systemd/systemd-udevd
 | 
			
		||||
dpkg-query: no path found matching pattern /lib/udev/hwdb.bin
 | 
			
		||||
libuuid1:amd64: /lib/x86_64-linux-gnu/libuuid.so.1.3.0`,
 | 
			
		||||
			},
 | 
			
		||||
			wantPkgNames: []string{
 | 
			
		||||
				"udev",
 | 
			
		||||
				"libuuid1",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			o := &debian{}
 | 
			
		||||
			gotPkgNames := o.parseGetPkgName(tt.args.stdout)
 | 
			
		||||
			if !reflect.DeepEqual(gotPkgNames, tt.wantPkgNames) {
 | 
			
		||||
				t.Errorf("debian.parseGetPkgName() = %v, want %v", gotPkgNames, tt.wantPkgNames)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -74,11 +74,20 @@ func (o *oracle) sudoNoPasswdCmdsFast() []cmd {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *oracle) sudoNoPasswdCmdsFastRoot() []cmd {
 | 
			
		||||
	cmds := []cmd{{"needs-restarting", exitStatusZero}}
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return cmds
 | 
			
		||||
	if !o.ServerInfo.IsContainer() {
 | 
			
		||||
		return []cmd{
 | 
			
		||||
			{"repoquery -h", exitStatusZero},
 | 
			
		||||
			{"needs-restarting", exitStatusZero},
 | 
			
		||||
			{"which which", exitStatusZero},
 | 
			
		||||
			{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"ls -l /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"cat /proc/1/maps", exitStatusZero},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []cmd{
 | 
			
		||||
		{"repoquery -h", exitStatusZero},
 | 
			
		||||
		{"needs-restarting", exitStatusZero},
 | 
			
		||||
	}
 | 
			
		||||
	return append(cmds, cmd{"repoquery -h", exitStatusZero})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *oracle) sudoNoPasswdCmdsDeep() []cmd {
 | 
			
		||||
@@ -94,3 +103,7 @@ func (o rootPrivOracle) repoquery() bool {
 | 
			
		||||
func (o rootPrivOracle) yumMakeCache() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivOracle) yumPS() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -142,6 +142,7 @@ type redhatBase struct {
 | 
			
		||||
type rootPriv interface {
 | 
			
		||||
	repoquery() bool
 | 
			
		||||
	yumMakeCache() bool
 | 
			
		||||
	yumPS() bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type cmd struct {
 | 
			
		||||
@@ -189,7 +190,7 @@ func (o *redhatBase) preCure() error {
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) postScan() error {
 | 
			
		||||
	if o.isExecYumPS() {
 | 
			
		||||
		if err := o.yumPS(); err != nil {
 | 
			
		||||
		if err := o.yumPs(); err != nil {
 | 
			
		||||
			err = xerrors.Errorf("Failed to execute yum-ps: %w", err)
 | 
			
		||||
			o.log.Warnf("err: %+v", err)
 | 
			
		||||
			o.warns = append(o.warns, err)
 | 
			
		||||
@@ -431,24 +432,9 @@ func (o *redhatBase) parseUpdatablePacksLine(line string) (models.Package, error
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isExecScanUsingYum() bool {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if o.Distro.Family == config.CentOS {
 | 
			
		||||
		// CentOS doesn't have security channel
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if o.getServerInfo().Mode.IsFastRoot() || o.getServerInfo().Mode.IsDeep() {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isExecYumPS() bool {
 | 
			
		||||
	// RedHat has no yum-ps
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
	case config.RedHat,
 | 
			
		||||
	case config.Oracle,
 | 
			
		||||
		config.OpenSUSE,
 | 
			
		||||
		config.OpenSUSELeap,
 | 
			
		||||
		config.SUSEEnterpriseServer,
 | 
			
		||||
@@ -456,12 +442,7 @@ func (o *redhatBase) isExecYumPS() bool {
 | 
			
		||||
		config.SUSEOpenstackCloud:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// yum ps needs internet connection
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() || o.getServerInfo().Mode.IsFast() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
	return !o.getServerInfo().Mode.IsFast()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isExecNeedsRestarting() bool {
 | 
			
		||||
@@ -496,107 +477,69 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) yumPS() error {
 | 
			
		||||
	cmd := "LANGUAGE=en_US.UTF-8 yum info yum"
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
func (o *redhatBase) yumPs() error {
 | 
			
		||||
	stdout, err := o.ps()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to yum ps: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !o.checkYumPsInstalled(r.Stdout) {
 | 
			
		||||
		switch o.Distro.Family {
 | 
			
		||||
		case config.RedHat, config.Oracle:
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			return xerrors.New("yum-plugin-ps is not installed")
 | 
			
		||||
	pidNames := o.parsePs(stdout)
 | 
			
		||||
	pidLoadedFiles := map[string][]string{}
 | 
			
		||||
	// for pid, name := range pidNames {
 | 
			
		||||
	for pid := range pidNames {
 | 
			
		||||
		stdout := ""
 | 
			
		||||
		stdout, err = o.lsProcExe(pid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to exec /proc/%s/exe err: %s", pid, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd = "LANGUAGE=en_US.UTF-8 yum -q ps all --color=never"
 | 
			
		||||
	r = o.exec(util.PrependProxyEnv(cmd), sudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	packs := o.parseYumPS(r.Stdout)
 | 
			
		||||
	for name, pack := range packs {
 | 
			
		||||
		p := o.Packages[name]
 | 
			
		||||
		p.AffectedProcs = pack.AffectedProcs
 | 
			
		||||
		o.Packages[name] = p
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) checkYumPsInstalled(stdout string) bool {
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(stdout))
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := strings.TrimSpace(scanner.Text())
 | 
			
		||||
		if strings.HasPrefix(line, "Loaded plugins: ") {
 | 
			
		||||
			if strings.Contains(line, " ps,") || strings.HasSuffix(line, " ps") {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		s, err := o.parseLsProcExe(stdout)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to parse /proc/%s/exe: %s", pid, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], s)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumPS(stdout string) models.Packages {
 | 
			
		||||
	packs := models.Packages{}
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(stdout))
 | 
			
		||||
	isPackageLine, needToParseProcline := false, false
 | 
			
		||||
	currentPackName := ""
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := scanner.Text()
 | 
			
		||||
		fields := strings.Fields(line)
 | 
			
		||||
		if len(fields) == 0 ||
 | 
			
		||||
			len(fields) == 1 && fields[0] == "ps" ||
 | 
			
		||||
			len(fields) == 6 && fields[0] == "pid" {
 | 
			
		||||
		stdout, err = o.grepProcMap(pid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to exec /proc/%s/maps: %s", pid, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		ss := o.parseGrepProcMap(stdout)
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for pid, loadedFiles := range pidLoadedFiles {
 | 
			
		||||
		o.log.Debugf("rpm -qf: %#v", loadedFiles)
 | 
			
		||||
		pkgNames, err := o.getPkgName(loadedFiles)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNames, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isPackageLine = !strings.HasPrefix(line, " ")
 | 
			
		||||
		if isPackageLine {
 | 
			
		||||
			if 1 < len(fields) && fields[1] == "Upgrade" {
 | 
			
		||||
				needToParseProcline = true
 | 
			
		||||
		uniq := map[string]struct{}{}
 | 
			
		||||
		for _, name := range pkgNames {
 | 
			
		||||
			uniq[name] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
				// Search o.Packages to divide into name, version, release
 | 
			
		||||
				name, pack, found := o.Packages.FindOne(func(p models.Package) bool {
 | 
			
		||||
					var epochNameVerRel string
 | 
			
		||||
					if index := strings.Index(p.Version, ":"); 0 < index {
 | 
			
		||||
						epoch := p.Version[0:index]
 | 
			
		||||
						ver := p.Version[index+1 : len(p.Version)]
 | 
			
		||||
						epochNameVerRel = fmt.Sprintf("%s:%s-%s-%s.%s",
 | 
			
		||||
							epoch, p.Name, ver, p.Release, p.Arch)
 | 
			
		||||
					} else {
 | 
			
		||||
						epochNameVerRel = fmt.Sprintf("%s-%s-%s.%s",
 | 
			
		||||
							p.Name, p.Version, p.Release, p.Arch)
 | 
			
		||||
					}
 | 
			
		||||
					return strings.HasPrefix(fields[0], epochNameVerRel)
 | 
			
		||||
				})
 | 
			
		||||
				if !found {
 | 
			
		||||
					o.log.Errorf("`yum ps` package is not found: %s", line)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				packs[name] = pack
 | 
			
		||||
				currentPackName = name
 | 
			
		||||
			} else {
 | 
			
		||||
				needToParseProcline = false
 | 
			
		||||
			}
 | 
			
		||||
		} else if needToParseProcline {
 | 
			
		||||
			if 6 < len(fields) {
 | 
			
		||||
				proc := models.AffectedProcess{
 | 
			
		||||
					PID:  fields[0],
 | 
			
		||||
					Name: fields[1],
 | 
			
		||||
				}
 | 
			
		||||
				pack := packs[currentPackName]
 | 
			
		||||
				pack.AffectedProcs = append(pack.AffectedProcs, proc)
 | 
			
		||||
				packs[currentPackName] = pack
 | 
			
		||||
			} else {
 | 
			
		||||
				o.log.Errorf("`yum ps` Unknown Format: %s", line)
 | 
			
		||||
				continue
 | 
			
		||||
		procName := ""
 | 
			
		||||
		if _, ok := pidNames[pid]; ok {
 | 
			
		||||
			procName = pidNames[pid]
 | 
			
		||||
		}
 | 
			
		||||
		proc := models.AffectedProcess{
 | 
			
		||||
			PID:  pid,
 | 
			
		||||
			Name: procName,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for fqpn := range uniq {
 | 
			
		||||
			p, err := o.Packages.FindByFQPN(fqpn)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			p.AffectedProcs = append(p.AffectedProcs, proc)
 | 
			
		||||
			o.Packages[p.Name] = *p
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return packs
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) needsRestarting() error {
 | 
			
		||||
@@ -690,8 +633,22 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
 | 
			
		||||
	return strings.Replace(fqpn, "-(none):", "-", -1), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) hasYumColorOption() bool {
 | 
			
		||||
	cmd := "yum --help | grep color"
 | 
			
		||||
func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
 | 
			
		||||
	cmd := rpmQf(o.Distro) + strings.Join(paths, " ")
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), noSudo)
 | 
			
		||||
	return len(r.Stdout) > 0
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := scanner.Text()
 | 
			
		||||
		pack, err := o.parseInstalledPackagesLine(line)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Debugf("Failed to parse rpm -qf line: %s, r: %s. continue", line, r)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		pkgNames = append(pkgNames, pack.FQPN())
 | 
			
		||||
	}
 | 
			
		||||
	return pkgNames, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -329,229 +329,6 @@ if-not-architecture 0 100 200 amzn-main`
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCheckYumPsInstalled(t *testing.T) {
 | 
			
		||||
	r := newCentOS(config.ServerInfo{})
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: `Loaded plugins: changelog, fastestmirror, ps, remove-with-leaves, show-leaves
 | 
			
		||||
Loading mirror speeds from cached hostfile
 | 
			
		||||
 * base: ftp.tsukuba.wide.ad.jp
 | 
			
		||||
 * extras: ftp.tsukuba.wide.ad.jp
 | 
			
		||||
 * updates: ftp.tsukuba.wide.ad.jp
 | 
			
		||||
Installed Packages
 | 
			
		||||
Name        : yum
 | 
			
		||||
Arch        : noarch
 | 
			
		||||
Version     : 3.4.3
 | 
			
		||||
Release     : 150.el7.centos
 | 
			
		||||
Size        : 5.5 M
 | 
			
		||||
Repo        : installed
 | 
			
		||||
From repo   : anaconda
 | 
			
		||||
Summary     : RPM package installer/updater/manager
 | 
			
		||||
URL         : http://yum.baseurl.org/
 | 
			
		||||
License     : GPLv2+
 | 
			
		||||
Description : Yum is a utility that can check for and automatically download and
 | 
			
		||||
            : install updated RPM packages. Dependencies are obtained and downloaded
 | 
			
		||||
            : automatically, prompting the user for permission as necessary.
 | 
			
		||||
 | 
			
		||||
Available Packages
 | 
			
		||||
Name        : yum
 | 
			
		||||
Arch        : noarch
 | 
			
		||||
Version     : 3.4.3
 | 
			
		||||
Release     : 154.el7.centos.1
 | 
			
		||||
Size        : 1.2 M
 | 
			
		||||
Repo        : updates/7/x86_64
 | 
			
		||||
Summary     : RPM package installer/updater/manager
 | 
			
		||||
URL         : http://yum.baseurl.org/
 | 
			
		||||
License     : GPLv2+
 | 
			
		||||
Description : Yum is a utility that can check for and automatically download and
 | 
			
		||||
            : install updated RPM packages. Dependencies are obtained and downloaded
 | 
			
		||||
            : automatically, prompting the user for permission as necessary.`,
 | 
			
		||||
			out: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: `Failed to set locale, defaulting to C
 | 
			
		||||
Loaded plugins: amazon-id, rhui-lb, search-disabled-repos
 | 
			
		||||
Installed Packages
 | 
			
		||||
Name        : yum
 | 
			
		||||
Arch        : noarch
 | 
			
		||||
Version     : 3.4.3
 | 
			
		||||
Release     : 154.el7
 | 
			
		||||
Size        : 5.5 M
 | 
			
		||||
Repo        : installed
 | 
			
		||||
From repo   : rhui-REGION-rhel-server-releases
 | 
			
		||||
Summary     : RPM package installer/updater/manager
 | 
			
		||||
URL         : http://yum.baseurl.org/
 | 
			
		||||
License     : GPLv2+
 | 
			
		||||
Description : Yum is a utility that can check for and automatically download and
 | 
			
		||||
            : install updated RPM packages. Dependencies are obtained and downloaded
 | 
			
		||||
            : automatically, prompting the user for permission as necessary.`,
 | 
			
		||||
			out: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		ok := r.checkYumPsInstalled(tt.in)
 | 
			
		||||
		if ok != tt.out {
 | 
			
		||||
			t.Errorf("expected: %v\nactual: %v", tt.out, ok)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseYumPS(t *testing.T) {
 | 
			
		||||
	r := newCentOS(config.ServerInfo{})
 | 
			
		||||
	r.Distro = config.Distro{Family: "centos"}
 | 
			
		||||
	r.Packages = models.NewPackages(
 | 
			
		||||
		models.Package{
 | 
			
		||||
			Name:    "python",
 | 
			
		||||
			Version: "2.7.5",
 | 
			
		||||
			Release: "34.el7",
 | 
			
		||||
			Arch:    "x86_64",
 | 
			
		||||
		},
 | 
			
		||||
		models.Package{
 | 
			
		||||
			Name:    "util-linux",
 | 
			
		||||
			Version: "2.23.2",
 | 
			
		||||
			Release: "26.el7",
 | 
			
		||||
			Arch:    "x86_64",
 | 
			
		||||
		},
 | 
			
		||||
		models.Package{
 | 
			
		||||
			Name:    "wpa_supplicant",
 | 
			
		||||
			Version: "1:2.0",
 | 
			
		||||
			Release: "17.el7_1",
 | 
			
		||||
			Arch:    "x86_64",
 | 
			
		||||
		},
 | 
			
		||||
		models.Package{
 | 
			
		||||
			Name:    "yum",
 | 
			
		||||
			Version: "3.4.3",
 | 
			
		||||
			Release: "150.el7.centos",
 | 
			
		||||
			Arch:    "noarch",
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out models.Packages
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			`       pid proc                  CPU      RSS      State uptime
 | 
			
		||||
python-2.7.5-34.el7.x86_64 Upgrade 2.7.5-48.el7.x86_64
 | 
			
		||||
       741 tuned                1:54    16 MB   Sleeping:  14 day(s) 21:52:32
 | 
			
		||||
     38755 yum                  0:00    42 MB    Running:  00:00
 | 
			
		||||
util-linux-2.23.2-26.el7.x86_64 Upgrade 2.23.2-33.el7_3.2.x86_64
 | 
			
		||||
       626 agetty               0:00   848 kB   Sleeping:  14 day(s) 21:52:37
 | 
			
		||||
       628 agetty               0:00   848 kB   Sleeping:  14 day(s) 21:52:37
 | 
			
		||||
1:wpa_supplicant-2.0-17.el7_1.x86_64 Upgrade 1:2.0-21.el7_3.x86_64
 | 
			
		||||
       638 wpa_supplicant       0:00   2.6 MB   Sleeping:  14 day(s) 21:52:37
 | 
			
		||||
yum-3.4.3-150.el7.centos.noarch
 | 
			
		||||
     38755 yum                  0:00    42 MB    Running:  00:00
 | 
			
		||||
ps
 | 
			
		||||
	 `,
 | 
			
		||||
			models.NewPackages(
 | 
			
		||||
				models.Package{
 | 
			
		||||
					Name:    "python",
 | 
			
		||||
					Version: "2.7.5",
 | 
			
		||||
					Release: "34.el7",
 | 
			
		||||
					Arch:    "x86_64",
 | 
			
		||||
					// NewVersion: "2.7.5-",
 | 
			
		||||
					// NewRelease: "48.el7.x86_64",
 | 
			
		||||
					AffectedProcs: []models.AffectedProcess{
 | 
			
		||||
						{
 | 
			
		||||
							PID:  "741",
 | 
			
		||||
							Name: "tuned",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							PID:  "38755",
 | 
			
		||||
							Name: "yum",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				models.Package{
 | 
			
		||||
					Name:    "util-linux",
 | 
			
		||||
					Version: "2.23.2",
 | 
			
		||||
					Release: "26.el7",
 | 
			
		||||
					Arch:    "x86_64",
 | 
			
		||||
					// NewVersion: "2.7.5",
 | 
			
		||||
					// NewRelease: "48.el7.x86_64",
 | 
			
		||||
					AffectedProcs: []models.AffectedProcess{
 | 
			
		||||
						{
 | 
			
		||||
							PID:  "626",
 | 
			
		||||
							Name: "agetty",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							PID:  "628",
 | 
			
		||||
							Name: "agetty",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				models.Package{
 | 
			
		||||
					Name:    "wpa_supplicant",
 | 
			
		||||
					Version: "1:2.0",
 | 
			
		||||
					Release: "17.el7_1",
 | 
			
		||||
					Arch:    "x86_64",
 | 
			
		||||
					// NewVersion: "1:2.0",
 | 
			
		||||
					// NewRelease: "21.el7_3.x86_64",
 | 
			
		||||
					AffectedProcs: []models.AffectedProcess{
 | 
			
		||||
						{
 | 
			
		||||
							PID:  "638",
 | 
			
		||||
							Name: "wpa_supplicant",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			`    pid proc                  CPU      RSS      State uptime
 | 
			
		||||
acpid-2.0.19-6.7.amzn1.x86_64
 | 
			
		||||
      2388 acpid                0:00   1.4 MB   Sleeping:  21:08
 | 
			
		||||
at-3.1.10-48.15.amzn1.x86_64
 | 
			
		||||
      2546 atd                  0:00   164 kB   Sleeping:  21:06
 | 
			
		||||
cronie-anacron-1.4.4-15.8.amzn1.x86_64
 | 
			
		||||
      2637 anacron              0:00   1.5 MB   Sleeping:  13:14
 | 
			
		||||
12:dhclient-4.1.1-51.P1.26.amzn1.x86_64
 | 
			
		||||
      2061 dhclient             0:00   1.4 MB   Sleeping:  21:10
 | 
			
		||||
      2193 dhclient             0:00   2.1 MB   Sleeping:  21:08
 | 
			
		||||
mingetty-1.08-5.9.amzn1.x86_64
 | 
			
		||||
      2572 mingetty             0:00   1.4 MB   Sleeping:  21:06
 | 
			
		||||
      2575 mingetty             0:00   1.4 MB   Sleeping:  21:06
 | 
			
		||||
      2578 mingetty             0:00   1.5 MB   Sleeping:  21:06
 | 
			
		||||
      2580 mingetty             0:00   1.4 MB   Sleeping:  21:06
 | 
			
		||||
      2582 mingetty             0:00   1.4 MB   Sleeping:  21:06
 | 
			
		||||
      2584 mingetty             0:00   1.4 MB   Sleeping:  21:06
 | 
			
		||||
openssh-server-6.6.1p1-33.66.amzn1.x86_64
 | 
			
		||||
      2481 sshd                 0:00   2.6 MB   Sleeping:  21:07
 | 
			
		||||
python27-2.7.12-2.120.amzn1.x86_64
 | 
			
		||||
      2649 yum                  0:00    35 MB    Running:  00:01
 | 
			
		||||
rsyslog-5.8.10-9.26.amzn1.x86_64
 | 
			
		||||
      2261 rsyslogd             0:00   2.6 MB   Sleeping:  21:08
 | 
			
		||||
udev-173-4.13.amzn1.x86_64
 | 
			
		||||
      1528 udevd                0:00   2.5 MB   Sleeping:  21:12
 | 
			
		||||
      1652 udevd                0:00   2.1 MB   Sleeping:  21:12
 | 
			
		||||
      1653 udevd                0:00   2.0 MB   Sleeping:  21:12
 | 
			
		||||
upstart-0.6.5-13.3.13.amzn1.x86_64
 | 
			
		||||
         1 init                 0:00   2.5 MB   Sleeping:  21:13
 | 
			
		||||
util-linux-2.23.2-33.28.amzn1.x86_64
 | 
			
		||||
      2569 agetty               0:00   1.6 MB   Sleeping:  21:06
 | 
			
		||||
yum-3.4.3-150.70.amzn1.noarch
 | 
			
		||||
      2649 yum                  0:00    35 MB    Running:  00:01
 | 
			
		||||
`,
 | 
			
		||||
			models.Packages{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		packages := r.parseYumPS(tt.in)
 | 
			
		||||
		for name, ePack := range tt.out {
 | 
			
		||||
			if !reflect.DeepEqual(ePack, packages[name]) {
 | 
			
		||||
				e := pp.Sprintf("%v", ePack)
 | 
			
		||||
				a := pp.Sprintf("%v", packages[name])
 | 
			
		||||
				t.Errorf("expected %s, actual %s", e, a)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseNeedsRestarting(t *testing.T) {
 | 
			
		||||
	r := newCentOS(config.ServerInfo{})
 | 
			
		||||
	r.Distro = config.Distro{Family: "centos"}
 | 
			
		||||
@@ -580,49 +357,3 @@ func TestParseNeedsRestarting(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIsExecScanUsingYum(t *testing.T) {
 | 
			
		||||
	r := newRHEL(config.ServerInfo{})
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		modes  []byte
 | 
			
		||||
		family string
 | 
			
		||||
		out    bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			modes: []byte{config.Offline},
 | 
			
		||||
			out:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			modes:  []byte{},
 | 
			
		||||
			family: config.CentOS,
 | 
			
		||||
			out:    false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			family: config.Amazon,
 | 
			
		||||
			modes:  []byte{config.FastRoot},
 | 
			
		||||
			out:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			family: config.Amazon,
 | 
			
		||||
			modes:  []byte{config.Deep},
 | 
			
		||||
			out:    true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		r.Distro.Family = tt.family
 | 
			
		||||
		mode := config.ScanMode{}
 | 
			
		||||
		for _, m := range tt.modes {
 | 
			
		||||
			mode.Set(m)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		si := r.getServerInfo()
 | 
			
		||||
		si.Mode = mode
 | 
			
		||||
		r.setServerInfo(si)
 | 
			
		||||
 | 
			
		||||
		out := r.isExecScanUsingYum()
 | 
			
		||||
		if out != tt.out {
 | 
			
		||||
			t.Errorf("[%d] expected %#v, actual %#v", i, tt.out, out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								scan/rhel.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								scan/rhel.go
									
									
									
									
									
								
							@@ -81,14 +81,14 @@ func (o *rhel) sudoNoPasswdCmdsFast() []cmd {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rhel) sudoNoPasswdCmdsFastRoot() []cmd {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return []cmd{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	if majorVersion < 6 {
 | 
			
		||||
	if !o.ServerInfo.IsContainer() {
 | 
			
		||||
		return []cmd{
 | 
			
		||||
			{"repoquery -h", exitStatusZero},
 | 
			
		||||
			{"needs-restarting", exitStatusZero},
 | 
			
		||||
			{"which which", exitStatusZero},
 | 
			
		||||
			{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"ls -l /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"cat /proc/1/maps", exitStatusZero},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []cmd{
 | 
			
		||||
@@ -110,3 +110,7 @@ func (o rootPrivRHEL) repoquery() bool {
 | 
			
		||||
func (o rootPrivRHEL) yumMakeCache() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivRHEL) yumPS() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -52,8 +52,25 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rpmQa(distro config.Distro) string {
 | 
			
		||||
	const old = "rpm -qa --queryformat \"%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n\""
 | 
			
		||||
	const new = "rpm -qa --queryformat \"%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n\""
 | 
			
		||||
	const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"`
 | 
			
		||||
	const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
 | 
			
		||||
	switch distro.Family {
 | 
			
		||||
	case config.SUSEEnterpriseServer:
 | 
			
		||||
		if v, _ := distro.MajorVersion(); v < 12 {
 | 
			
		||||
			return old
 | 
			
		||||
		}
 | 
			
		||||
		return new
 | 
			
		||||
	default:
 | 
			
		||||
		if v, _ := distro.MajorVersion(); v < 6 {
 | 
			
		||||
			return old
 | 
			
		||||
		}
 | 
			
		||||
		return new
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rpmQf(distro config.Distro) string {
 | 
			
		||||
	const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" `
 | 
			
		||||
	const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
 | 
			
		||||
	switch distro.Family {
 | 
			
		||||
	case config.SUSEEnterpriseServer:
 | 
			
		||||
		if v, _ := distro.MajorVersion(); v < 12 {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user