diff --git a/subsystems/provisioning/definitions.go b/subsystems/provisioning/definitions.go index 4141f72..152d0d2 100644 --- a/subsystems/provisioning/definitions.go +++ b/subsystems/provisioning/definitions.go @@ -29,9 +29,13 @@ const ( ConnCheckFilepath = "/etc/NetworkManager/conf.d/80-viam.conf" ConnCheckContents = "[connectivity]\nuri=http://packages.viam.com/check_network_status.txt\ninterval=300\n" - NetworkTypeWifi = "wifi" - NetworkTypeWired = "wired" - NetworkTypeHotspot = "hotspot" + wifiPowerSaveFilepath = "/etc/NetworkManager/conf.d/81-viam-wifi-powersave.conf" + wifiPowerSaveContentsDefault = "# This file intentionally left blank.\n" + wifiPowerSaveContentsDisable = "[connection]\n# Explicitly disable\nwifi.powersave = 2\n" + wifiPowerSaveContentsEnable = "[connection]\n# Explicitly enable\nwifi.powersave = 3\n" + NetworkTypeWifi = "wifi" + NetworkTypeWired = "wired" + NetworkTypeHotspot = "hotspot" IfNameAny = "any" @@ -365,6 +369,9 @@ type Config struct { // Computed from HotspotPrefix and Manufacturer hotspotSSID string + + // If set, will explicitly enable or disable power save for all wifi connections managed by NetworkManager. + WifiPowerSave *bool `json:"wifi_power_save"` } // Timeout allows parsing golang-style durations (1h20m30s) OR seconds-as-float from/to json. diff --git a/subsystems/provisioning/grpc.go b/subsystems/provisioning/grpc.go index d408cb1..5aee5ef 100644 --- a/subsystems/provisioning/grpc.go +++ b/subsystems/provisioning/grpc.go @@ -16,7 +16,7 @@ func (w *Provisioning) startGRPC() error { bind := PortalBindAddr + ":4772" lis, err := net.Listen("tcp", bind) if err != nil { - return errw.Wrapf(err, "error listening on: %s", bind) + return errw.Wrapf(err, "listening on: %s", bind) } w.grpcServer = grpc.NewServer(grpc.WaitForHandlers(true)) diff --git a/subsystems/provisioning/networkmanager.go b/subsystems/provisioning/networkmanager.go index 82f95f8..81683b8 100644 --- a/subsystems/provisioning/networkmanager.go +++ b/subsystems/provisioning/networkmanager.go @@ -175,13 +175,13 @@ func (w *Provisioning) StartProvisioning(ctx context.Context, inputChan chan<- u return err } if err := w.activateConnection(ctx, w.Config().HotspotInterface, w.Config().hotspotSSID); err != nil { - return errw.Wrap(err, "error starting provisioning mode hotspot") + return errw.Wrap(err, "starting provisioning mode hotspot") } // start portal with ssid list and known connections if err := w.startPortal(inputChan); err != nil { err = errors.Join(err, w.deactivateConnection(w.Config().HotspotInterface, w.Config().hotspotSSID)) - return errw.Wrap(err, "could not start web/grpc portal") + return errw.Wrap(err, "starting web/grpc portal") } w.connState.setProvisioning(true) diff --git a/subsystems/provisioning/portal.go b/subsystems/provisioning/portal.go index 996b552..086a944 100644 --- a/subsystems/provisioning/portal.go +++ b/subsystems/provisioning/portal.go @@ -35,11 +35,11 @@ func (w *Provisioning) startPortal(inputChan chan<- userInput) error { w.portalData = &portalData{input: &userInput{}, inputChan: inputChan} if err := w.startGRPC(); err != nil { - return errw.Wrap(err, "error starting GRPC service") + return errw.Wrap(err, "starting GRPC service") } if err := w.startWeb(); err != nil { - return errw.Wrap(err, "error starting web portal service") + return errw.Wrap(err, "starting web portal service") } return nil @@ -56,7 +56,7 @@ func (w *Provisioning) startWeb() error { bind := PortalBindAddr + ":80" lis, err := net.Listen("tcp", bind) if err != nil { - return errw.Wrapf(err, "error listening on: %s", bind) + return errw.Wrapf(err, "listening on: %s", bind) } w.portalData.workers.Add(1) diff --git a/subsystems/provisioning/provisioning.go b/subsystems/provisioning/provisioning.go index 7d7367f..0e21218 100644 --- a/subsystems/provisioning/provisioning.go +++ b/subsystems/provisioning/provisioning.go @@ -164,7 +164,7 @@ func (w *Provisioning) init(ctx context.Context) error { w.updateHotspotSSID(w.cfg) if err := w.writeDNSMasq(); err != nil { - return errw.Wrap(err, "error writing dnsmasq configuration") + return errw.Wrap(err, "writing dnsmasq configuration") } if err := w.testConnCheck(); err != nil { @@ -230,6 +230,10 @@ func (w *Provisioning) Start(ctx context.Context) error { } } + if err := w.writeWifiPowerSave(ctx); err != nil { + w.logger.Error(errw.Wrap(err, "applying wifi power save configuration")) + } + w.processAdditionalnetworks(ctx) if err := w.checkOnline(true); err != nil { @@ -357,7 +361,7 @@ func (w *Provisioning) processAdditionalnetworks(ctx context.Context) { for _, network := range w.cfg.Networks { _, err := w.addOrUpdateConnection(network) if err != nil { - w.logger.Error(errw.Wrapf(err, "error adding network %s", network.SSID)) + w.logger.Error(errw.Wrapf(err, "adding network %s", network.SSID)) continue } if network.Interface != "" { @@ -375,3 +379,37 @@ func (w *Provisioning) updateHotspotSSID(cfg *Config) { cfg.hotspotSSID = cfg.hotspotSSID[:32] } } + +// must be run inside dataMu lock. +func (w *Provisioning) writeWifiPowerSave(ctx context.Context) error { + contents := wifiPowerSaveContentsDefault + if w.cfg.WifiPowerSave != nil { + if *w.cfg.WifiPowerSave { + contents = wifiPowerSaveContentsEnable + } else { + contents = wifiPowerSaveContentsDisable + } + } + + isNew, err := agent.WriteFileIfNew(wifiPowerSaveFilepath, []byte(contents)) + if err != nil { + return errw.Wrap(err, "writing wifi-powersave.conf") + } + + if isNew { + w.logger.Infof("Updated %s to: %q", wifiPowerSaveFilepath, contents) + // Reload NetworkManager to apply changes + if err := w.nm.Reload(0); err != nil { + return errw.Wrap(err, "reloading NetworkManager after wifi-powersave.conf update") + } + + ssid := w.netState.ActiveSSID(w.cfg.HotspotInterface) + if w.connState.getConnected() && ssid != "" { + if err := w.activateConnection(ctx, w.cfg.HotspotInterface, ssid); err != nil { + return errw.Wrapf(err, "reactivating %s to enforce powersave setting", ssid) + } + } + } + + return nil +} diff --git a/subsystems/provisioning/scanning.go b/subsystems/provisioning/scanning.go index 9122615..c3eaa8a 100644 --- a/subsystems/provisioning/scanning.go +++ b/subsystems/provisioning/scanning.go @@ -19,7 +19,7 @@ func (w *Provisioning) networkScan(ctx context.Context) error { prevScan, err := wifiDev.GetPropertyLastScan() if err != nil { - return errw.Wrap(err, "error scanning wifi") + return errw.Wrap(err, "scanning wifi") } err = wifiDev.RequestScan() diff --git a/subsystems/provisioning/setup.go b/subsystems/provisioning/setup.go index dda0785..4fef08a 100644 --- a/subsystems/provisioning/setup.go +++ b/subsystems/provisioning/setup.go @@ -35,35 +35,35 @@ func (w *Provisioning) writeDNSMasq() error { func (w *Provisioning) testConnCheck() error { connCheckEnabled, err := w.nm.GetPropertyConnectivityCheckEnabled() if err != nil { - return errw.Wrap(err, "error getting NetworkManager connectivity check state") + return errw.Wrap(err, "getting NetworkManager connectivity check state") } if !connCheckEnabled { hasConnCheck, err := w.nm.GetPropertyConnectivityCheckAvailable() if err != nil { - return errw.Wrap(err, "error getting NetworkManager connectivity check configuration") + return errw.Wrap(err, "getting NetworkManager connectivity check configuration") } if !hasConnCheck { if err := w.writeConnCheck(); err != nil { - return (errw.Wrap(err, "error writing NetworkManager connectivity check configuration")) + return (errw.Wrap(err, "writing NetworkManager connectivity check configuration")) } if err := w.nm.Reload(0); err != nil { - return (errw.Wrap(err, "error reloading NetworkManager")) + return (errw.Wrap(err, "reloading NetworkManager")) } hasConnCheck, err = w.nm.GetPropertyConnectivityCheckAvailable() if err != nil { - return errw.Wrap(err, "error getting NetworkManager connectivity check configuration") + return errw.Wrap(err, "getting NetworkManager connectivity check configuration") } if !hasConnCheck { - return errors.New("error configuring NetworkManager connectivity check") + return errors.New("configuring NetworkManager connectivity check") } } connCheckEnabled, err = w.nm.GetPropertyConnectivityCheckEnabled() if err != nil { - return errw.Wrap(err, "error getting NetworkManager connectivity check state") + return errw.Wrap(err, "getting NetworkManager connectivity check state") } if !connCheckEnabled {