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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
package system
import (
"bytes"
"context"
"os"
"os/exec"
"strconv"
"sync"
"github.com/aelsabbahy/GOnetstat"
// This needs a better name
"github.com/mitchellh/go-ps"
util2 "github.com/goss-org/goss/util"
)
type Resource interface {
Exists() (bool, error)
}
type System struct {
NewPackage func(context.Context, string, *System, util2.Config) Package
NewFile func(context.Context, string, *System, util2.Config) File
NewAddr func(context.Context, string, *System, util2.Config) Addr
NewPort func(context.Context, string, *System, util2.Config) Port
NewService func(context.Context, string, *System, util2.Config) Service
NewUser func(context.Context, string, *System, util2.Config) User
NewGroup func(context.Context, string, *System, util2.Config) Group
NewCommand func(context.Context, string, *System, util2.Config) Command
NewDNS func(context.Context, string, *System, util2.Config) DNS
NewProcess func(context.Context, string, *System, util2.Config) Process
NewGossfile func(context.Context, string, *System, util2.Config) Gossfile
NewKernelParam func(context.Context, string, *System, util2.Config) KernelParam
NewMount func(context.Context, string, *System, util2.Config) Mount
NewInterface func(context.Context, string, *System, util2.Config) Interface
NewHTTP func(context.Context, string, *System, util2.Config) HTTP
ports map[string][]GOnetstat.Process
portsOnce sync.Once
procMap map[string][]ps.Process
procOnce sync.Once
}
func (s *System) Ports() map[string][]GOnetstat.Process {
s.portsOnce.Do(func() {
s.ports = GetPorts(false)
})
return s.ports
}
func (s *System) ProcMap() (map[string][]ps.Process, error) {
var err error
s.procOnce.Do(func() {
s.procMap, err = GetProcs()
})
return s.procMap, err
}
func New(packageManager string) *System {
sys := &System{
NewFile: NewDefFile,
NewAddr: NewDefAddr,
NewPort: NewDefPort,
NewUser: NewDefUser,
NewGroup: NewDefGroup,
NewCommand: NewDefCommand,
NewDNS: NewDefDNS,
NewProcess: NewDefProcess,
NewGossfile: NewDefGossfile,
NewKernelParam: NewDefKernelParam,
NewMount: NewDefMount,
NewInterface: NewDefInterface,
NewHTTP: NewDefHTTP,
}
sys.detectService()
sys.detectPackage(packageManager)
return sys
}
// detectPackage adds the correct package creation function to a System struct
func (sys *System) detectPackage(p string) {
if p != "dpkg" && p != "apk" && p != "pacman" && p != "rpm" {
p = DetectPackageManager()
}
switch p {
case "dpkg":
sys.NewPackage = NewDebPackage
case "apk":
sys.NewPackage = NewAlpinePackage
case "pacman":
sys.NewPackage = NewPacmanPackage
default:
sys.NewPackage = NewRpmPackage
}
}
// detectService adds the correct service creation function to a System struct
func (sys *System) detectService() {
switch DetectService() {
case "upstart":
sys.NewService = NewServiceUpstart
case "systemd":
sys.NewService = NewServiceSystemd
case "systemdlegacy":
sys.NewService = NewServiceSystemdLegacy
case "alpineinit":
sys.NewService = NewAlpineServiceInit
default:
sys.NewService = NewServiceInit
}
}
// SupportedPackageManagers is a list of package managers we support
func SupportedPackageManagers() []string {
return []string{"apk", "dpkg", "pacman", "rpm"}
}
// IsSupportedPackageManager determines if p is a supported package manager
func IsSupportedPackageManager(p string) bool {
for _, m := range SupportedPackageManagers() {
if m == p {
return true
}
}
return false
}
// DetectPackageManager attempts to detect whether or not the system is using
// "dpkg", "rpm", "apk", or "pacman" package managers. It first attempts to
// detect the distro. If that fails, it falls back to finding package manager
// executables. If that fails, it returns the empty string.
func DetectPackageManager() string {
switch DetectDistro() {
case "ubuntu":
return "dpkg"
case "redhat":
return "rpm"
case "alpine":
return "apk"
case "arch":
return "pacman"
case "debian":
return "dpkg"
}
for _, manager := range []string{"dpkg", "rpm", "apk", "pacman"} {
if HasCommand(manager) {
return manager
}
}
return ""
}
// DetectService attempts to detect what kind of service management the system
// is using, "systemd", "upstart", "alpineinit", or "init". It looks for systemctl
// command to detect systemd, and falls back on DetectDistro otherwise. If it can't
// decide, it returns "init".
func DetectService() string {
if HasCommand("systemctl") {
if isLegacySystemd() {
return "systemdlegacy"
}
return "systemd"
}
// Centos Docker container doesn't run systemd, so we detect it or use init.
switch DetectDistro() {
case "ubuntu":
return "upstart"
case "alpine":
return "alpineinit"
case "arch":
return "systemd"
}
return "init"
}
// DetectDistro attempts to detect which Linux distribution this computer is
// using. One of "ubuntu", "redhat" (including Centos), "alpine", "arch", or
// "debian". If it can't decide, it returns an empty string.
func DetectDistro() string {
if b, e := os.ReadFile("/etc/lsb-release"); e == nil && bytes.Contains(b, []byte("Ubuntu")) {
return "ubuntu"
} else if isRedhat() {
return "redhat"
} else if _, err := os.Stat("/etc/alpine-release"); err == nil {
return "alpine"
} else if _, err := os.Stat("/etc/arch-release"); err == nil {
return "arch"
} else if _, err := os.Stat("/etc/debian_version"); err == nil {
return "debian"
}
return ""
}
// HasCommand returns whether or not an executable by this name is on the PATH.
func HasCommand(cmd string) bool {
if _, err := exec.LookPath(cmd); err == nil {
return true
}
return false
}
func isLegacySystemd() bool {
if b, err := os.ReadFile("/etc/debian_version"); err == nil {
i := bytes.Index(b, []byte("."))
if i < 0 {
return false
}
if major, err := strconv.Atoi(string(b[:i])); err == nil {
return major < 9
}
}
return false
}
func isRedhat() bool {
if _, err := os.Stat("/etc/redhat-release"); err == nil {
return true
} else if _, err := os.Stat("/etc/system-release"); err == nil {
return true
}
return false
}
|