From b99050a2528ec9de2340300cd15ca87018c5a939 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Mon, 29 Apr 2024 00:27:41 +0300 Subject: [PATCH 1/5] [support/services] Add package for collecting services info --- CHANGELOG.md | 4 ++++ ek.go | 2 +- support/support.go | 43 +++++++++++++++++++++++----------- support/support_nix.go | 51 +++++++++++++++++++++++++++++++++++++++++ support/support_test.go | 9 ++++++++ 5 files changed, 95 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e04c34d..ce29ca7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Changelog +### 12.119.0 + +- `[support/services]` Added package for collecting services info + ### 12.118.0 - `[terminal/input]` Added method `SetHistoryCapacity` diff --git a/ek.go b/ek.go index c89b60ec..23903b05 100644 --- a/ek.go +++ b/ek.go @@ -21,7 +21,7 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // // VERSION is current ek package version -const VERSION = "12.118.0" +const VERSION = "12.119.0" // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/support/support.go b/support/support.go index 25b363a2..d79d4984 100644 --- a/support/support.go +++ b/support/support.go @@ -8,14 +8,12 @@ By default, it collects information about the application and environment: - Go version used - Binary SHA - Git commit SHA - - Environment variables - - Applications - - Custom checks There are also some sub-packages to collect/parse additional information: - apps: Package for extracting apps versions info - deps: Package for extracting dependency information from gomod data - pkgs: Package for collecting information about installed packages + - services: Package for collecting information about services - fs: Package for collecting information about the file system - network: Package to collect information about the network @@ -26,6 +24,7 @@ Example of collecting maximum information about the application and system: WithDeps(deps.Extract(gomodData)). WithApps(apps.Golang(), apps.GCC()). WithPackages(pkgs.Collect("rpm", "go,golang", "java,jre,jdk", "nano")). + WithServices(services.Collect("firewalld", "nginx")). WithChecks(myAppAvailabilityCheck()). WithEnvVars("LANG", "PAGER", "SSH_CLIENT"). WithNetwork(network.Collect("https://domain.com/ip-echo")). @@ -40,6 +39,7 @@ it to the console. WithDeps(deps.Extract(gomodData)). WithApps(apps.Golang(), apps.GCC()). WithPackages(pkgs.Collect("rpm", "go,golang", "java,jre,jdk", "nano")). + WithServices(services.Collect("firewalld", "nginx")). WithChecks(myAppAvailabilityCheck()). WithEnvVars("LANG", "PAGER", "SSH_CLIENT"). WithNetwork(network.Collect("https://domain.com/ip-echo")). @@ -67,6 +67,14 @@ const ( CHECK_SKIP CheckStatus = "skip" ) +type ServiceStatus string + +const ( + STATUS_WORKS ServiceStatus = "works" + STATUS_STOPPED ServiceStatus = "stopped" + STATUS_UNKNOWN ServiceStatus = "unknown" +) + // ////////////////////////////////////////////////////////////////////////////////// // // Info contains all support information (can be encoded in JSON/GOB) @@ -75,16 +83,17 @@ type Info struct { Version string `json:"version"` Binary string `json:"binary"` - Build *BuildInfo `json:"build,omitempty"` - OS *OSInfo `json:"os,omitempty"` - System *SystemInfo `json:"system,omitempty"` - Network *NetworkInfo `json:"network,omitempty"` - FS []FSInfo `json:"fs,omitempty"` - Pkgs []Pkg `json:"pkgs,omitempty"` - Deps []Dep `json:"deps,omitempty"` - Apps []App `json:"apps,omitempty"` - Checks []Check `json:"checks,omitempty"` - Env []EnvVar `json:"env,omitempty"` + Build *BuildInfo `json:"build,omitempty"` + OS *OSInfo `json:"os,omitempty"` + System *SystemInfo `json:"system,omitempty"` + Network *NetworkInfo `json:"network,omitempty"` + FS []FSInfo `json:"fs,omitempty"` + Pkgs []Pkg `json:"pkgs,omitempty"` + Services []Service `json:"services,omitempty"` + Deps []Dep `json:"deps,omitempty"` + Apps []App `json:"apps,omitempty"` + Checks []Check `json:"checks,omitempty"` + Env []EnvVar `json:"env,omitempty"` } // BuildInfo contains information about binary @@ -139,6 +148,14 @@ type FSInfo struct { Free uint64 `json:"free,omitempty"` } +// Service contains basic info about service +type Service struct { + Name string `json:"name"` + Status ServiceStatus `json:"status"` + IsPresent bool `json:"is_present"` + IsEnabled bool `json:"is_enabled"` +} + // App contains basic information about app type App struct { Name string `json:"name"` diff --git a/support/support_nix.go b/support/support_nix.go index 230ad568..82b3eada 100644 --- a/support/support_nix.go +++ b/support/support_nix.go @@ -90,6 +90,17 @@ func (i *Info) WithPackages(pkgs []Pkg) *Info { return i } +// WithServices adds information about services +func (i *Info) WithServices(services []Service) *Info { + if i == nil { + return nil + } + + i.Services = append(i.Services, services...) + + return i +} + // WithPackages adds information about system apps func (i *Info) WithApps(apps ...App) *Info { if i == nil { @@ -174,6 +185,7 @@ func (i *Info) Print() { i.printFSInfo() i.printEnvVars() i.printPackagesInfo() + i.printServicesInfo() i.printAppsInfo() i.printChecksInfo() i.printDependencies() @@ -305,6 +317,34 @@ func (i *Info) printPackagesInfo() { } } +// printServicesInfo prints services info +func (i *Info) printServicesInfo() { + if len(i.Services) == 0 { + return + } + + fmtutil.Separator(false, "SERVICES") + + size := getMaxServiceNameSize(i.Services) + + for _, s := range i.Services { + var status string + + switch s.Status { + case STATUS_WORKS: + status = "{g}works{!}" + case STATUS_STOPPED: + status = "{s}stopped{!}" + } + + if s.IsEnabled { + status += " {s-}(enabled){!}" + } + + format(size, true, s.Name, fmtc.Sprint(status)) + } +} + // printAppsInfo prints info about applications func (i *Info) printAppsInfo() { if len(i.Apps) == 0 { @@ -505,6 +545,17 @@ func getMaxAppNameSize(apps []App) int { return size } +// getMaxServiceNameSize returns max package name size +func getMaxServiceNameSize(apps []Service) int { + var size int + + for _, s := range apps { + size = mathutil.Max(size, len(s.Name)) + } + + return size +} + // getMaxDeviceNameSize returns max device name size func getMaxDeviceNameSize(mounts []FSInfo) int { var size int diff --git a/support/support_test.go b/support/support_test.go index af30b9ee..464bcb42 100644 --- a/support/support_test.go +++ b/support/support_test.go @@ -73,6 +73,12 @@ func (s *SupportSuite) TestCollect(c *C) { Pkg{}, } + services := []Service{ + Service{"test1", STATUS_WORKS, true, true}, + Service{"test2", STATUS_STOPPED, true, false}, + Service{"test3", STATUS_UNKNOWN, false, false}, + } + apps := []App{ App{"test", "1.2.3"}, App{"test", ""}, @@ -84,6 +90,7 @@ func (s *SupportSuite) TestCollect(c *C) { c.Assert(i.WithRevision(""), NotNil) c.Assert(i.WithRevision("1234567"), NotNil) c.Assert(i.WithPackages(pkgs), NotNil) + c.Assert(i.WithServices(services), NotNil) c.Assert(i.WithApps(apps...), NotNil) c.Assert(i.WithChecks(chks...), NotNil) c.Assert(i.WithEnvVars("", "SUPPORT_VAR", "TERM", "CI"), NotNil) @@ -96,6 +103,7 @@ func (s *SupportSuite) TestCollect(c *C) { i.OS = nil i.Env = nil i.Pkgs = nil + i.Services = nil i.Apps = nil i.Checks = nil i.Network = nil @@ -120,6 +128,7 @@ func (s *SupportSuite) TestNil(c *C) { c.Assert(i.WithDeps(nil), IsNil) c.Assert(i.WithRevision(""), IsNil) c.Assert(i.WithPackages(nil), IsNil) + c.Assert(i.WithServices(nil), IsNil) c.Assert(i.WithApps(App{}), IsNil) c.Assert(i.WithChecks(Check{}), IsNil) c.Assert(i.WithEnvVars(""), IsNil) From dbf6b3468082f3b2f3100a2764097a65d8cf7bfb Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Mon, 29 Apr 2024 00:27:51 +0300 Subject: [PATCH 2/5] [support/services] Add package for collecting services info --- support/services/services_darwin.go | 18 ++++++++++++ support/services/services_linux.go | 43 ++++++++++++++++++++++++++++ support/services/services_windows.go | 18 ++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 support/services/services_darwin.go create mode 100644 support/services/services_linux.go create mode 100644 support/services/services_windows.go diff --git a/support/services/services_darwin.go b/support/services/services_darwin.go new file mode 100644 index 00000000..90674fb6 --- /dev/null +++ b/support/services/services_darwin.go @@ -0,0 +1,18 @@ +// Package services provides methods for collecting information about system services +package services + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import "github.com/essentialkaos/ek/v12/support" + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Collect collect info about services +func Collect(services ...string) []support.Service { + return nil +} diff --git a/support/services/services_linux.go b/support/services/services_linux.go new file mode 100644 index 00000000..bbe5501c --- /dev/null +++ b/support/services/services_linux.go @@ -0,0 +1,43 @@ +// Package services provides methods for collecting information about system services +package services + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "github.com/essentialkaos/ek/v12/initsystem" + "github.com/essentialkaos/ek/v12/support" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Collect collect info about services +func Collect(services ...string) []support.Service { + var result []support.Service + + for _, s := range services { + service := support.Service{Name: s, Status: support.STATUS_UNKNOWN} + + if initsystem.IsPresent(s) { + service.IsPresent = true + service.IsEnabled, _ = initsystem.IsEnabled(s) + + isWorks, err := initsystem.IsWorks(s) + + switch { + case err == nil && isWorks: + service.Status = support.STATUS_WORKS + case err == nil && !isWorks: + service.Status = support.STATUS_STOPPED + } + } + + result = append(result, service) + } + + return result +} diff --git a/support/services/services_windows.go b/support/services/services_windows.go new file mode 100644 index 00000000..385ae76b --- /dev/null +++ b/support/services/services_windows.go @@ -0,0 +1,18 @@ +// Package services provides methods for collecting information about system services +package services + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import "github.com/essentialkaos/ek/v12/support" + +// ////////////////////////////////////////////////////////////////////////////////// // + +// ❗ Collect collect info about services +func Collect(services ...string) []support.Service { + panic("UNSUPPORTED") +} From f4e4e11c95279650108c29c93108bd86b58a4a5d Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Mon, 29 Apr 2024 01:58:41 +0300 Subject: [PATCH 3/5] [initsystem] Add launchd support --- .scripts/packages.list | 2 +- CHANGELOG.md | 1 + initsystem/examples_test.go | 6 ++ initsystem/initsystem.go | 11 +++- initsystem/initsystem_darwin.go | 82 +++++++++++++++++++++++----- initsystem/initsystem_darwin_test.go | 45 +++++++++++++++ initsystem/initsystem_windows.go | 11 ++-- 7 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 initsystem/initsystem_darwin_test.go diff --git a/.scripts/packages.list b/.scripts/packages.list index 982bfbb8..d2043fcd 100644 --- a/.scripts/packages.list +++ b/.scripts/packages.list @@ -17,7 +17,7 @@ * + fsutil * + hash * + httputil -* ! initsystem +* - initsystem * + jsonutil * + knf * + knf/united diff --git a/CHANGELOG.md b/CHANGELOG.md index ce29ca7d..35a97d60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### 12.119.0 +- `[initsystem]` Added [launchd](https://www.launchd.info) support - `[support/services]` Added package for collecting services info ### 12.118.0 diff --git a/initsystem/examples_test.go b/initsystem/examples_test.go index d6ddfe7e..9b419289 100644 --- a/initsystem/examples_test.go +++ b/initsystem/examples_test.go @@ -34,6 +34,12 @@ func ExampleSystemd() { } } +func ExampleLaunchd() { + if Launchd() { + fmt.Println("Launchd init system is used") + } +} + func ExampleIsPresent() { serviceName := "crond" diff --git a/initsystem/initsystem.go b/initsystem/initsystem.go index 8bffa3ea..84a4565b 100644 --- a/initsystem/initsystem.go +++ b/initsystem/initsystem.go @@ -43,7 +43,7 @@ var ( // ////////////////////////////////////////////////////////////////////////////////// // -// SysV returns true if SysV is used on system +// SysV returns true if SysV is used on the system func SysV() bool { if sysvStatus != _STATUS_UNKNOWN { return sysvStatus == _STATUS_PRESENT @@ -59,7 +59,7 @@ func SysV() bool { return sysvStatus == _STATUS_PRESENT } -// Upstart returns true if Upstart is used on system +// Upstart returns true if Upstart is used on the system func Upstart() bool { if upstartStatus != _STATUS_UNKNOWN { return upstartStatus == _STATUS_PRESENT @@ -75,7 +75,7 @@ func Upstart() bool { return upstartStatus == _STATUS_PRESENT } -// Systemd returns true if Systemd is used on system +// Systemd returns true if Systemd is used on the system func Systemd() bool { if systemdStatus != _STATUS_UNKNOWN { return systemdStatus == _STATUS_PRESENT @@ -91,6 +91,11 @@ func Systemd() bool { return systemdStatus == _STATUS_PRESENT } +// Launchd returns true if Launchd is used on the system +func Launchd() bool { + return false +} + // IsPresent returns true if service is present in any init system func IsPresent(name string) bool { if hasSystemdService(name) { diff --git a/initsystem/initsystem_darwin.go b/initsystem/initsystem_darwin.go index a558c7a5..997a6159 100644 --- a/initsystem/initsystem_darwin.go +++ b/initsystem/initsystem_darwin.go @@ -8,38 +8,92 @@ package initsystem // // // ////////////////////////////////////////////////////////////////////////////////// // -// ❗ SysV returns true if SysV is used on system +import ( + "bytes" + "fmt" + "os/exec" + "strings" + + "github.com/essentialkaos/ek/v12/strutil" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// SysV returns true if SysV is used on system func SysV() bool { - panic("UNSUPPORTED") return false } -// ❗ Upstart returns true if Upstart is used on system +// Upstart returns true if Upstart is used on system func Upstart() bool { - panic("UNSUPPORTED") return false } -// ❗ Systemd returns true if Systemd is used on system +// Systemd returns true if Systemd is used on system func Systemd() bool { - panic("UNSUPPORTED") return false } -// ❗ IsPresent returns true if service is present in any init system +// Launchd returns true if Launchd is used on the system +func Launchd() bool { + return true +} + +// IsPresent returns true if service is present in any init system func IsPresent(name string) bool { - panic("UNSUPPORTED") - return false + isExist, _, _ := getLaunchdStatus(name) + return isExist } -// ❗ IsWorks returns service state +// IsWorks returns service state func IsWorks(name string) (bool, error) { - panic("UNSUPPORTED") - return false, nil + _, isWorks, err := getLaunchdStatus(name) + return isWorks, err } -// ❗ IsEnabled returns true if auto start enabled for given service +// IsEnabled returns true if auto start enabled for given service func IsEnabled(name string) (bool, error) { - panic("UNSUPPORTED") return false, nil } + +// ////////////////////////////////////////////////////////////////////////////////// // + +func getLaunchdStatus(name string) (bool, bool, error) { + output, err := exec.Command("launchctl", "list").Output() + + if err != nil { + return false, false, fmt.Errorf("launchd returned error") + } + + isExist, isWorks := parseLaunchdOutput(output, name) + + return isExist, isWorks, nil +} + +func parseLaunchdOutput(data []byte, name string) (bool, bool) { + buf := bytes.NewBuffer(data) + + for { + line, err := buf.ReadString('\n') + + if err != nil { + break + } + + procName := strutil.ReadField(line, 2, false, '\t') + + if !strings.HasPrefix(procName, name) { + continue + } + + procPID := strutil.ReadField(line, 0, false, '\t') + + if procPID == "-" { + return true, false + } else { + return true, true + } + } + + return false, false +} diff --git a/initsystem/initsystem_darwin_test.go b/initsystem/initsystem_darwin_test.go new file mode 100644 index 00000000..9d6a24d3 --- /dev/null +++ b/initsystem/initsystem_darwin_test.go @@ -0,0 +1,45 @@ +package initsystem + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "os" + "testing" + + . "github.com/essentialkaos/check" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func Test(t *testing.T) { TestingT(t) } + +type InitSuite struct{} + +// ////////////////////////////////////////////////////////////////////////////////// // + +var _ = Suite(&InitSuite{}) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func (s *InitSuite) TestLaunchdIsPresent(c *C) { + c.Assert(IsPresent("com.apple.unknown"), Equals, false) + c.Assert(IsPresent("com.apple.homed"), Equals, true) + c.Assert(IsPresent("com.apple.cloudphotod"), Equals, true) +} + +func (s *InitSuite) TestLaunchdIsWorks(c *C) { + isWorks, err := IsWorks("com.apple.homed") + + c.Assert(err, IsNil) + c.Assert(isWorks, Equals, true) + + isWorks, err = IsWorks("com.apple.cloudphotod") + + c.Assert(err, IsNil) + c.Assert(isWorks, Equals, false) +} diff --git a/initsystem/initsystem_windows.go b/initsystem/initsystem_windows.go index a558c7a5..68e4ac20 100644 --- a/initsystem/initsystem_windows.go +++ b/initsystem/initsystem_windows.go @@ -11,35 +11,34 @@ package initsystem // ❗ SysV returns true if SysV is used on system func SysV() bool { panic("UNSUPPORTED") - return false } // ❗ Upstart returns true if Upstart is used on system func Upstart() bool { panic("UNSUPPORTED") - return false } // ❗ Systemd returns true if Systemd is used on system func Systemd() bool { panic("UNSUPPORTED") - return false +} + +// ❗ Launchd returns true if Launchd is used on the system +func Launchd() bool { + panic("UNSUPPORTED") } // ❗ IsPresent returns true if service is present in any init system func IsPresent(name string) bool { panic("UNSUPPORTED") - return false } // ❗ IsWorks returns service state func IsWorks(name string) (bool, error) { panic("UNSUPPORTED") - return false, nil } // ❗ IsEnabled returns true if auto start enabled for given service func IsEnabled(name string) (bool, error) { panic("UNSUPPORTED") - return false, nil } From ed27c0b5f700f92c805acbd96fa3a38e799a7ec1 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Mon, 29 Apr 2024 02:00:12 +0300 Subject: [PATCH 4/5] [initsystem] Add launchd support --- initsystem/initsystem_darwin_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/initsystem/initsystem_darwin_test.go b/initsystem/initsystem_darwin_test.go index 9d6a24d3..8197f23c 100644 --- a/initsystem/initsystem_darwin_test.go +++ b/initsystem/initsystem_darwin_test.go @@ -8,7 +8,6 @@ package initsystem // ////////////////////////////////////////////////////////////////////////////////// // import ( - "os" "testing" . "github.com/essentialkaos/check" From be40c8664d5079878784040c3818e6fa232ee1af Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Mon, 29 Apr 2024 02:09:13 +0300 Subject: [PATCH 5/5] [support/services] Add package for collecting services info --- .../{services_linux.go => services.go} | 3 +++ support/services/services_darwin.go | 18 ------------------ 2 files changed, 3 insertions(+), 18 deletions(-) rename support/services/{services_linux.go => services.go} (97%) delete mode 100644 support/services/services_darwin.go diff --git a/support/services/services_linux.go b/support/services/services.go similarity index 97% rename from support/services/services_linux.go rename to support/services/services.go index bbe5501c..21a5a46d 100644 --- a/support/services/services_linux.go +++ b/support/services/services.go @@ -1,3 +1,6 @@ +//go:build !windows +// +build !windows + // Package services provides methods for collecting information about system services package services diff --git a/support/services/services_darwin.go b/support/services/services_darwin.go deleted file mode 100644 index 90674fb6..00000000 --- a/support/services/services_darwin.go +++ /dev/null @@ -1,18 +0,0 @@ -// Package services provides methods for collecting information about system services -package services - -// ////////////////////////////////////////////////////////////////////////////////// // -// // -// Copyright (c) 2024 ESSENTIAL KAOS // -// Apache License, Version 2.0 // -// // -// ////////////////////////////////////////////////////////////////////////////////// // - -import "github.com/essentialkaos/ek/v12/support" - -// ////////////////////////////////////////////////////////////////////////////////// // - -// Collect collect info about services -func Collect(services ...string) []support.Service { - return nil -}