diff --git a/pkg/apcssh/cmd_gettime.go b/pkg/apcssh/cmd_gettime.go new file mode 100644 index 0000000..7da6397 --- /dev/null +++ b/pkg/apcssh/cmd_gettime.go @@ -0,0 +1,56 @@ +package apcssh + +import ( + "fmt" + "regexp" + "strings" + "time" +) + +// GetTime sends the APC `system` command and then attempts to parse the +// response to determine the UPS current date/time. +func (cli *Client) GetTime() (time.Time, error) { + result, err := cli.cmd("date") + if err != nil { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s)", err) + } else if !strings.EqualFold(result.code, "e000") { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s: %s)", result.code, result.codeText) + } + + // capture each portion of the date information + regex := regexp.MustCompile(`Date:\s*(\S*)\s*[\r\n]Time:\s*(\S*)\s*[\r\n]Format:\s*(\S*)\s*[\r\n]Time Zone:\s*(\S*)\s*[\r\n]?`) + datePieces := regex.FindStringSubmatch(result.resultText) + if len(datePieces) != 5 { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (length of datetime value pieces was %d (expected: 5))", len(datePieces)) + } + dateVal := datePieces[1] + timeVal := datePieces[2] + formatUPSVal := datePieces[3] + timeZoneVal := datePieces[4] + + // known APC UPS format strings + dateFormatVal := "" + switch formatUPSVal { + case "mm/dd/yyyy": + dateFormatVal = "01/02/2006" + case "dd.mm.yyyy": + dateFormatVal = "02.01.2006" + case "mmm-dd-yy": + dateFormatVal = "Jan-02-06" + case "dd-mmm-yy": + dateFormatVal = "02-Jan-06" + case "yyyy-mm-dd": + dateFormatVal = "2006-01-02" + + default: + return time.Time{}, fmt.Errorf("apcssh: failed to get time (ups returned unknown format string (%s)", formatUPSVal) + } + + // convert to time.Time + t, err := time.Parse(dateFormatVal+" 15:04:05 -07:00", dateVal+" "+timeVal+" "+timeZoneVal) + if err != nil { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (time parse failed: %s)", err) + } + + return t, nil +} diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index a431eb8..eacda53 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -12,6 +12,8 @@ import ( "time" ) +const timeLoggingFormat = time.RFC1123Z + // cmdInstall is the app's command to create apc p15 file content from key and cert // pem files and upload the p15 to the specified APC UPS func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { @@ -75,6 +77,16 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { } app.stdLogger.Println("install: connected to ups ssh, installing ssl key and cert...") + // check time - don't fail it time is no good, just do logging here + upsT, err := client.GetTime() + if err != nil { + app.errLogger.Printf("warn: install: failed to fetch UPS time (%s), you should manually verify the time is correct on the UPS", err) + } else if upsT.After(time.Now().Add(1*time.Hour)) || upsT.Before(time.Now().Add(-1*time.Hour)) { + app.errLogger.Printf("warn: install: UPS clock skew detected (this system's time is %s vs. UPS time %s", time.Now().Format(timeLoggingFormat), upsT.Format(timeLoggingFormat)) + } else { + app.stdLogger.Printf("install: UPS clock appears correct (%s)", upsT.Format(timeLoggingFormat)) + } + // install SSL Cert err = client.InstallSSLCert(keyP15, certPem, keyCertP15) if err != nil {