go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/uptime/unix.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package uptime 5 6 import ( 7 "fmt" 8 "io" 9 "regexp" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/rs/zerolog/log" 15 "go.mondoo.com/cnquery/providers/os/connection/shared" 16 ) 17 18 var UnixUptimeRegex = regexp.MustCompile(`^.*up[\s]*(?:\s*(\d+)\s(day[s]*),)*(?:\s*(\d+)\s(min[s]*),)*(?:\s+([\d:]+),\s)*\s*(?:(\d+)\suser[s]*,\s)*\s*load\s+average[s]*:\s+(\d+[\.,]\d+)[,\s]+(\d+[\.,]\d+)[,\s]+(\d+[\.,]\d+)\s*$`) 19 20 type UnixUptimeResult struct { 21 Duration int64 22 Users int 23 LoadOneMinute float64 24 LoadFiveMinutes float64 25 LoadFifteenMinutes float64 26 } 27 28 func unixDuration(date, measure string) (int64, error) { 29 // calculate the time x * days / minutes + hours ( m[1]*m[2] + m[3]) 30 duration, err := strconv.ParseInt(date, 10, 64) 31 if err != nil { 32 return 0, err 33 } 34 35 switch measure { 36 case "day": 37 fallthrough 38 case "days": 39 duration = duration * 24 * int64(time.Hour) 40 case "min": 41 fallthrough 42 case "mins": 43 duration = duration * int64(time.Minute) 44 } 45 return duration, nil 46 } 47 48 func ParseUnixUptime(uptime string) (*UnixUptimeResult, error) { 49 log.Debug().Str("uptime", uptime).Msg("parse") 50 m := UnixUptimeRegex.FindStringSubmatch(uptime) 51 52 if len(m) != 10 { 53 return nil, fmt.Errorf("could not parse uptime: %s", uptime) 54 } 55 56 var duration int64 57 var err error 58 59 // parse days 60 if len(m[2]) > 0 { 61 unixDuration, err := unixDuration(m[1], m[2]) 62 if err != nil { 63 return nil, err 64 } 65 duration = duration + unixDuration 66 } 67 68 // parse mins 69 if len(m[4]) > 0 { 70 unixDuration, err := unixDuration(m[3], m[4]) 71 if err != nil { 72 return nil, err 73 } 74 duration = duration + unixDuration 75 } 76 77 // add optional hours 78 if len(m[5]) > 0 { 79 hours := strings.Split(m[5], ":") 80 if len(hours) == 2 { 81 // log.Debug().Msg("parse hour") 82 hh, err := strconv.ParseInt(hours[0], 10, 64) 83 if err != nil { 84 return nil, err 85 } 86 87 // log.Debug().Msg("parse minutes") 88 mm, err := strconv.ParseInt(hours[1], 10, 64) 89 if err != nil { 90 return nil, err 91 } 92 93 duration = duration + hh*int64(time.Hour) + mm*int64(time.Minute) 94 } else { 95 return nil, fmt.Errorf("could not parse uptime hours: %s", uptime) 96 } 97 } 98 99 // users is optional and is not returned on alpine 100 users := 0 101 if len(m[6]) > 0 { 102 users, err = strconv.Atoi(m[6]) 103 if err != nil { 104 return nil, err 105 } 106 } 107 108 loadOneMinute, err := strconv.ParseFloat(strings.Replace(m[7], ",", ".", 1), 64) 109 if err != nil { 110 return nil, err 111 } 112 113 loadFiveMinutes, err := strconv.ParseFloat(strings.Replace(m[8], ",", ".", 1), 64) 114 if err != nil { 115 return nil, err 116 } 117 118 loadFifteenMinutes, err := strconv.ParseFloat(strings.Replace(m[9], ",", ".", 1), 64) 119 if err != nil { 120 return nil, err 121 } 122 123 return &UnixUptimeResult{ 124 Duration: duration, 125 Users: users, 126 LoadOneMinute: loadOneMinute, 127 LoadFiveMinutes: loadFiveMinutes, 128 LoadFifteenMinutes: loadFifteenMinutes, 129 }, nil 130 } 131 132 type Unix struct { 133 conn shared.Connection 134 } 135 136 func (s *Unix) Name() string { 137 return "Unix Uptime" 138 } 139 140 func (s *Unix) Duration() (time.Duration, error) { 141 cmd, err := s.conn.RunCommand("uptime") 142 if err != nil { 143 return 0, err 144 } 145 146 ut, err := s.parse(cmd.Stdout) 147 if err != nil { 148 return 0, err 149 } 150 151 return time.Duration(ut.Duration), nil 152 } 153 154 func (s *Unix) parse(r io.Reader) (*UnixUptimeResult, error) { 155 content, err := io.ReadAll(r) 156 if err != nil { 157 return nil, err 158 } 159 160 return ParseUnixUptime(string(content)) 161 }