github.com/ubuntu/ubuntu-report@v1.7.4-0.20240410144652-96f37d845fac/internal/metrics/files.go (about) 1 package metrics 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "math" 7 "os" 8 "path/filepath" 9 "strconv" 10 "strings" 11 12 "github.com/pkg/errors" 13 log "github.com/sirupsen/logrus" 14 "github.com/ubuntu/ubuntu-report/internal/utils" 15 ) 16 17 func (m Metrics) getVersion() string { 18 v, err := matchFromFile(filepath.Join(m.root, "etc/os-release"), `^VERSION_ID="(.*)"$`, false) 19 if err != nil { 20 log.Infof("couldn't get version information from os-release: "+utils.ErrFormat, err) 21 return "" 22 } 23 return v 24 } 25 26 func (m Metrics) getRAM() *float64 { 27 s, err := matchFromFile(filepath.Join(m.root, "proc/meminfo"), `^MemTotal: +(\d+) kB$`, false) 28 if err != nil { 29 log.Infof("couldn't get RAM information from meminfo: "+utils.ErrFormat, err) 30 return nil 31 } 32 v, err := convKBToGB(s) 33 if err != nil { 34 log.Infof("partition size should be an integer: "+utils.ErrFormat, err) 35 return nil 36 } 37 return &v 38 } 39 40 func (m Metrics) getTimeZone() string { 41 v, err := getFromFileTrimmed(filepath.Join(m.root, "etc/timezone")) 42 if err != nil { 43 log.Infof("couldn't get timezone information: "+utils.ErrFormat, err) 44 return "" 45 } 46 if strings.Contains(v, "\n") { 47 log.Infof(utils.ErrFormat, errors.Errorf("malformed timezone information, file contains: %s", v)) 48 return "" 49 } 50 return v 51 } 52 53 func (m Metrics) getAutologin() bool { 54 v, err := matchFromFile(filepath.Join(m.root, "etc/gdm3/custom.conf"), `^AutomaticLoginEnable ?= ?(.*)$`, true) 55 if err != nil { 56 log.Infof("couldn't get autologin information from gdm: "+utils.ErrFormat, err) 57 return false 58 } 59 if strings.ToLower(v) != "true" { 60 return false 61 } 62 return true 63 } 64 65 func (m Metrics) getOEM() (string, string, string, string) { 66 v, err := getFromFileTrimmed(filepath.Join(m.root, "sys/class/dmi/id/sys_vendor")) 67 if err != nil { 68 log.Infof("couldn't get sys vendor information: "+utils.ErrFormat, err) 69 } 70 if strings.Contains(v, "\n") { 71 log.Infof(utils.ErrFormat, errors.Errorf("malformed sys vendor information, file contains: %s", v)) 72 v = "" 73 } 74 p, err := getFromFileTrimmed(filepath.Join(m.root, "sys/class/dmi/id/product_name")) 75 if err != nil { 76 log.Infof("couldn't get sys product name information: "+utils.ErrFormat, err) 77 } 78 if strings.Contains(p, "\n") { 79 log.Infof(utils.ErrFormat, errors.Errorf("malformed sys product name information, file contains: %s", p)) 80 p = "" 81 } 82 f, err := getFromFileTrimmed(filepath.Join(m.root, "sys/class/dmi/id/product_family")) 83 if err != nil { 84 log.Infof("couldn't get sys product family information: "+utils.ErrFormat, err) 85 } 86 if strings.Contains(f, "\n") { 87 log.Infof(utils.ErrFormat, errors.Errorf("malformed sys product family information, file contains: %s", f)) 88 f = "" 89 } 90 dcd, err := matchFromFile(filepath.Join(m.root, "var/lib/ubuntu_dist_channel"), `^([^\s#]+)$`, true) 91 if err != nil { 92 log.Infof("no DCD information: "+utils.ErrFormat, err) 93 } 94 return v, p, f, dcd 95 } 96 97 func (m Metrics) getBIOS() (string, string) { 98 vd, err := getFromFileTrimmed(filepath.Join(m.root, "sys/class/dmi/id/bios_vendor")) 99 if err != nil { 100 log.Infof("couldn't get bios vendor information: "+utils.ErrFormat, err) 101 vd = "" 102 } 103 if strings.Contains(vd, "\n") { 104 log.Infof(utils.ErrFormat, errors.Errorf("malformed bios vendor information, file contains: %s", vd)) 105 vd = "" 106 } 107 ve, err := getFromFileTrimmed(filepath.Join(m.root, "sys/class/dmi/id/bios_version")) 108 if err != nil { 109 log.Infof("couldn't get bios version: "+utils.ErrFormat, err) 110 ve = "" 111 } 112 if strings.Contains(ve, "\n") { 113 log.Infof(utils.ErrFormat, errors.Errorf("malformed bios version information, file contains: %s", ve)) 114 ve = "" 115 } 116 return vd, ve 117 } 118 119 func (m Metrics) getLivePatch() bool { 120 if _, err := os.Stat(filepath.Join(m.root, "var/snap/canonical-livepatch/common/machine-token")); err != nil { 121 return false 122 } 123 return true 124 } 125 126 func (m Metrics) getDisks() []float64 { 127 var sizes []float64 128 129 blockFolder := filepath.Join(m.root, "sys/block") 130 dirs, err := ioutil.ReadDir(blockFolder) 131 if err != nil { 132 log.Infof("couldn't get disk block information: "+utils.ErrFormat, err) 133 return nil 134 } 135 136 for _, d := range dirs { 137 if !(strings.HasPrefix(d.Name(), "hd") || strings.HasPrefix(d.Name(), "sd") || strings.HasPrefix(d.Name(), "vd")) { 138 continue 139 } 140 141 v, err := getFromFileTrimmed(filepath.Join(blockFolder, d.Name(), "size")) 142 if err != nil { 143 log.Infof("couldn't get disk block information for %s: "+utils.ErrFormat, d.Name(), err) 144 continue 145 } 146 s, err := strconv.Atoi(v) 147 if err != nil { 148 log.Infof("number of block for disk %s isn't an integer: "+utils.ErrFormat, d.Name(), err) 149 continue 150 } 151 152 v, err = getFromFileTrimmed(filepath.Join(blockFolder, d.Name(), "queue/logical_block_size")) 153 if err != nil { 154 log.Infof("couldn't get disk block information for %s: "+utils.ErrFormat, d.Name(), err) 155 continue 156 } 157 bs, err := strconv.Atoi(v) 158 if err != nil { 159 log.Infof("block size for disk %s isn't an integer: "+utils.ErrFormat, d.Name(), err) 160 continue 161 } 162 163 // convert in Gib in .1 precision 164 size := float64(s) * float64(bs) / (1000 * 1000 * 1000) 165 size = math.Round(size*10) / 10 166 167 sizes = append(sizes, size) 168 } 169 170 return sizes 171 } 172 173 func (m Metrics) installerInfo() json.RawMessage { 174 return getAndValidateJSONFromFile(filepath.Join(m.root, installerLogsPath), "install") 175 } 176 177 func (m Metrics) upgradeInfo() json.RawMessage { 178 return getAndValidateJSONFromFile(filepath.Join(m.root, upgradeLogsPath), "upgrade") 179 } 180 181 func matchFromFile(p, regex string, notFoundOk bool) (string, error) { 182 f, err := os.Open(p) 183 if err != nil { 184 return "", errors.Wrapf(err, "couldn't open %s", p) 185 } 186 defer f.Close() 187 188 return filterFirst(f, regex, notFoundOk) 189 } 190 191 func getFromFile(p string) ([]byte, error) { 192 f, err := os.Open(p) 193 if err != nil { 194 return nil, errors.Wrapf(err, "couldn't open %s", p) 195 } 196 defer f.Close() 197 198 b, err := ioutil.ReadAll(f) 199 if err != nil { 200 return nil, errors.Wrapf(err, "couldn't read %s", p) 201 } 202 203 return b, nil 204 } 205 206 func getFromFileTrimmed(p string) (string, error) { 207 b, err := getFromFile(p) 208 if err != nil { 209 return "", err 210 } 211 return strings.TrimSpace(string(b)), nil 212 } 213 214 func getAndValidateJSONFromFile(p string, errmsg string) json.RawMessage { 215 b, err := getFromFile(p) 216 if err != nil { 217 log.Infof("no %s data found: "+utils.ErrFormat, errmsg, err) 218 return nil 219 } 220 if !json.Valid(b) { 221 log.Infof("%s data found, but not valid json.", errmsg) 222 return nil 223 } 224 return json.RawMessage(b) 225 }