2018-09-13 01:07:57 +02:00
// +build linux
2018-05-27 23:33:41 +00:00
package service
import (
2018-12-07 00:59:54 +01:00
"github.com/mame82/P4wnP1_aloa/common_web"
"github.com/mame82/P4wnP1_aloa/netlink"
pb "github.com/mame82/P4wnP1_aloa/proto"
2018-09-11 17:43:04 +02:00
"sync"
"os/exec"
2018-12-07 00:59:54 +01:00
"github.com/mame82/P4wnP1_aloa/service/util"
2018-05-27 23:33:41 +00:00
"errors"
"fmt"
"strings"
2018-09-11 17:43:04 +02:00
"log"
2018-05-27 23:33:41 +00:00
"time"
2018-09-11 17:43:04 +02:00
"syscall"
"net"
"io/ioutil"
"os"
2018-09-07 15:50:18 +02:00
"regexp"
"strconv"
2018-05-27 23:33:41 +00:00
)
const (
2018-09-13 01:07:57 +02:00
wifi_if_name string = "wlan0"
WPA_SUPPLICANT_CONNECT_TIMEOUT = time . Second * 20
2018-09-25 19:52:18 +02:00
HOSTAPD_WAIT_AP_UP_TIMEOUT = time . Second * 8
2018-05-28 14:34:59 +00:00
)
2018-09-11 17:43:04 +02:00
func wifiCheckExternalBinaries ( ) error {
2018-09-07 15:50:18 +02:00
if ! binaryAvailable ( "wpa_supplicant" ) {
2018-09-11 17:43:04 +02:00
return errors . New ( "wpa_supplicant seems to be missing, please install it" )
2018-09-06 20:55:12 +02:00
}
2018-09-07 15:50:18 +02:00
// to create wpa_supplicant.conf
if ! binaryAvailable ( "wpa_passphrase" ) {
2018-09-11 17:43:04 +02:00
return errors . New ( "wpa_passphrase seems to be missing, please install it" )
2018-09-07 15:50:18 +02:00
}
if ! binaryAvailable ( "hostapd" ) {
2018-09-11 17:43:04 +02:00
return errors . New ( "hostapd seems to be missing, please install it" )
2018-09-07 15:50:18 +02:00
}
// for wifiScan
if ! binaryAvailable ( "iw" ) {
2018-09-11 17:43:04 +02:00
return errors . New ( "The tool 'iw' seems to be missing, please install it" )
2018-09-06 20:55:12 +02:00
}
2018-09-07 15:50:18 +02:00
2018-09-11 17:43:04 +02:00
return nil
}
2018-09-07 15:50:18 +02:00
2018-09-11 17:43:04 +02:00
type WiFiService struct {
2018-09-25 11:47:02 +02:00
RootSvc * Service
2018-09-13 01:07:57 +02:00
State * pb . WiFiState
//Settings *pb.WiFi2Settings
mutexSettings * sync . Mutex // Lock settings on change
CmdWpaSupplicant * exec . Cmd //Manages wpa-supplicant process
mutexWpaSupplicant * sync . Mutex //mutex for wpa-supplicant proc
CmdHostapd * exec . Cmd //Manages hostapd process
mutexHostapd * sync . Mutex //hostapd proc lock
IfaceName string //Name of WiFi interface
PathWpaSupplicantConf string // path to config file for wpa-supplicant
PathHostapdConf string // path to config file for hostapd
LoggerHostapd * util . TeeLogger //logger for hostapd
LoggerWpaSupplicant * util . TeeLogger //logger for WPA supplicant
2018-09-11 17:43:04 +02:00
OutMonitorWpaSupplicant * wpaSupplicantOutMonitor //Monitors wpa_supplicant output and sets signals where needed
2018-09-21 12:43:08 +02:00
OutMonitorHostapd * hostapdOutMonitor //Monitors hostapd output and sets signals where needed
2018-09-06 20:55:12 +02:00
}
2018-09-21 12:43:08 +02:00
func ( wSvc * WiFiService ) StartHostapd ( timeout time . Duration ) ( err error ) {
2018-09-11 17:43:04 +02:00
log . Printf ( "Starting hostapd for interface '%s'...\n" , wSvc . IfaceName )
2018-09-07 15:50:18 +02:00
2018-09-11 17:43:04 +02:00
wSvc . mutexHostapd . Lock ( )
defer wSvc . mutexHostapd . Unlock ( )
2018-09-07 15:50:18 +02:00
//stop hostapd if already running
2018-09-11 17:43:04 +02:00
if wSvc . CmdHostapd != nil {
2018-09-07 15:50:18 +02:00
// avoid deadlock
2018-09-11 17:43:04 +02:00
wSvc . mutexHostapd . Unlock ( )
wSvc . StopHostapd ( )
wSvc . mutexHostapd . Lock ( )
2018-09-07 15:50:18 +02:00
}
2018-09-25 20:51:25 +02:00
wSvc . CmdHostapd = exec . Command ( "/usr/sbin/hostapd" , wSvc . PathHostapdConf )
// the logger is the signal generator for OutMonitorHostapd, so we reset the signal before applying the logger
wSvc . OutMonitorHostapd . resultReceived . Reset ( )
2018-09-11 17:43:04 +02:00
wSvc . CmdHostapd . Stdout = wSvc . LoggerHostapd . LogWriter
wSvc . CmdHostapd . Stderr = wSvc . LoggerHostapd . LogWriter
err = wSvc . CmdHostapd . Start ( )
2018-09-07 15:50:18 +02:00
if err != nil {
2018-09-11 17:43:04 +02:00
wSvc . CmdHostapd . Wait ( )
2018-09-07 15:50:18 +02:00
return errors . New ( fmt . Sprintf ( "Error starting hostapd '%v'" , err ) )
}
2018-09-21 12:43:08 +02:00
//wait for result in output
apUp , errcon := wSvc . OutMonitorHostapd . WaitConnectResultOnce ( timeout )
if errcon != nil {
log . Printf ( "... hostapd reached timeout of '%v' without beeing able to bring up an Access Point\n" , timeout )
// avoid dead lock
wSvc . mutexHostapd . Unlock ( )
wSvc . StopHostapd ( )
wSvc . mutexHostapd . Lock ( )
return errors . New ( "TIMEOUT REACHED" )
}
if apUp {
//We could return success and keep wpa_supplicant running
log . Printf ( "... hostapd AP for interface '%s' started\n" , wSvc . IfaceName )
return nil
} else {
log . Println ( "... hostapd failed to bring up an Access Point, stopping ..." )
//wifiStopWpaSupplicant(nameIface)
// avoid dead lock
wSvc . mutexHostapd . Unlock ( )
wSvc . StopHostapd ( )
wSvc . mutexHostapd . Lock ( )
2018-09-25 19:52:18 +02:00
log . Println ( "... hostapd terminated" )
2018-09-21 12:43:08 +02:00
return errors . New ( "Hostapd failed to bring up Access Point" )
}
2018-09-07 15:50:18 +02:00
return nil
}
2018-09-11 17:43:04 +02:00
func ( wSvc * WiFiService ) StopHostapd ( ) ( err error ) {
eSuccess := fmt . Sprintf ( "... hostapd for interface '%s' stopped" , wSvc . IfaceName )
eCantStop := fmt . Sprintf ( "... couldn't terminate hostapd for interface '%s'" , wSvc . IfaceName )
2018-09-07 15:50:18 +02:00
2018-09-25 19:52:18 +02:00
log . Println ( "... killing hostapd" )
2018-09-11 17:43:04 +02:00
wSvc . mutexHostapd . Lock ( )
defer wSvc . mutexHostapd . Unlock ( )
2018-09-07 15:50:18 +02:00
2018-09-11 17:43:04 +02:00
if wSvc . CmdHostapd == nil {
log . Printf ( "... hostapd for interface '%s' isn't running, no need to stop it\n" , wSvc . IfaceName )
2018-09-07 15:50:18 +02:00
return nil
}
2018-09-25 20:51:25 +02:00
/ *
2018-09-11 17:43:04 +02:00
wSvc . CmdHostapd . Process . Signal ( syscall . SIGTERM )
2018-09-25 19:52:18 +02:00
time . Sleep ( time . Millisecond * 500 )
if wSvc . CmdHostapd . ProcessState == nil || ! wSvc . CmdHostapd . ProcessState . Exited ( ) {
2018-09-11 17:43:04 +02:00
wSvc . CmdHostapd . Process . Kill ( )
2018-09-25 19:52:18 +02:00
wSvc . CmdHostapd . Wait ( )
2018-09-07 15:50:18 +02:00
2018-09-25 19:52:18 +02:00
//wSvc.CmdHostapd.Process.Kill()
2018-09-11 17:43:04 +02:00
if wSvc . CmdHostapd . ProcessState . Exited ( ) {
wSvc . CmdHostapd = nil
2018-09-07 15:50:18 +02:00
log . Println ( eSuccess )
return nil
} else {
log . Println ( eCantStop )
return errors . New ( eCantStop )
}
2018-09-25 19:52:18 +02:00
2018-09-07 15:50:18 +02:00
}
2018-09-25 20:51:25 +02:00
* /
err = ProcSoftKill ( wSvc . CmdHostapd , time . Second )
if err != nil { return errors . New ( eCantStop ) }
2018-09-25 19:52:18 +02:00
2018-09-11 17:43:04 +02:00
wSvc . CmdHostapd = nil
2018-09-07 15:50:18 +02:00
log . Println ( eSuccess )
return nil
}
2018-09-06 20:55:12 +02:00
2018-09-11 17:43:04 +02:00
func ( wSvc * WiFiService ) StopWpaSupplicant ( ) ( err error ) {
eSuccess := fmt . Sprintf ( "... wpa_supplicant for interface '%s' stopped" , wSvc . IfaceName )
eCantStop := fmt . Sprintf ( "... couldn't terminate wpa_supplicant for interface '%s'" , wSvc . IfaceName )
2018-09-06 20:55:12 +02:00
2018-09-11 17:43:04 +02:00
log . Printf ( "... stop running wpa_supplicant processes for interface '%s'\n" , wSvc . IfaceName )
2018-09-06 20:55:12 +02:00
2018-09-11 17:43:04 +02:00
wSvc . mutexWpaSupplicant . Lock ( )
defer wSvc . mutexWpaSupplicant . Unlock ( )
2018-09-06 20:55:12 +02:00
2018-09-11 17:43:04 +02:00
if wSvc . CmdWpaSupplicant == nil {
log . Printf ( "... wpa_supplicant for interface '%s' wasn't running, no need to stop it\n" , wSvc . IfaceName )
2018-09-06 20:55:12 +02:00
return nil
}
2018-10-15 03:31:14 +02:00
/ *
2018-09-11 17:43:04 +02:00
log . Printf ( "... sending SIGTERM for wpa_supplicant on interface '%s' with PID\n" , wSvc . IfaceName , wSvc . CmdWpaSupplicant . Process . Pid )
wSvc . CmdWpaSupplicant . Process . Signal ( syscall . SIGTERM )
wSvc . CmdWpaSupplicant . Wait ( )
if ! wSvc . CmdWpaSupplicant . ProcessState . Exited ( ) {
log . Printf ( "... wpa_supplicant didn't react on SIGTERM for interface '%s', trying SIGKILL\n" , wSvc . IfaceName )
wSvc . CmdWpaSupplicant . Process . Kill ( )
2018-09-06 20:55:12 +02:00
2018-09-07 15:50:18 +02:00
time . Sleep ( 500 * time . Millisecond )
2018-09-11 17:43:04 +02:00
if wSvc . CmdWpaSupplicant . ProcessState . Exited ( ) {
wSvc . CmdWpaSupplicant = nil
2018-09-06 20:55:12 +02:00
log . Println ( eSuccess )
return nil
} else {
log . Println ( eCantStop )
return errors . New ( eCantStop )
}
}
2018-10-15 03:31:14 +02:00
* /
log . Printf ( "... stopping wpa_supplicant\n" , wSvc . IfaceName , wSvc . CmdWpaSupplicant . Process . Pid )
err = ProcSoftKill ( wSvc . CmdWpaSupplicant , time . Second * 2 )
if err != nil { return errors . New ( eCantStop ) }
2018-09-06 20:55:12 +02:00
2018-09-11 17:43:04 +02:00
wSvc . CmdWpaSupplicant = nil
2018-09-06 20:55:12 +02:00
log . Println ( eSuccess )
return nil
}
2018-09-11 17:43:04 +02:00
func ( wSvc * WiFiService ) StartWpaSupplicant ( timeout time . Duration ) ( err error ) {
log . Printf ( "Starting wpa_supplicant for interface '%s'...\n" , wSvc . IfaceName )
2018-09-06 20:55:12 +02:00
2018-09-11 17:43:04 +02:00
wSvc . mutexWpaSupplicant . Lock ( )
defer wSvc . mutexWpaSupplicant . Unlock ( )
2018-09-06 20:55:12 +02:00
//stop wpa_supplicant if already running
2018-09-11 17:43:04 +02:00
if wSvc . CmdWpaSupplicant != nil {
2018-09-07 15:50:18 +02:00
// avoid dead lock
2018-09-11 17:43:04 +02:00
wSvc . mutexWpaSupplicant . Unlock ( )
wSvc . StopWpaSupplicant ( )
wSvc . mutexWpaSupplicant . Lock ( )
2018-09-06 20:55:12 +02:00
}
//we monitor output of wpa_supplicant till we are connected, fail due to wrong PSK or timeout is reached
//Note: PID file creation doesn't work when not started as daemon, so we do it manually, later on
2018-09-11 17:43:04 +02:00
wSvc . CmdWpaSupplicant = exec . Command ( "/sbin/wpa_supplicant" , "-c" , wSvc . PathWpaSupplicantConf , "-i" , wSvc . IfaceName )
2018-09-25 20:51:25 +02:00
// the logger is the signal generator for OutMonitorWpaSupplicant, so we reset the signal before apllying the logger
wSvc . OutMonitorWpaSupplicant . resultReceived . Reset ( )
2018-09-11 17:43:04 +02:00
wSvc . CmdWpaSupplicant . Stdout = wSvc . LoggerWpaSupplicant . LogWriter
2018-09-06 20:55:12 +02:00
2018-09-11 17:43:04 +02:00
err = wSvc . CmdWpaSupplicant . Start ( )
2018-09-07 15:50:18 +02:00
if err != nil {
return err
}
2018-09-06 20:55:12 +02:00
2018-09-07 15:50:18 +02:00
//wait for result in output
2018-09-11 17:43:04 +02:00
connected , errcon := wSvc . OutMonitorWpaSupplicant . WaitConnectResultOnce ( timeout )
2018-09-07 15:50:18 +02:00
if errcon != nil {
2018-09-06 20:55:12 +02:00
log . Printf ( "... wpa_supplicant reached timeout of '%v' without beeing able to connect to given network\n" , timeout )
log . Println ( "... killing wpa_supplicant" )
2018-09-07 15:50:18 +02:00
// avoid dead lock
2018-09-11 17:43:04 +02:00
wSvc . mutexWpaSupplicant . Unlock ( )
wSvc . StopWpaSupplicant ( )
wSvc . mutexWpaSupplicant . Lock ( )
2018-09-06 20:55:12 +02:00
return errors . New ( "TIMEOUT REACHED" )
}
2018-09-07 15:50:18 +02:00
if connected {
//We could return success and keep wpa_supplicant running
log . Println ( "... connected to given WiFi network, wpa_supplicant running" )
return nil
} else {
//we stop wpa_supplicant and return err
log . Println ( "... seems the wrong PSK was provided for the given WiFi network, stopping wpa_supplicant ..." )
//wifiStopWpaSupplicant(nameIface)
log . Println ( "... killing wpa_supplicant" )
// avoid dead lock
2018-09-11 17:43:04 +02:00
wSvc . mutexWpaSupplicant . Unlock ( )
wSvc . StopWpaSupplicant ( )
wSvc . mutexWpaSupplicant . Lock ( )
2018-09-07 15:50:18 +02:00
return errors . New ( "Wrong PSK" )
}
2018-09-06 20:55:12 +02:00
return nil
}
2018-05-27 23:33:41 +00:00
2018-09-13 01:07:57 +02:00
func MatchGivenBBSToScanResult ( scanRes [ ] BSS , targets [ ] * pb . WiFiBSSCfg ) ( matches [ ] * pb . WiFiBSSCfg ) {
for _ , bssCfgTarget := range targets {
for _ , bssCfgScan := range scanRes {
2018-09-11 17:43:04 +02:00
if bssCfgScan . SSID == bssCfgTarget . SSID {
2018-09-13 01:07:57 +02:00
log . Printf ( "Found SSID '%s'\n" , bssCfgScan . SSID )
if len ( bssCfgTarget . PSK ) == 0 && bssCfgScan . AuthMode != WiFiAuthMode_OPEN {
log . Printf ( "No PSK provided for '%s', but authentication mode isn't OPEN. Ignoring this network ...\n" , bssCfgScan . SSID )
} else {
// SSID match, possible candidate
matches = append ( matches , bssCfgTarget )
}
2018-09-11 17:43:04 +02:00
}
}
}
return
2018-09-03 18:20:00 +02:00
}
2018-09-13 01:07:57 +02:00
func ( wSvc * WiFiService ) runStaMode ( newWifiSettings * pb . WiFiSettings ) ( err error ) {
2018-09-11 17:43:04 +02:00
if len ( newWifiSettings . Client_BSSList ) == 0 {
return errors . New ( "Error: WiFi mode set to station (STA) but no BSS configurations provided" )
}
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
//scan for provided wifi
scanres , err := WifiScan ( wSvc . IfaceName )
2018-05-27 23:33:41 +00:00
if err != nil {
2018-09-11 17:43:04 +02:00
return errors . New ( fmt . Sprintf ( "Scanning for existing WiFi networks failed: %v" , err ) )
2018-05-27 23:33:41 +00:00
}
2018-09-11 17:43:04 +02:00
matchingBssList := MatchGivenBBSToScanResult ( scanres , newWifiSettings . Client_BSSList )
if len ( matchingBssList ) == 0 {
return errors . New ( fmt . Sprintf ( "Non of the given SSIDs found during scan\n" ) )
2018-05-27 23:33:41 +00:00
}
2018-09-11 17:43:04 +02:00
// Create config for the remaining networks
confstr , err := wifiCreateWpaSupplicantConfStringList ( matchingBssList )
2018-09-13 01:07:57 +02:00
if err != nil {
return err
}
2018-09-11 17:43:04 +02:00
// store config to file
log . Printf ( "Creating wpa_supplicant configuration file at '%s'\n" , wSvc . PathWpaSupplicantConf )
err = ioutil . WriteFile ( wSvc . PathWpaSupplicantConf , [ ] byte ( confstr ) , os . ModePerm )
2018-09-13 01:07:57 +02:00
if err != nil {
return err
}
2018-09-07 15:50:18 +02:00
2018-09-11 17:43:04 +02:00
//ToDo: proper error handling, in case connection not possible
err = wSvc . StartWpaSupplicant ( WPA_SUPPLICANT_CONNECT_TIMEOUT )
2018-09-13 01:07:57 +02:00
if err != nil {
return err
}
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
return nil
}
// ToDo: Output monitor for AP-ENABLED (same approach as for wpa_supplicant)
2018-09-13 01:07:57 +02:00
func ( wSvc * WiFiService ) runAPMode ( newWifiSettings * pb . WiFiSettings ) ( err error ) {
2018-09-11 17:43:04 +02:00
//generate hostapd.conf (overwrite old one)
hostapdCreateConfigFile2 ( newWifiSettings , wSvc . PathHostapdConf )
//start hostapd
2018-09-21 12:43:08 +02:00
err = wSvc . StartHostapd ( HOSTAPD_WAIT_AP_UP_TIMEOUT )
2018-09-07 15:50:18 +02:00
if err != nil {
2018-09-21 12:43:08 +02:00
fmt . Println ( "Wait 2 seconds and retry to to start hostapd once..." )
2018-09-25 19:52:18 +02:00
time . Sleep ( 2 * time . Second )
2018-09-21 12:43:08 +02:00
err = wSvc . StartHostapd ( HOSTAPD_WAIT_AP_UP_TIMEOUT )
if err != nil {
return err
}
2018-09-07 15:50:18 +02:00
}
2018-09-06 17:05:49 +02:00
2018-09-11 17:43:04 +02:00
return nil
}
2018-05-27 23:33:41 +00:00
2018-09-13 01:07:57 +02:00
func ( wSvc * WiFiService ) DeploySettings ( newWifiSettings * pb . WiFiSettings ) ( wstate * pb . WiFiState , err error ) {
2018-09-11 17:43:04 +02:00
log . Println ( "Deploying new WiFi settings..." )
log . Printf ( "Settings: %+v\n" , newWifiSettings )
wSvc . mutexSettings . Lock ( )
defer wSvc . mutexSettings . Unlock ( )
//ToDo: Dis/Enable nexmon if needed
//stop wpa_supplicant if needed
err = wSvc . StopWpaSupplicant ( )
2018-09-13 01:07:57 +02:00
if err != nil {
return wSvc . State , err
}
2018-09-11 17:43:04 +02:00
//kill hostapd in case it is still running
err = wSvc . StopHostapd ( )
2018-09-13 01:07:57 +02:00
if err != nil {
return wSvc . State , err
}
// Note: When hostapd is killed, the WiFi interface is shut down. If the firmware is reloade (toggeling
// nexmon) the whole interface gets destroyed. Both have influence on processes depending on the network
// configuration of the WiFi interface (e.g. DHCP server / client). In order to avoid errors, we reconfigure the
// WiFi network interface after changing the WiFi state.
// We do this after setting the respective WiFi mode (STA / AP / FAILOVER), but in order to make this modes
// work, we enable the interface first - regardless of its 'enabled' state.
iface , tmperr := net . InterfaceByName ( wSvc . IfaceName )
if tmperr == nil {
netlink . NetworkLinkUp ( iface )
}
// Set proper regulatory domain
errReg := wifiSetReg ( newWifiSettings . Regulatory )
if errReg != nil {
log . Printf ( "Error setting WiFi regulatory domain '%s': %v\n" , newWifiSettings . Regulatory , err ) //we don't abort on error here
}
2018-09-11 17:43:04 +02:00
2018-10-15 03:31:14 +02:00
var triggerEvent * pb . Event = nil
2018-09-11 17:43:04 +02:00
if ! newWifiSettings . Disabled {
switch newWifiSettings . WorkingMode {
2018-09-13 01:07:57 +02:00
case pb . WiFiWorkingMode_AP :
2018-09-11 17:43:04 +02:00
err = wSvc . runAPMode ( newWifiSettings )
2018-09-26 13:32:11 +02:00
// emit Trigger event if AP is Up
if err == nil {
2018-10-15 03:31:14 +02:00
triggerEvent = ConstructEventTrigger ( common_web . TRIGGER_EVT_TYPE_WIFI_AP_STARTED )
2018-09-26 13:32:11 +02:00
}
2018-09-13 01:07:57 +02:00
case pb . WiFiWorkingMode_STA , pb . WiFiWorkingMode_STA_FAILOVER_AP :
2018-09-11 17:43:04 +02:00
errSta := wSvc . runStaMode ( newWifiSettings )
2018-09-26 13:32:11 +02:00
if errSta == nil {
2018-10-15 03:31:14 +02:00
triggerEvent = ConstructEventTrigger ( common_web . TRIGGER_EVT_TYPE_WIFI_CONNECTED_AS_STA )
} else {
2018-09-11 17:43:04 +02:00
//in failover mode, we try to enable AP first
2018-09-13 01:07:57 +02:00
if newWifiSettings . WorkingMode == pb . WiFiWorkingMode_STA_FAILOVER_AP {
2018-09-11 17:43:04 +02:00
log . Println ( errSta )
log . Printf ( "Trying to fail over to Access Point Mode..." )
err = wSvc . runAPMode ( newWifiSettings )
2018-09-26 13:32:11 +02:00
if err == nil {
2018-10-15 03:31:14 +02:00
triggerEvent = ConstructEventTrigger ( common_web . TRIGGER_EVT_TYPE_WIFI_AP_STARTED )
2018-09-26 13:32:11 +02:00
}
2018-09-11 17:43:04 +02:00
} else {
err = errSta
}
2018-09-07 15:50:18 +02:00
}
2018-09-11 17:43:04 +02:00
default :
// None allowed working mode, so we leave wSvc as UNKNOWN
err = errors . New ( "Unknown working mode" )
2018-05-27 23:33:41 +00:00
}
2018-09-11 17:43:04 +02:00
}
2018-09-25 20:51:25 +02:00
//fmt.Println("HOSTAPD ERR CHECK\n==============\n-->", err)
2018-09-25 19:52:18 +02:00
if err == nil {
log . Printf ( "... WiFi settings deployed successfully\n" )
} else {
log . Printf ( "... deploying WiFi settings failed: %s\n" , err . Error ( ) )
return wSvc . State , err
}
2018-05-27 23:33:41 +00:00
2018-09-13 01:07:57 +02:00
// At this point, we reestablish the interface settings
2018-09-25 11:47:02 +02:00
//ReInitNetworkInterface(wSvc.IfaceName)
if nim , err := wSvc . RootSvc . SubSysNetwork . GetManagedInterface ( wSvc . IfaceName ) ; err == nil {
nim . ReDeploy ( )
}
2018-09-13 01:07:57 +02:00
2018-09-25 19:52:18 +02:00
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
// update settings (wSvc is updated by runAPMode/runStaMode)
2018-09-13 01:07:57 +02:00
wSvc . State . CurrentSettings = newWifiSettings
2018-09-03 18:20:00 +02:00
2018-09-13 19:49:47 +02:00
// update rest of state
2018-09-13 21:52:27 +02:00
if serr := wSvc . UpdateStateFromIw ( ) ; serr != nil {
log . Println ( "Couldn't update internal WiFi state:" , serr )
2018-09-13 19:49:47 +02:00
}
2018-10-15 03:31:14 +02:00
// Fire the event after everything is done, especially after redeployment of the network interface settings
// to allow an ActionTrigger which deploys another ethernet settings template (without changing the settings
// in parallel)
// ToDo: check if it makes sense to lock the methods responsible for deploying ethernet interface settings and WifI settings (both long-running)
if triggerEvent != nil {
wSvc . RootSvc . SubSysEvent . Emit ( triggerEvent )
}
2018-09-11 17:43:04 +02:00
return wSvc . State , nil
2018-05-27 23:33:41 +00:00
}
2018-09-25 11:47:02 +02:00
func NewWifiService ( rootSvc * Service ) ( res * WiFiService ) {
2018-09-11 17:43:04 +02:00
ifName := wifi_if_name
err := wifiCheckExternalBinaries ( )
2018-09-13 01:07:57 +02:00
if err != nil {
panic ( err )
}
2018-09-07 16:30:25 +02:00
2018-09-11 17:43:04 +02:00
//Check interface existence
2018-10-27 00:49:14 +02:00
if exists := CheckInterfaceExistence ( ifName ) ; ! exists {
2018-09-11 17:43:04 +02:00
panic ( errors . New ( fmt . Sprintf ( "WiFi interface '%s' not present" ) ) )
}
2018-09-07 16:30:25 +02:00
2018-09-11 17:43:04 +02:00
res = & WiFiService {
2018-09-25 11:47:02 +02:00
RootSvc : rootSvc ,
2018-09-11 17:43:04 +02:00
mutexSettings : & sync . Mutex { } ,
CmdWpaSupplicant : nil ,
mutexWpaSupplicant : & sync . Mutex { } ,
CmdHostapd : nil ,
mutexHostapd : & sync . Mutex { } ,
IfaceName : ifName ,
PathWpaSupplicantConf : fmt . Sprintf ( "/tmp/wpa_supplicant_%s.conf" , ifName ) ,
PathHostapdConf : fmt . Sprintf ( "/tmp/hostapd_%s.conf" , ifName ) ,
2018-09-07 16:30:25 +02:00
}
2018-09-11 17:43:04 +02:00
res . OutMonitorWpaSupplicant = NewWpaSupplicantOutMonitor ( )
2018-09-21 12:43:08 +02:00
res . OutMonitorHostapd = NewHostapdOutMonitor ( )
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
res . LoggerHostapd = util . NewTeeLogger ( true )
res . LoggerHostapd . SetPrefix ( "hostapd: " )
2018-09-21 12:43:08 +02:00
res . LoggerHostapd . AddOutput ( res . OutMonitorHostapd )
2018-09-11 17:43:04 +02:00
res . LoggerWpaSupplicant = util . NewTeeLogger ( true )
res . LoggerWpaSupplicant . SetPrefix ( "wpa_supplicant: " )
res . LoggerWpaSupplicant . AddOutput ( res . OutMonitorWpaSupplicant ) // add watcher too tee'ed output writers
// Initial settings and state on service start
2018-09-13 01:07:57 +02:00
res . State = & pb . WiFiState {
2018-09-13 19:49:47 +02:00
Mode : pb . WiFiStateMode_STA_NOT_CONNECTED ,
Channel : 0 ,
Ssid : "" ,
2018-09-11 17:43:04 +02:00
}
2018-09-13 01:07:57 +02:00
res . State . CurrentSettings = & pb . WiFiSettings {
Disabled : false ,
WorkingMode : pb . WiFiWorkingMode_AP ,
Client_BSSList : [ ] * pb . WiFiBSSCfg { & pb . WiFiBSSCfg { SSID : "" , PSK : "" } } ,
Ap_BSS : & pb . WiFiBSSCfg { } ,
}
2018-09-11 17:43:04 +02:00
return res
2018-05-27 23:33:41 +00:00
}
2018-09-21 12:43:08 +02:00
// io.Writer firing a signal if predefined output arrives
type hostapdOutMonitor struct {
resultReceived * util . Signal
result bool
* sync . Mutex
}
func ( m * hostapdOutMonitor ) Write ( p [ ] byte ) ( n int , err error ) {
// if result already received, the write could exit (early out)
if m . resultReceived . IsSet ( ) {
2020-02-06 00:20:54 +01:00
// fix endless loop of writing 'CTRL-EVENT-TERMINATING'
return len ( p ) , nil
2018-09-21 12:43:08 +02:00
}
// check if buffer contains relevant strings (assume write is called line wise by the hosted process
// otherwise we'd need to utilize an io.Reader
line := string ( p )
switch {
2018-09-25 19:52:18 +02:00
case strings . Contains ( line , "AP-DISABLED" ) :
2018-09-21 12:43:08 +02:00
log . Printf ( "Starting Access Point failed\n" )
m . Lock ( )
defer m . Unlock ( )
m . result = false
m . resultReceived . Set ( )
case strings . Contains ( line , "AP-ENABLED" ) :
log . Printf ( "Access point is up\n" )
m . Lock ( )
defer m . Unlock ( )
m . result = true
m . resultReceived . Set ( )
}
return len ( p ) , nil
}
func ( m * hostapdOutMonitor ) WaitConnectResultOnce ( timeout time . Duration ) ( connected bool , err error ) {
err = m . resultReceived . WaitTimeout ( timeout )
if err != nil {
return false , errors . New ( "Couldn't retrieve hostapd connection state before timeout" )
}
m . Lock ( )
defer m . Unlock ( )
connected = m . result
m . resultReceived . Reset ( ) //Disable result received, for next use
return
}
func NewHostapdOutMonitor ( ) * hostapdOutMonitor {
return & hostapdOutMonitor {
resultReceived : util . NewSignal ( false , false ) ,
Mutex : & sync . Mutex { } ,
2018-09-25 19:52:18 +02:00
result : false ,
2018-09-21 12:43:08 +02:00
}
}
2018-09-11 17:43:04 +02:00
type wpaSupplicantOutMonitor struct {
resultReceived * util . Signal
result bool
* sync . Mutex
2018-05-27 23:33:41 +00:00
}
2018-09-11 17:43:04 +02:00
func ( m * wpaSupplicantOutMonitor ) Write ( p [ ] byte ) ( n int , err error ) {
// if result already received, the write could exit (early out)
if m . resultReceived . IsSet ( ) {
2020-02-06 00:20:54 +01:00
return len ( p ) , nil
2018-05-27 23:33:41 +00:00
}
2018-09-11 17:43:04 +02:00
// check if buffer contains relevant strings (assume write is called line wise by the hosted process
// otherwise we'd need to utilize an io.Reader
line := string ( p )
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
switch {
case strings . Contains ( line , "WRONG_KEY" ) :
log . Printf ( "Seems the provided PSK doesn't match\n" )
m . Lock ( )
defer m . Unlock ( )
m . result = false
m . resultReceived . Set ( )
case strings . Contains ( line , "CTRL-EVENT-CONNECTED" ) :
2018-09-13 19:49:47 +02:00
// CTRL-EVENT-CONNECTED - Connection to 4e:66:41:a0:5b:35 completed
2018-09-11 17:43:04 +02:00
log . Printf ( "Connected to target network\n" )
m . Lock ( )
defer m . Unlock ( )
m . result = true
m . resultReceived . Set ( )
}
return len ( p ) , nil
}
func ( m * wpaSupplicantOutMonitor ) WaitConnectResultOnce ( timeout time . Duration ) ( connected bool , err error ) {
err = m . resultReceived . WaitTimeout ( timeout )
2018-09-07 15:50:18 +02:00
if err != nil {
2018-09-11 17:43:04 +02:00
return false , errors . New ( "Couldn't retrieve wpa_supplicant connection state before timeout" )
2018-09-07 15:50:18 +02:00
}
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
m . Lock ( )
defer m . Unlock ( )
connected = m . result
m . resultReceived . Reset ( ) //Disable result received, for next use
return
}
func NewWpaSupplicantOutMonitor ( ) * wpaSupplicantOutMonitor {
return & wpaSupplicantOutMonitor {
resultReceived : util . NewSignal ( false , false ) ,
Mutex : & sync . Mutex { } ,
}
}
type WiFiAuthMode int
const (
WiFiAuthMode_OPEN WiFiAuthMode = iota
//WiFiAuthMode_WEP
WiFiAuthMode_WPA_PSK
WiFiAuthMode_WPA2_PSK
WiFiAuthMode_UNSUPPORTED
)
type BSS struct {
SSID string
BSSID net . HardwareAddr
Frequency int
BeaconInterval time . Duration //carefull, on IE level beacon interval isn't measured in milliseconds
AuthMode WiFiAuthMode
Signal float32 //Signal strength in dBm
2018-05-27 23:33:41 +00:00
}
func WifiScan ( ifName string ) ( result [ ] BSS , err error ) {
proc := exec . Command ( "/sbin/iw" , ifName , "scan" )
res , err := proc . CombinedOutput ( )
if err != nil {
2018-09-13 01:07:57 +02:00
return nil , errors . New ( fmt . Sprintf ( "Error running scan: '%s'\niw output: %s" , err , res ) )
2018-05-27 23:33:41 +00:00
}
result , err = ParseIwScan ( string ( res ) )
return
}
2018-09-13 01:07:57 +02:00
func wifiCreateWpaSupplicantConfStringList ( bsslist [ ] * pb . WiFiBSSCfg ) ( config string , err error ) {
2018-09-11 17:43:04 +02:00
// if a PSK is provided, we assume it is needed, otherwise we assume OPEN AUTHENTICATION
2018-09-13 01:07:57 +02:00
for _ , bss := range bsslist {
2018-09-11 17:43:04 +02:00
ssid := bss . SSID
psk := bss . PSK
if len ( psk ) > 0 {
fmt . Println ( "Connecting WiFi with PSK" )
proc := exec . Command ( "/usr/bin/wpa_passphrase" , ssid , psk )
cres , err := proc . CombinedOutput ( )
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
if err != nil {
return "" , errors . New ( fmt . Sprintf ( "Error craeting wpa_supplicant.conf for SSID '%s' with PSK '%s': %s" , ssid , psk , string ( cres ) ) )
}
config += string ( cres )
} else {
fmt . Println ( "Connecting WiFi with OPEN AUTH" )
config += fmt . Sprintf (
` network = {
2018-09-06 17:05:49 +02:00
ssid = "%s"
key_mgmt = NONE
2018-09-11 17:43:04 +02:00
}
` , ssid )
}
2018-05-27 23:33:41 +00:00
}
2018-09-06 17:05:49 +02:00
2018-05-28 14:34:59 +00:00
config = "ctrl_interface=/run/wpa_supplicant\n" + config
2018-05-27 23:33:41 +00:00
return
}
2018-09-13 01:07:57 +02:00
func wifiCreateHostapdConfString ( ws * pb . WiFiSettings ) ( config string , err error ) {
if ws . WorkingMode != pb . WiFiWorkingMode_AP && ws . WorkingMode != pb . WiFiWorkingMode_STA_FAILOVER_AP {
2018-09-11 17:43:04 +02:00
return "" , errors . New ( "Couldn't create hostapd configuration, the settings don't include an AP" )
2018-05-27 23:33:41 +00:00
}
2018-09-11 17:43:04 +02:00
if ws . Ap_BSS == nil {
2018-05-27 23:33:41 +00:00
return "" , errors . New ( "WiFiSettings don't contain a BSS configuration for an AP" )
}
config = fmt . Sprintf ( "interface=%s\n" , wifi_if_name )
2018-09-07 15:50:18 +02:00
config += fmt . Sprintf ( "driver=nl80211\n" ) //netlink capable driver
config += fmt . Sprintf ( "hw_mode=g\n" ) //Use 2.4GHz band
config += fmt . Sprintf ( "ieee80211n=1\n" ) //Enable 802.111n
config += fmt . Sprintf ( "wmm_enabled=1\n" ) //Enable WMM
2018-05-27 23:33:41 +00:00
config += fmt . Sprintf ( "ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]\n" ) // 40MHz channels with 20ns guard interval
2018-09-07 15:50:18 +02:00
config += fmt . Sprintf ( "macaddr_acl=0\n" ) //Accept all MAC addresses
2018-05-27 23:33:41 +00:00
2018-09-11 17:43:04 +02:00
config += fmt . Sprintf ( "ssid=%s\n" , ws . Ap_BSS . SSID )
config += fmt . Sprintf ( "channel=%d\n" , ws . Channel )
2018-05-27 23:33:41 +00:00
2018-09-13 01:07:57 +02:00
if ws . AuthMode == pb . WiFiAuthMode_WPA2_PSK {
2018-05-27 23:33:41 +00:00
config += fmt . Sprintf ( "auth_algs=1\n" ) //Use WPA authentication
2018-09-07 15:50:18 +02:00
config += fmt . Sprintf ( "wpa=2\n" ) //Use WPA2
2018-05-27 23:33:41 +00:00
//ToDo: check if PSK could be provided encrypted
config += fmt . Sprintf ( "wpa_key_mgmt=WPA-PSK\n" ) //Use a pre-shared key
2018-09-11 17:43:04 +02:00
config += fmt . Sprintf ( "wpa_passphrase=%s\n" , ws . Ap_BSS . PSK ) //Set PSK
2018-09-13 01:07:57 +02:00
config += fmt . Sprintf ( "rsn_pairwise=CCMP\n" ) //Use Use AES, instead of TKIP
2018-05-27 23:33:41 +00:00
} else {
config += fmt . Sprintf ( "auth_algs=3\n" ) //Both, open and shared auth
}
2018-09-11 17:43:04 +02:00
if ws . HideSsid {
2018-05-27 23:33:41 +00:00
config += fmt . Sprintf ( "ignore_broadcast_ssid=2\n" ) //Require clients to know the SSID
} else {
config += fmt . Sprintf ( "ignore_broadcast_ssid=0\n" ) //Send beacons + probes
}
return
}
2018-09-13 01:07:57 +02:00
func hostapdCreateConfigFile2 ( s * pb . WiFiSettings , filename string ) ( err error ) {
2018-05-27 23:33:41 +00:00
log . Printf ( "Creating hostapd configuration file at '%s'\n" , filename )
2018-09-13 01:07:57 +02:00
fileContent , err := wifiCreateHostapdConfString ( s )
2018-05-28 14:34:59 +00:00
if err != nil {
2018-09-07 15:50:18 +02:00
return
2018-05-27 23:33:41 +00:00
}
2018-09-07 15:50:18 +02:00
err = ioutil . WriteFile ( filename , [ ] byte ( fileContent ) , os . ModePerm )
return
2018-05-28 17:19:17 +00:00
}
//ToDo: Create netlink based implementation (not relying on 'iw'): low priority
func ParseIwScan ( scanresult string ) ( bsslist [ ] BSS , err error ) {
//fmt.Printf("Parsing:\n%s\n", scanresult)
//split into BSS sections
rp := regexp . MustCompile ( "(?msU)^BSS.*" )
strBSSList := rp . Split ( scanresult , - 1 )
if len ( strBSSList ) < 1 {
return nil , errors . New ( "Error parsing iw scan result" ) //splitting should always result in one element at least
}
bsslist = [ ] BSS { }
rp_bssid := regexp . MustCompile ( "[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}" )
rp_freq := regexp . MustCompile ( "(?m)freq:\\s*([0-9]{4})" )
rp_ssid := regexp . MustCompile ( "(?m)SSID:\\s*(.*)\n" )
rp_beacon_intv := regexp . MustCompile ( "(?m)beacon interval:\\s*([0-9]*)TU" )
rp_WEP := regexp . MustCompile ( "(?m)WEP:" )
rp_WPA := regexp . MustCompile ( "(?m)WPA:" )
rp_WPA2 := regexp . MustCompile ( "(?m)RSN:" )
rp_PSK := regexp . MustCompile ( "(?m)Authentication suites: PSK" ) //ToDo: check if PSK occurs under respective IE (RSN or WPA, when either is chosen)
//signal: -75.00 dBm
rp_signal := regexp . MustCompile ( "(?m)signal:\\s*(-?[0-9]*\\.[0-9]*)" )
for idx , strBSS := range strBSSList [ 1 : ] {
currentBSS := BSS { }
//fmt.Printf("BSS %d\n================\n%s\n", idx, strBSS)
fmt . Printf ( "BSS %d\n================\n" , idx )
//BSSID (should be in first line)
strBSSID := rp_bssid . FindString ( strBSS )
fmt . Printf ( "BSSID: %s\n" , strBSSID )
currentBSS . BSSID , err = net . ParseMAC ( strBSSID )
2018-09-07 15:50:18 +02:00
if err != nil {
return nil , err
}
2018-05-28 17:19:17 +00:00
//freq
strFreq_sub := rp_freq . FindStringSubmatch ( strBSS )
strFreq := "0"
2018-09-07 15:50:18 +02:00
if len ( strFreq_sub ) > 1 {
strFreq = strFreq_sub [ 1 ]
}
2018-05-28 17:19:17 +00:00
fmt . Printf ( "Freq: %s\n" , strFreq )
2018-09-07 15:50:18 +02:00
tmpI64 , err := strconv . ParseInt ( strFreq , 10 , 32 )
if err != nil {
return nil , err
}
2018-05-28 17:19:17 +00:00
currentBSS . Frequency = int ( tmpI64 )
//ssid
strSsid_sub := rp_ssid . FindStringSubmatch ( strBSS )
strSSID := ""
2018-09-07 15:50:18 +02:00
if len ( strSsid_sub ) > 1 {
strSSID = strSsid_sub [ 1 ]
}
2018-05-28 17:19:17 +00:00
fmt . Printf ( "SSID: '%s'\n" , strSSID )
currentBSS . SSID = strSSID
//beacon interval
strBI_sub := rp_beacon_intv . FindStringSubmatch ( strBSS )
strBI := "100"
2018-09-07 15:50:18 +02:00
if len ( strBI_sub ) > 1 {
strBI = strBI_sub [ 1 ]
}
2018-05-28 17:19:17 +00:00
fmt . Printf ( "Beacon Interval: %s\n" , strBI )
2018-09-07 15:50:18 +02:00
tmpI64 , err = strconv . ParseInt ( strBI , 10 , 32 )
if err != nil {
return nil , err
}
currentBSS . BeaconInterval = time . Microsecond * time . Duration ( tmpI64 * 1024 ) //1TU = 1024 microseconds (not 1000)
2018-05-28 17:19:17 +00:00
//auth type
//assume OPEN
//if "WEP: is present assume UNSUPPORTED
//if "WPA:" is present assume WPA (overwrite WEP/UNSUPPORTED)
//if "RSN:" is present assume WPA2 (overwrite WPA/UNSUPPORTED)
//in case of WPA/WPA2 check for presence of "Authentication suites: PSK" to assure PSK support, otherwise assume unsupported (no EAP/CHAP support for now)
currentBSS . AuthMode = WiFiAuthMode_OPEN
2018-09-07 15:50:18 +02:00
if rp_WEP . MatchString ( strBSS ) {
currentBSS . AuthMode = WiFiAuthMode_UNSUPPORTED
}
if rp_WPA . MatchString ( strBSS ) {
currentBSS . AuthMode = WiFiAuthMode_WPA_PSK
}
if rp_WPA2 . MatchString ( strBSS ) {
currentBSS . AuthMode = WiFiAuthMode_WPA2_PSK
}
2018-05-28 17:19:17 +00:00
if currentBSS . AuthMode == WiFiAuthMode_WPA_PSK || currentBSS . AuthMode == WiFiAuthMode_WPA2_PSK {
2018-09-07 15:50:18 +02:00
if ! rp_PSK . MatchString ( strBSS ) {
currentBSS . AuthMode = WiFiAuthMode_UNSUPPORTED
}
2018-05-28 17:19:17 +00:00
}
switch currentBSS . AuthMode {
case WiFiAuthMode_UNSUPPORTED :
fmt . Println ( "AuthMode: UNSUPPORTED" )
case WiFiAuthMode_OPEN :
fmt . Println ( "AuthMode: OPEN" )
case WiFiAuthMode_WPA_PSK :
fmt . Println ( "AuthMode: WPA PSK" )
case WiFiAuthMode_WPA2_PSK :
fmt . Println ( "AuthMode: WPA2 PSK" )
}
//signal
strSignal_sub := rp_signal . FindStringSubmatch ( strBSS )
strSignal := "0.0"
2018-09-07 15:50:18 +02:00
if len ( strSignal_sub ) > 1 {
strSignal = strSignal_sub [ 1 ]
}
2018-05-28 17:19:17 +00:00
tmpFloat , err := strconv . ParseFloat ( strSignal , 32 )
2018-09-07 15:50:18 +02:00
if err != nil {
return nil , err
}
2018-05-28 17:19:17 +00:00
currentBSS . Signal = float32 ( tmpFloat )
fmt . Printf ( "Signal: %s dBm\n" , strSignal )
bsslist = append ( bsslist , currentBSS )
}
2018-09-07 15:50:18 +02:00
return bsslist , nil
2018-09-13 19:49:47 +02:00
} //ToDo: Create netlink based implementation (not relying on 'iw'): low priority
2018-09-13 21:52:27 +02:00
func ( wsvc WiFiService ) UpdateStateFromIw ( ) ( err error ) {
2018-09-13 19:49:47 +02:00
proc := exec . Command ( "/sbin/iw" , "dev" , wsvc . IfaceName , "info" )
res , err := proc . CombinedOutput ( )
if err != nil {
2018-09-13 21:52:27 +02:00
return errors . New ( fmt . Sprintf ( "Error fetching wifi info: '%s'\niw output: %s" , err , res ) )
2018-09-13 19:49:47 +02:00
}
/ *
AP
--
Interface wlan0
ifindex 2
wdev 0x1
addr b8 : 27 : eb : 71 : bb : bc
ssid \ xf0 \ x9f \ x92 \ xa5 \ xf0 \ x9f \ x96 \ xa5 \ xf0 \ x9f \ x92 \ xa5 \ xe2 \ x93 \ x85 \ xe2 \ x9e \ x83 \ xe2 \ x93 \ x8c \ xe2 \ x93 \ x83 \ xf0 \ x9f \ x85 \ x9f \ xe2 \ x9d \ xb6
type AP
wiphy 0
channel 2 ( 2417 MHz ) , width : 20 MHz , center1 : 2417 MHz
txpower 31.00 dBm
NOT CONNECTED
-- -- -- -- -- -- -
Interface wlan0
ifindex 2
wdev 0x1
addr b8 : 27 : eb : 71 : bb : bc
type managed
wiphy 0
channel 2 ( 2417 MHz ) , width : 20 MHz , center1 : 2417 MHz
txpower 31.00 dBm
CONNECTED
-- -- -- -- -- -
Interface wlan0
ifindex 2
wdev 0x1
addr b8 : 27 : eb : 71 : bb : bc
ssid WLAN - 579086
type managed
wiphy 0
channel 6 ( 2437 MHz ) , width : 20 MHz , center1 : 2437 MHz
txpower 31.00 dBm
* /
2018-09-24 15:41:09 +02:00
output := string ( res )
2018-09-13 19:49:47 +02:00
//split into BSS sections
reSsid := regexp . MustCompile ( "(?m)ssid (.*)\n" )
reMode := regexp . MustCompile ( "(?m)type (.*)\n" )
reChannel := regexp . MustCompile ( "(?m)channel ([0-9]+) .*\n" )
strSsid_sub := reSsid . FindStringSubmatch ( output )
strSsid := ""
if len ( strSsid_sub ) > 1 {
unSsid , uerr := strconv . Unquote ( fmt . Sprintf ( "\"%s\"" , strSsid_sub [ 1 ] ) )
if uerr == nil {
strSsid = unSsid
} else {
fmt . Println ( "Unquote error" , uerr )
}
}
// fmt.Printf("SSID: %s\n", strSsid)
strChannel_sub := reChannel . FindStringSubmatch ( output )
strChannel := "0"
if len ( strChannel_sub ) > 1 {
strChannel = strChannel_sub [ 1 ]
}
// fmt.Printf("Channel: %s\n", strChannel)
strMode_sub := reMode . FindStringSubmatch ( output )
strMode := "0"
if len ( strMode_sub ) > 1 {
strMode = strMode_sub [ 1 ]
}
switch strings . ToLower ( strMode ) {
case "ap" :
2018-09-13 21:52:27 +02:00
wsvc . State . Mode = pb . WiFiStateMode_AP_UP
2018-09-13 19:49:47 +02:00
default :
2018-09-13 21:52:27 +02:00
wsvc . State . Mode = pb . WiFiStateMode_STA_NOT_CONNECTED
2018-09-13 19:49:47 +02:00
}
if len ( strSsid ) > 0 {
2018-09-13 21:52:27 +02:00
wsvc . State . Ssid = strSsid
if wsvc . State . Mode == pb . WiFiStateMode_STA_NOT_CONNECTED {
2018-09-14 15:31:53 +02:00
wsvc . State . Mode = pb . WiFiStateMode_STA_CONNECTED // when a SSID is present, the wifi interface is connected to an AP
2018-09-13 19:49:47 +02:00
}
}
intCh := 0
intCh , _ = strconv . Atoi ( strChannel )
2018-09-13 21:52:27 +02:00
wsvc . State . Channel = uint32 ( intCh )
2018-09-13 19:49:47 +02:00
2018-09-13 21:52:27 +02:00
return nil
2018-09-07 15:50:18 +02:00
}
2018-09-13 01:07:57 +02:00
func wifiSetReg ( reg string ) ( err error ) {
if len ( reg ) == 0 {
reg = "US" //default
log . Printf ( "No ISO/IEC 3166-1 alpha2 regulatory domain provided, defaulting to '%s'\n" , reg )
}
reg = strings . ToUpper ( reg )
proc := exec . Command ( "/sbin/iw" , "reg" , "set" , reg )
err = proc . Run ( )
if err != nil {
return err
}
log . Printf ( "Notified kernel to use ISO/IEC 3166-1 alpha2 regulatory domain '%s'\n" , reg )
return nil
}
2018-09-25 20:51:25 +02:00
func ProcSoftKill ( cmd * exec . Cmd , timeToKill time . Duration ) ( err error ) {
if cmd . Process == nil {
// process already dead
return nil
}
//send SIGTERM for softkill
cmd . Process . Signal ( syscall . SIGTERM )
//we wait for process to exit or issue SIGKILL after timeout
hasExitted := make ( chan interface { } , 0 )
go func ( ) {
cmd . Process . Wait ( ) // even if waite ends with error, the process should have died
//fmt.Println("WAIT RES ENDED")
close ( hasExitted )
} ( )
select {
case <- hasExitted :
//fmt.Println("HAS EXITED")
return nil
case <- time . After ( timeToKill ) :
//timeout exceeded, send SIGKILL
//fmt.Println("TIMEOUT, SENDING SIGKILL")
cmd . Process . Kill ( )
return nil
}
}