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  }