go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/packages/opkg_packages.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package packages 5 6 import ( 7 "bufio" 8 "fmt" 9 "io" 10 "regexp" 11 "strings" 12 13 "github.com/rs/zerolog/log" 14 "go.mondoo.com/cnquery/providers/os/connection/shared" 15 ) 16 17 const ( 18 OpkgPkgFormat = "opkg" 19 ) 20 21 var OPKG_LIST_PACKAGE_REGEX = regexp.MustCompile(`^([\w\d\-]+)\s-\s([\w\d\-\.]+)$`) 22 23 // ParseOpkgListPackagesCommand parses the output of `opkg list-installed` 24 func ParseOpkgListPackagesCommand(input io.Reader) []Package { 25 pkgs := []Package{} 26 scanner := bufio.NewScanner(input) 27 for scanner.Scan() { 28 line := scanner.Text() 29 m := OPKG_LIST_PACKAGE_REGEX.FindStringSubmatch(line) 30 if m != nil { 31 pkgs = append(pkgs, Package{ 32 Name: m[1], 33 Version: m[2], 34 Format: OpkgPkgFormat, 35 }) 36 } 37 } 38 return pkgs 39 } 40 41 var ( 42 OPKG_REGEX = regexp.MustCompile(`^(.+):\s(.+)$`) 43 OPKG_ORIGIN_REGEX = regexp.MustCompile(`^\s*([^\(]*)(?:\((.*)\))?\s*$`) 44 ) 45 46 // ParseOpkgPackages parses the opkg database content located in: 47 // `/var/lib/opkg/status` or `/usr/lib/opkg/status` 48 func ParseOpkgPackages(input io.Reader) ([]Package, error) { 49 const STATE_RESET = 0 50 const STATE_DESC = 1 51 pkgs := []Package{} 52 53 add := func(pkg Package) { 54 // do sanitization checks to ensure we have minimal information 55 if pkg.Name != "" && pkg.Version != "" { 56 pkgs = append(pkgs, pkg) 57 } else { 58 log.Debug().Msg("ignored opkg packages since information is missing") 59 } 60 } 61 62 scanner := bufio.NewScanner(input) 63 pkg := Package{Format: OpkgPkgFormat} 64 state := STATE_RESET 65 var key string 66 for scanner.Scan() { 67 line := scanner.Text() 68 69 // reset package definition once we reach a newline 70 if len(line) == 0 { 71 add(pkg) 72 pkg = Package{Format: OpkgPkgFormat} 73 } 74 75 m := OPKG_REGEX.FindStringSubmatch(line) 76 key = "" 77 if m != nil { 78 key = m[1] 79 state = STATE_RESET 80 } 81 switch { 82 case key == "Package": 83 pkg.Name = strings.TrimSpace(m[2]) 84 case key == "Version": 85 pkg.Version = strings.TrimSpace(m[2]) 86 case key == "Architecture": 87 pkg.Arch = strings.TrimSpace(m[2]) 88 case key == "Status": 89 pkg.Status = strings.TrimSpace(m[2]) 90 case key == "Source": 91 o := OPKG_ORIGIN_REGEX.FindStringSubmatch(m[2]) 92 if o != nil && len(o) >= 1 { 93 pkg.Origin = strings.TrimSpace(o[1]) 94 } else { 95 log.Error().Str("origin", m[2]).Msg("cannot parse opkg origin") 96 } 97 // description supports multi-line statements, start desc 98 case key == "Description": 99 pkg.Description = strings.TrimSpace(m[2]) 100 state = STATE_DESC 101 // next desc line, append to previous one 102 case state == STATE_DESC: 103 pkg.Description += "\n" + strings.TrimSpace(line) 104 } 105 } 106 107 // if the last line is not an empty line we have things in flight, lets check it 108 add(pkg) 109 110 return pkgs, nil 111 } 112 113 type OpkgPkgManager struct { 114 conn shared.Connection 115 } 116 117 func (opkg *OpkgPkgManager) Name() string { 118 return "Opkg Package Manager" 119 } 120 121 func (opkg *OpkgPkgManager) Format() string { 122 return OpkgPkgFormat 123 } 124 125 func (opkg *OpkgPkgManager) List() ([]Package, error) { 126 // if we can run commands, we can use `opkg list-installed` 127 if opkg.conn.Capabilities().Has(shared.Capability_RunCommand) { 128 cmd, err := opkg.conn.RunCommand("opkg list-installed") 129 if err != nil { 130 return nil, fmt.Errorf("could not read package list") 131 } 132 return ParseOpkgListPackagesCommand(cmd.Stdout), nil 133 } 134 135 // otherwise let's try to read the package list from file 136 return opkg.ListFromFile() 137 } 138 139 func (opkg *OpkgPkgManager) ListFromFile() ([]Package, error) { 140 fs := opkg.conn.FileSystem() 141 opkgStatusFiles := []string{ 142 "/usr/lib/opkg/status", 143 "/var/lib/opkg/status", 144 } 145 146 var opkgStatusFile string 147 for _, f := range opkgStatusFiles { 148 _, err := fs.Stat(f) 149 if err == nil { 150 opkgStatusFile = f 151 break 152 } 153 } 154 155 if opkgStatusFile == "" { 156 return nil, fmt.Errorf("could not find opkg package list") 157 } 158 159 fi, err := fs.Open(opkgStatusFile) 160 if err != nil { 161 return nil, fmt.Errorf("could not read opkg package list") 162 } 163 defer fi.Close() 164 165 list, err := ParseOpkgPackages(fi) 166 if err != nil { 167 return nil, fmt.Errorf("could not parse opkg package list") 168 } 169 return list, nil 170 } 171 172 func (opkg *OpkgPkgManager) Available() (map[string]PackageUpdate, error) { 173 return map[string]PackageUpdate{}, nil 174 }