File: version.go

package info (click to toggle)
golang-github-containers-common 0.66.0%2Bds2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,608 kB
  • sloc: makefile: 126; sh: 125
file content (142 lines) | stat: -rw-r--r-- 4,207 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package version

import (
	"bytes"
	"fmt"
	"os/exec"
	"strings"

	"go.podman.io/storage/pkg/fileutils"
)

const (
	UnknownPackage = "Unknown"
)

// Note: This function is copied from containers/podman libpod/util.go
// Please see https://github.com/containers/common/pull/1460
func queryPackageVersion(cmdArg ...string) string {
	err := fileutils.Exists(cmdArg[0])
	if err != nil {
		return ""
	}
	output := UnknownPackage
	if 1 < len(cmdArg) {
		cmd := exec.Command(cmdArg[0], cmdArg[1:]...)
		if outp, err := cmd.Output(); err == nil {
			output = string(outp)
			switch cmdArg[0] {
			case "/usr/bin/dlocate":
				// can return multiple matches
				output, _, _ := strings.Cut(output, "\n")
				r, _, _ := strings.Cut(output, ": ")
				regexpFormat := `^..\s` + r + `\s`
				cmd = exec.Command(cmdArg[0], "-P", regexpFormat, "-l")
				cmd.Env = []string{"COLUMNS=160"} // show entire value
				// dlocate always returns exit code 1 for list command
				if outp, _ = cmd.Output(); len(outp) > 0 {
					lines := strings.Split(string(outp), "\n")
					if len(lines) > 1 {
						line := lines[len(lines)-2] // trailing newline
						f := strings.Fields(line)
						if len(f) >= 2 {
							return f[1] + "_" + f[2]
						}
					}
				}
			case "/usr/bin/dpkg":
				r, _, _ := strings.Cut(output, ": ")
				queryFormat := `${Package}_${Version}_${Architecture}`
				cmd = exec.Command("/usr/bin/dpkg-query", "-f", queryFormat, "-W", r)
				if outp, err := cmd.Output(); err == nil {
					output = string(outp)
				}
			case "/usr/bin/pacman":
				pkg := strings.Trim(output, "\n")
				cmd = exec.Command(cmdArg[0], "-Q", "--", pkg)
				if outp, err := cmd.Output(); err == nil {
					output = strings.ReplaceAll(string(outp), " ", "-")
				}
			case "/sbin/apk":
				prefix := cmdArg[len(cmdArg)-1] + " is owned by "
				output = strings.Replace(output, prefix, "", 1)
			}
		}
	}
	return strings.Trim(output, "\n")
}

// Package tries to query the package information of the given program path.
// Note it must be an absolute path.
func Package(program string) string {
	// Note: This function is copied from containers/podman libpod/util.go
	// Please see https://github.com/containers/common/pull/1460
	err := fileutils.Exists(program)
	if err != nil {
		return UnknownPackage
	}

	type Packager struct {
		Format  string
		Command []string
	}
	packagers := []Packager{
		{"rpm", []string{"/usr/bin/rpm", "-q", "-f"}},
		{"deb", []string{"/usr/bin/dlocate", "-F"}},             // Debian, Ubuntu (quick)
		{"deb", []string{"/usr/bin/dpkg", "-S"}},                // Debian, Ubuntu (slow)
		{"pacman", []string{"/usr/bin/pacman", "-Qoq"}},         // Arch
		{"gentoo", []string{"/usr/bin/qfile", "-qv"}},           // Gentoo (quick)
		{"gentoo", []string{"/usr/bin/equery", "b"}},            // Gentoo (slow)
		{"apk", []string{"/sbin/apk", "info", "-W"}},            // Alpine
		{"pkg", []string{"/usr/local/sbin/pkg", "which", "-q"}}, // FreeBSD
	}

	lastformat := ""
	for _, packager := range packagers {
		if packager.Format == lastformat {
			continue
		}
		cmd := packager.Command
		cmd = append(cmd, program)
		if out := queryPackageVersion(cmd...); out != UnknownPackage {
			if out == "" {
				continue
			}
			return out
		}
		lastformat = packager.Format
	}
	return UnknownPackage
}

// Program returns the --version output as string of the given command.
func Program(name string) (string, error) {
	// Note: This function is copied from containers/podman libpod/util.go
	// Please see https://github.com/containers/common/pull/1460
	return program(name, false)
}

func ProgramDnsname(name string) (string, error) {
	return program(name, true)
}

func program(program string, dnsname bool) (string, error) {
	cmd := exec.Command(program, "--version")
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Run()
	if err != nil {
		return "", fmt.Errorf("`%v --version` failed: %v %v (%v)", program, stderr.String(), stdout.String(), err)
	}

	output := strings.TrimSuffix(stdout.String(), "\n")
	// dnsname --version returns the information to stderr
	if dnsname {
		output = strings.TrimSuffix(stderr.String(), "\n")
	}

	return output, nil
}