Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolved Issue 1661 - AIX Runtime Panic in Host #1663

Merged
merged 11 commits into from
Jun 14, 2024
86 changes: 48 additions & 38 deletions host/host_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,69 +45,79 @@ func BootTimeWithContext(ctx context.Context) (btime uint64, err error) {
return timeSince(ut), nil
}

// This function takes multiple formats of output frmo the uptime
// command and converts the data into minutes.
// Parses result from uptime into minutes
// Some examples of uptime output that this command handles:
// 11:54AM up 13 mins, 1 user, load average: 2.78, 2.62, 1.79
// 12:41PM up 1 hr, 1 user, load average: 2.47, 2.85, 2.83
// 07:43PM up 5 hrs, 1 user, load average: 3.27, 2.91, 2.72
// 11:18:23 up 83 days, 18:29, 4 users, load average: 0.16, 0.03, 0.01
// 08:47PM up 2 days, 20 hrs, 1 user, load average: 2.47, 2.17, 2.17
// 01:16AM up 4 days, 29 mins, 1 user, load average: 2.29, 2.31, 2.21
func UptimeWithContext(ctx context.Context) (uint64, error) {
out, err := invoke.CommandWithContext(ctx, "uptime")
if err != nil {
return 0, err
}

// Convert our uptime to a series of fields we can extract
ut := strings.Fields(string(out[:]))
return parseUptime(string(out[:])), nil
}

func parseUptime(uptime string) uint64 {
ut := strings.Fields(uptime)
var days, hours, mins uint64
var err error

// Convert the second field value to integer
var days uint64 = 0
var hours uint64 = 0
var minutes uint64 = 0
if ut[3] == "day," || ut[3] == "days," {
switch {
case ut[3] == "day," || ut[3] == "days,":
days, err = strconv.ParseUint(ut[2], 10, 64)
if err != nil {
return 0, err
return 0
}

// Split field 4 into hours and minutes
hm := strings.Split(ut[4], ":")
hours, err = strconv.ParseUint(hm[0], 10, 64)
if err != nil {
return 0, err
}
minutes, err = strconv.ParseUint(strings.Replace(hm[1], ",", "", -1), 10, 64)
if err != nil {
return 0, err
// day provided along with a single hour or hours
// ie: up 2 days, 20 hrs,
if ut[5] == "hr," || ut[5] == "hrs," {
hours, err = strconv.ParseUint(ut[4], 10, 64)
if err != nil {
return 0
}
}
} else if ut[3] == "hr," || ut[3] == "hrs," {
hours, err = strconv.ParseUint(ut[2], 10, 64)
if err != nil {
return 0, err

// mins provided along with a single min or mins
// ie: up 4 days, 29 mins,
if ut[5] == "min," || ut[5] == "mins," {
mins, err = strconv.ParseUint(ut[4], 10, 64)
if err != nil {
return 0
}
}
} else if ut[3] == "mins," {
minutes, err = strconv.ParseUint(ut[2], 10, 64)
if err != nil {
return 0, err

// alternatively day provided with hh:mm
// ie: up 83 days, 18:29
if strings.Contains(ut[4], ":") {
hm := strings.Split(ut[4], ":")
hours, err = strconv.ParseUint(hm[0], 10, 64)
if err != nil {
return 0
}
mins, err = strconv.ParseUint(strings.Trim(hm[1], ","), 10, 64)
if err != nil {
return 0
}
}
} else if _, err := strconv.ParseInt(ut[3], 10, 64); err == nil && strings.Contains(ut[2], ":") {
// Split field 2 into hours and minutes
hm := strings.Split(ut[2], ":")
hours, err = strconv.ParseUint(hm[0], 10, 64)
case ut[3] == "hr," || ut[3] == "hrs,":
hours, err = strconv.ParseUint(ut[2], 10, 64)
if err != nil {
return 0, err
return 0
}
minutes, err = strconv.ParseUint(strings.Replace(hm[1], ",", "", -1), 10, 64)
case ut[3] == "mins," || ut[3] == "mins,":
mins, err = strconv.ParseUint(ut[2], 10, 64)
if err != nil {
return 0, err
return 0
}
}

// Stack them all together as minutes
total_time := (days * 24 * 60) + (hours * 60) + minutes

return total_time, nil
return (days * 24 * 60) + (hours * 60) + mins
}

// This is a weak implementation due to the limitations on retrieving this data in AIX
Expand Down
43 changes: 43 additions & 0 deletions host/host_aix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build aix

package host

import (
"testing"
)

func TestParseUptimeValidInput(t *testing.T) {
testCases := []struct {
input string
expected uint64
}{
{"11:54AM up 13 mins, 1 user, load average: 2.78, 2.62, 1.79", 13},
{"12:41PM up 1 hr, 1 user, load average: 2.47, 2.85, 2.83", 60},
{"07:43PM up 5 hrs, 1 user, load average: 3.27, 2.91, 2.72", 300},
{"11:18:23 up 83 days, 18:29, 4 users, load average: 0.16, 0.03, 0.01", 120629},
{"08:47PM up 2 days, 20 hrs, 1 user, load average: 2.47, 2.17, 2.17", 4080},
{"01:16AM up 4 days, 29 mins, 1 user, load average: 2.29, 2.31, 2.21", 5789},
}
for _, tc := range testCases {
got := parseUptime(tc.input)
if got != tc.expected {
t.Errorf("parseUptime(%q) = %v, want %v", tc.input, got, tc.expected)
}
}
}

func TestParseUptimeInvalidInput(t *testing.T) {
testCases := []string{
"", // blank
"2x", // invalid string
"150", // integer
}

for _, tc := range testCases {
got := parseUptime(tc)
if got > 0 {
t.Errorf("parseUptime(%q) expected zero to be returned, received %v", tc, got)
}
}
}
Loading