apcssh: add shell cmd timeout

It was possible for scanner.Scan() to block indefinitely if the UPS never returned the expected prompt regex pattern. This could occur with a UPS using a prompt format I'm not aware of, or if the UPS responds in a non-standard way.

This change ensures that Scan() is aborted after a fixed amount of blocking time and the shell cmd function accordingly returns an error.

Some error messages, comments, and var names are also updated for clarity.
This commit is contained in:
Greg T. Wallace 2024-06-19 19:56:17 -04:00
parent 841a459dca
commit 703c26bd27
2 changed files with 92 additions and 44 deletions

View file

@ -1,31 +1,31 @@
package apcssh
import (
"io"
"regexp"
)
// scanAPCShell is a SplitFunc to capture shell output after each interactive
// shell command is run
func scanAPCShell(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
// EOF is not an expected response and should error (e.g., when the output pipe
// gets closed by timeout)
if atEOF {
return len(data), dropCR(data), io.ErrUnexpectedEOF
} else if len(data) == 0 {
// no data to process, request more data
return 0, nil, nil
}
// regex for shell prompt (e.g., `apc@apc>`, `apc>`, `some@dev>`, `other123>`, etc.)
re := regexp.MustCompile(`(\r\n|\r|\n)([A-Za-z0-9.]+@?)?[A-Za-z0-9.]+>`)
// find match for prompt
if index := re.FindStringIndex(string(data)); index != nil {
// advance starts after the prompt; token is everything before the prompt
return index[1], dropCR(data[0:index[0]]), nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), dropCR(data), nil
}
// Request more data.
// no match, request more data
return 0, nil, nil
}