diff --git a/agent/battery/battery.go b/agent/battery/battery.go index af35e002..4285aeec 100644 --- a/agent/battery/battery.go +++ b/agent/battery/battery.go @@ -33,7 +33,10 @@ var errNoBatteries = errors.New("no readable batteries") func normalizeBatteries(batteries []Battery) []Battery { nameCounts := make(map[string]int, len(batteries)) for i := range batteries { - name := strings.TrimSpace(batteries[i].Name) + // Names come from firmware (e.g. sysfs model_name) and are not guaranteed to + // be valid UTF-8. Invalid bytes are rejected when the hub decodes the CBOR + // payload, which drops every metric for the system, so strip them here. + name := strings.TrimSpace(strings.ToValidUTF8(batteries[i].Name, "")) if name == "" { name = "Battery " + strconv.Itoa(i+1) } diff --git a/agent/battery/battery_test.go b/agent/battery/battery_test.go index 9280eae3..fcc11f5e 100644 --- a/agent/battery/battery_test.go +++ b/agent/battery/battery_test.go @@ -2,6 +2,7 @@ package battery import ( "testing" + "unicode/utf8" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -33,3 +34,15 @@ func TestNormalizeBatteriesFallbackNames(t *testing.T) { bats := normalizeBatteries([]Battery{{}, {}, {Name: "Mouse"}, {Name: "Mouse"}}) assert.Equal(t, []string{"Battery 1", "Battery 2", "Mouse", "Mouse (2)"}, []string{bats[0].Name, bats[1].Name, bats[2].Name, bats[3].Name}) } + +func TestNormalizeBatteriesStripsInvalidUTF8(t *testing.T) { + // Firmware occasionally reports names that are not valid UTF-8 (a ThinkPad + // reporting "LNV-5B11K63024@\xd0" in model_name is a real example). + bats := normalizeBatteries([]Battery{{Name: "LNV-5B11K63024@\xd0"}, {Name: "\xff\xfe"}}) + assert.Equal(t, "LNV-5B11K63024@", bats[0].Name) + // A name made up entirely of invalid bytes falls back to the generic name. + assert.Equal(t, "Battery 2", bats[1].Name) + for _, b := range bats { + assert.True(t, utf8.ValidString(b.Name)) + } +}