go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/services/alpine_openrc.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package services 5 6 import ( 7 "bufio" 8 "io" 9 "path/filepath" 10 "regexp" 11 12 "github.com/spf13/afero" 13 "go.mondoo.com/cnquery/providers/os/connection/shared" 14 ) 15 16 type AlpineOpenrcServiceManager struct { 17 conn shared.Connection 18 } 19 20 func (s *AlpineOpenrcServiceManager) Name() string { 21 return "OpenRC Init Service Manager" 22 } 23 24 func (s *AlpineOpenrcServiceManager) List() ([]*Service, error) { 25 // retrieve service list by retrieving all files 26 var services []*Service 27 28 f, err := s.conn.FileSystem().Open("/etc/init.d") 29 if err != nil { 30 return nil, err 31 } 32 defer f.Close() 33 34 files, err := f.Readdir(-1) 35 if err != nil { 36 return nil, err 37 } 38 39 // retrieve service status from running systems 40 serviceStatusMap := map[string]bool{} 41 if s.conn.Capabilities().Has(shared.Capability_RunCommand) { 42 // check if the rc-status command exits, if not no service is running 43 cmd, err := s.conn.RunCommand("which rc-status") 44 if err != nil { 45 return nil, err 46 } 47 48 // if the rc-status command is installed 49 cmdOut, _ := io.ReadAll(cmd.Stdout) 50 if string(cmdOut) != "" { 51 cmd, err := s.conn.RunCommand("rc-status -s") 52 if err != nil { 53 return nil, err 54 } 55 56 serviceStatusMap, err = ParseOpenRCServiceStatus(cmd.Stdout) 57 if err != nil { 58 return nil, err 59 } 60 } 61 } 62 63 // check for services in runlevel 64 runlevel, err := determineOpenRcRunlevel(s.conn.FileSystem()) 65 if err != nil { 66 return nil, err 67 } 68 69 // build up service objects 70 for i := range files { 71 serviceFile := files[i] 72 name := serviceFile.Name() 73 services = append(services, &Service{ 74 Name: name, 75 Enabled: runlevel[name], // read status from rc 76 Installed: true, 77 Running: serviceStatusMap[name], // read from status from rc-status command 78 Type: "openrc", 79 }) 80 } 81 82 return services, nil 83 } 84 85 var OPENRC_SERVICE_STARTED = regexp.MustCompile(`^([a-zA-Z-\d]+)\s+\[\s*(stopped|started)\s*\]$`) 86 87 func ParseOpenRCServiceStatus(input io.Reader) (map[string]bool, error) { 88 status := map[string]bool{} 89 90 scanner := bufio.NewScanner(input) 91 for scanner.Scan() { 92 line := scanner.Text() 93 m := OPENRC_SERVICE_STARTED.FindStringSubmatch(line) 94 if len(m) == 3 { 95 status[m[1]] = (m[2] == "started") 96 } 97 } 98 return status, nil 99 } 100 101 func determineOpenRcRunlevel(fs afero.Fs) (map[string]bool, error) { 102 activated := map[string]bool{} 103 runlevelRoot := "/etc/runlevels/" 104 105 afutil := afero.Afero{Fs: fs} 106 ok, err := afutil.Exists(runlevelRoot) 107 if err != nil { 108 return nil, err 109 } 110 111 if ok { 112 f, err := fs.Open(runlevelRoot) 113 if err != nil { 114 return nil, err 115 } 116 117 // iterate over level 118 levels, err := f.Readdirnames(-1) 119 if err != nil { 120 return nil, err 121 } 122 123 for i := range levels { 124 level := levels[i] 125 126 levelF, err := fs.Open(filepath.Join(runlevelRoot, level)) 127 if err != nil { 128 return nil, err 129 } 130 131 serviceNames, err := levelF.Readdirnames(-1) 132 if err != nil { 133 levelF.Close() 134 return nil, err 135 } 136 levelF.Close() 137 138 for j := range serviceNames { 139 activated[serviceNames[j]] = true 140 } 141 } 142 } 143 144 return activated, nil 145 }