diff --git a/subsystems/provisioning/definitions.go b/subsystems/provisioning/definitions.go index c1134c6..941e73d 100644 --- a/subsystems/provisioning/definitions.go +++ b/subsystems/provisioning/definitions.go @@ -297,6 +297,12 @@ func ConfigFromJSON(defaultConf Config, jsonBytes []byte) (*Config, error) { return &conf, errw.Errorf("timeout values cannot be less than %s", time.Duration(minTimeout)) } + if conf.DeviceRebootAfterOfflineMinutes != 0 && + conf.DeviceRebootAfterOfflineMinutes < conf.OfflineTimeout || + conf.DeviceRebootAfterOfflineMinutes < conf.UserTimeout { + return &conf, errw.Errorf("device_reboot_after_offline_minutes cannot be less than offline_timeout or user_timeout") + } + return &conf, nil } @@ -370,6 +376,10 @@ type Config struct { // If set, will explicitly enable or disable power save for all wifi connections managed by NetworkManager. WifiPowerSave *bool `json:"wifi_power_save"` + + // If set, will reboot the device after it has been offline for this duration + // 0, default, will disable this feature. + DeviceRebootAfterOfflineMinutes Timeout `json:"device_reboot_after_offline_minutes"` } // Timeout allows parsing golang-style durations (1h20m30s) OR seconds-as-float from/to json. @@ -386,7 +396,7 @@ func (t *Timeout) UnmarshalJSON(b []byte) error { } switch value := v.(type) { case float64: - *t = Timeout(value * float64(time.Second)) + *t = Timeout(value * float64(time.Minute)) return nil case string: tmp, err := time.ParseDuration(value) diff --git a/subsystems/provisioning/networkmanager.go b/subsystems/provisioning/networkmanager.go index 9e0c297..11d791f 100644 --- a/subsystems/provisioning/networkmanager.go +++ b/subsystems/provisioning/networkmanager.go @@ -4,6 +4,7 @@ import ( "context" "errors" "os" + "os/exec" "reflect" "sort" "time" @@ -737,6 +738,21 @@ func (w *Provisioning) mainLoop(ctx context.Context) { } } + offlineRebootTimeout := w.cfg.DeviceRebootAfterOfflineMinutes > 0 && + lastConnectivity.Before(now.Add(time.Duration(w.cfg.DeviceRebootAfterOfflineMinutes)*-1)) + if offlineRebootTimeout { + w.logger.Infof("device has been offline for more than %s minutes, rebooting", w.cfg.DeviceRebootAfterOfflineMinutes) + cmd := exec.Command("systemctl", "reboot") + output, err := cmd.CombinedOutput() + if err != nil { + w.logger.Error(errw.Wrapf(err, "running 'systemctl reboot' %s", output)) + } + if !w.mainLoopHealth.Sleep(ctx, time.Minute*5) { + return + } + w.logger.Errorf("failed to reboot after %s time", time.Minute*5) + } + hitOfflineTimeout := lastConnectivity.Before(now.Add(time.Duration(w.cfg.OfflineTimeout)*-1)) && pModeChange.Before(now.Add(time.Duration(w.cfg.OfflineTimeout)*-1)) // not in provisioning mode, so start it if not configured (/etc/viam.json)