diff --git a/AccessPoint.go b/AccessPoint.go index 0d6341e..9381c6d 100644 --- a/AccessPoint.go +++ b/AccessPoint.go @@ -1,18 +1,59 @@ package gonetworkmanager import ( + "encoding/json" + "github.com/godbus/dbus" ) const ( AccessPointInterface = NetworkManagerInterface + ".AccessPoint" - AccessPointPropertySSID = AccessPointInterface + ".Ssid" + AccessPointPropertyFlags = AccessPointInterface + ".Flags" + AccessPointPropertyWPAFlags = AccessPointInterface + ".WpaFlags" + AccessPointPropertyRSNFlags = AccessPointInterface + ".RsnFlags" + AccessPointPropertySSID = AccessPointInterface + ".Ssid" + AccessPointPropertyFrequency = AccessPointInterface + ".Frequency" + AccessPointPropertyHWAddress = AccessPointInterface + ".HwAddress" + AccessPointPropertyMode = AccessPointInterface + ".Mode" + AccessPointPropertyMaxBitrate = AccessPointInterface + ".MaxBitrate" + AccessPointPropertyStrength = AccessPointInterface + ".Strength" ) type AccessPoint interface { + // GetFlags gets flags describing the capabilities of the access point. + GetFlags() uint32 + + // GetWPAFlags gets flags describing the access point's capabilities + // according to WPA (Wifi Protected Access). + GetWPAFlags() uint32 + + // GetRSNFlags gets flags describing the access point's capabilities + // according to the RSN (Robust Secure Network) protocol. + GetRSNFlags() uint32 + // GetSSID returns the Service Set Identifier identifying the access point. GetSSID() string + + // GetFrequency gets the radio channel frequency in use by the access point, + // in MHz. + GetFrequency() uint32 + + // GetHWAddress gets the hardware address (BSSID) of the access point. + GetHWAddress() string + + // GetMode describes the operating mode of the access point. + GetMode() Nm80211Mode + + // GetMaxBitrate gets the maximum bitrate this access point is capable of, in + // kilobits/second (Kb/s). + GetMaxBitrate() uint32 + + // GetStrength gets the current signal quality of the access point, in + // percent. + GetStrength() uint8 + + MarshalJSON() ([]byte, error) } func NewAccessPoint(objectPath dbus.ObjectPath) (AccessPoint, error) { @@ -24,6 +65,52 @@ type accessPoint struct { dbusBase } +func (a *accessPoint) GetFlags() uint32 { + return a.getUint32Property(AccessPointPropertyFlags) +} + +func (a *accessPoint) GetWPAFlags() uint32 { + return a.getUint32Property(AccessPointPropertyWPAFlags) +} + +func (a *accessPoint) GetRSNFlags() uint32 { + return a.getUint32Property(AccessPointPropertyRSNFlags) +} + func (a *accessPoint) GetSSID() string { return string(a.getSliceByteProperty(AccessPointPropertySSID)) } + +func (a *accessPoint) GetFrequency() uint32 { + return a.getUint32Property(AccessPointPropertyFrequency) +} + +func (a *accessPoint) GetHWAddress() string { + return a.getStringProperty(AccessPointPropertyHWAddress) +} + +func (a *accessPoint) GetMode() Nm80211Mode { + return Nm80211Mode(a.getUint32Property(AccessPointPropertyMode)) +} + +func (a *accessPoint) GetMaxBitrate() uint32 { + return a.getUint32Property(AccessPointPropertyMaxBitrate) +} + +func (a *accessPoint) GetStrength() uint8 { + return a.getUint8Property(AccessPointPropertyStrength) +} + +func (a *accessPoint) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "Flags": a.GetFlags(), + "WPAFlags": a.GetWPAFlags(), + "RSNFlags": a.GetRSNFlags(), + "SSID": a.GetSSID(), + "Frequency": a.GetFrequency(), + "HWAddress": a.GetHWAddress(), + "Mode": a.GetMode().String(), + "MaxBitrate": a.GetMaxBitrate(), + "Strength": a.GetStrength(), + }) +} diff --git a/Connection.go b/Connection.go index 5882f5b..5f8c123 100644 --- a/Connection.go +++ b/Connection.go @@ -1,6 +1,8 @@ package gonetworkmanager import ( + "encoding/json" + "github.com/godbus/dbus" ) @@ -19,6 +21,8 @@ type Connection interface { // network, as those are often protected. Secrets must be requested // separately using the GetSecrets() call. GetSettings() ConnectionSettings + + MarshalJSON() ([]byte, error) } func NewConnection(objectPath dbus.ObjectPath) (Connection, error) { @@ -46,3 +50,7 @@ func (c *connection) GetSettings() ConnectionSettings { return rv } + +func (c *connection) MarshalJSON() ([]byte, error) { + return json.Marshal(c.GetSettings()) +} diff --git a/Device.go b/Device.go index e397d33..8956922 100644 --- a/Device.go +++ b/Device.go @@ -1,6 +1,8 @@ package gonetworkmanager import ( + "encoding/json" + "github.com/godbus/dbus" ) @@ -48,6 +50,8 @@ type Device interface { // GetAvailableConnections gets an array of object paths of every configured // connection that is currently 'available' through this device. GetAvailableConnections() []Connection + + MarshalJSON() ([]byte, error) } func NewDevice(objectPath dbus.ObjectPath) (Device, error) { @@ -99,3 +103,17 @@ func (d *device) GetAvailableConnections() []Connection { return conns } + +func (d *device) marshalMap() map[string]interface{} { + return map[string]interface{}{ + "Interface": d.GetInterface(), + "State": d.GetState().String(), + "IP4Config": d.GetIP4Config(), + "DeviceType": d.GetDeviceType().String(), + "AvailableConnections": d.GetAvailableConnections(), + } +} + +func (d *device) MarshalJSON() ([]byte, error) { + return json.Marshal(d.marshalMap()) +} diff --git a/IP4Config.go b/IP4Config.go index a674538..e265b74 100644 --- a/IP4Config.go +++ b/IP4Config.go @@ -1,6 +1,8 @@ package gonetworkmanager import ( + "encoding/json" + "github.com/godbus/dbus" ) @@ -44,6 +46,8 @@ type IP4Config interface { // GetDomains gets a list of domains this address belongs to. GetDomains() []string + + MarshalJSON() ([]byte, error) } func NewIP4Config(objectPath dbus.ObjectPath) (IP4Config, error) { @@ -100,3 +104,12 @@ func (c *ip4Config) GetNameservers() []string { func (c *ip4Config) GetDomains() []string { return c.getSliceStringProperty(IP4ConfigPropertyDomains) } + +func (c *ip4Config) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "Addresses": c.GetAddresses(), + "Routes": c.GetRoutes(), + "Nameservers": c.GetNameservers(), + "Domains": c.GetDomains(), + }) +} diff --git a/NetworkManager.go b/NetworkManager.go index 07e0138..027ad6d 100644 --- a/NetworkManager.go +++ b/NetworkManager.go @@ -1,6 +1,8 @@ package gonetworkmanager import ( + "encoding/json" + "github.com/godbus/dbus" ) @@ -21,6 +23,11 @@ type NetworkManager interface { // NetworkManager daemon, based on the state of network devices under it's // management. GetState() NmState + + Subscribe() <-chan *dbus.Signal + Unsubscribe() + + MarshalJSON() ([]byte, error) } func NewNetworkManager() (NetworkManager, error) { @@ -30,6 +37,8 @@ func NewNetworkManager() (NetworkManager, error) { type networkManager struct { dbusBase + + sigChan chan *dbus.Signal } func (n *networkManager) GetDevices() []Device { @@ -52,3 +61,27 @@ func (n *networkManager) GetDevices() []Device { func (n *networkManager) GetState() NmState { return NmState(n.getUint32Property(NetworkManagerPropertyState)) } + +func (n *networkManager) Subscribe() <-chan *dbus.Signal { + if n.sigChan != nil { + return n.sigChan + } + + n.subscribeNamespace(NetworkManagerObjectPath) + n.sigChan = make(chan *dbus.Signal, 10) + n.conn.Signal(n.sigChan) + + return n.sigChan +} + +func (n *networkManager) Unsubscribe() { + n.conn.RemoveSignal(n.sigChan) + n.sigChan = nil +} + +func (n *networkManager) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "NetworkState": n.GetState().String(), + "Devices": n.GetDevices(), + }) +} diff --git a/WirelessDevice.go b/WirelessDevice.go index a712de0..00e729e 100644 --- a/WirelessDevice.go +++ b/WirelessDevice.go @@ -1,6 +1,8 @@ package gonetworkmanager import ( + "encoding/json" + "github.com/godbus/dbus" ) @@ -45,3 +47,9 @@ func (d *wirelessDevice) GetAccessPoints() []AccessPoint { return aps } + +func (d *wirelessDevice) MarshalJSON() ([]byte, error) { + m := d.device.marshalMap() + m["AccessPoints"] = d.GetAccessPoints() + return json.Marshal(m) +} diff --git a/enums.go b/enums.go index 6bf3d3d..658221c 100644 --- a/enums.go +++ b/enums.go @@ -65,3 +65,38 @@ const ( NmDeviceTypeGeneric NmDeviceType = 14 NmDeviceTypeTeam NmDeviceType = 15 ) + +//go:generate stringer -type=Nm80211APFlags +type Nm80211APFlags uint32 + +const ( + Nm80211APFlagsNone Nm80211APFlags = 0x0 + Nm80211APFlagsPrivacy Nm80211APFlags = 0x1 +) + +//go:generate stringer -type=Nm80211APSec +type Nm80211APSec uint32 + +const ( + Nm80211APSecNone Nm80211APSec = 0x0 + Nm80211APSecPairWEP40 Nm80211APSec = 0x1 + Nm80211APSecPairWEP104 Nm80211APSec = 0x2 + Nm80211APSecPairTKIP Nm80211APSec = 0x4 + Nm80211APSecPairCCMP Nm80211APSec = 0x8 + Nm80211APSecGroupWEP40 Nm80211APSec = 0x10 + Nm80211APSecGroupWEP104 Nm80211APSec = 0x20 + Nm80211APSecGroupTKIP Nm80211APSec = 0x40 + Nm80211APSecGroupCCMP Nm80211APSec = 0x80 + Nm80211APSecKeyMgmtPSK Nm80211APSec = 0x100 + Nm80211APSecKeyMgmt8021X Nm80211APSec = 0x200 +) + +//go:generate stringer -type=Nm80211Mode +type Nm80211Mode uint32 + +const ( + Nm80211ModeUnknown Nm80211Mode = 0 + Nm80211ModeAdhoc Nm80211Mode = 1 + Nm80211ModeInfra Nm80211Mode = 2 + Nm80211ModeAp Nm80211Mode = 3 +) diff --git a/errors.go b/errors.go deleted file mode 100644 index 990e6ad..0000000 --- a/errors.go +++ /dev/null @@ -1,9 +0,0 @@ -package gonetworkmanager - -import ( - "errors" -) - -var ( - ErrVariantType = errors.New("unexpected variant type") -) diff --git a/nm80211apflags_string.go b/nm80211apflags_string.go new file mode 100644 index 0000000..9b04c55 --- /dev/null +++ b/nm80211apflags_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=Nm80211APFlags"; DO NOT EDIT + +package gonetworkmanager + +import "fmt" + +const _Nm80211APFlags_name = "Nm80211APFlagsNoneNm80211APFlagsPrivacy" + +var _Nm80211APFlags_index = [...]uint8{0, 18, 39} + +func (i Nm80211APFlags) String() string { + if i >= Nm80211APFlags(len(_Nm80211APFlags_index)-1) { + return fmt.Sprintf("Nm80211APFlags(%d)", i) + } + return _Nm80211APFlags_name[_Nm80211APFlags_index[i]:_Nm80211APFlags_index[i+1]] +} diff --git a/nm80211apsec_string.go b/nm80211apsec_string.go new file mode 100644 index 0000000..99d7ed0 --- /dev/null +++ b/nm80211apsec_string.go @@ -0,0 +1,54 @@ +// Code generated by "stringer -type=Nm80211APSec"; DO NOT EDIT + +package gonetworkmanager + +import "fmt" + +const ( + _Nm80211APSec_name_0 = "Nm80211APSecNoneNm80211APSecPairWEP40Nm80211APSecPairWEP104" + _Nm80211APSec_name_1 = "Nm80211APSecPairTKIP" + _Nm80211APSec_name_2 = "Nm80211APSecPairCCMP" + _Nm80211APSec_name_3 = "Nm80211APSecGroupWEP40" + _Nm80211APSec_name_4 = "Nm80211APSecGroupWEP104" + _Nm80211APSec_name_5 = "Nm80211APSecGroupTKIP" + _Nm80211APSec_name_6 = "Nm80211APSecGroupCCMP" + _Nm80211APSec_name_7 = "Nm80211APSecKeyMgmtPSK" + _Nm80211APSec_name_8 = "Nm80211APSecKeyMgmt8021X" +) + +var ( + _Nm80211APSec_index_0 = [...]uint8{0, 16, 37, 59} + _Nm80211APSec_index_1 = [...]uint8{0, 20} + _Nm80211APSec_index_2 = [...]uint8{0, 20} + _Nm80211APSec_index_3 = [...]uint8{0, 22} + _Nm80211APSec_index_4 = [...]uint8{0, 23} + _Nm80211APSec_index_5 = [...]uint8{0, 21} + _Nm80211APSec_index_6 = [...]uint8{0, 21} + _Nm80211APSec_index_7 = [...]uint8{0, 22} + _Nm80211APSec_index_8 = [...]uint8{0, 24} +) + +func (i Nm80211APSec) String() string { + switch { + case 0 <= i && i <= 2: + return _Nm80211APSec_name_0[_Nm80211APSec_index_0[i]:_Nm80211APSec_index_0[i+1]] + case i == 4: + return _Nm80211APSec_name_1 + case i == 8: + return _Nm80211APSec_name_2 + case i == 16: + return _Nm80211APSec_name_3 + case i == 32: + return _Nm80211APSec_name_4 + case i == 64: + return _Nm80211APSec_name_5 + case i == 128: + return _Nm80211APSec_name_6 + case i == 256: + return _Nm80211APSec_name_7 + case i == 512: + return _Nm80211APSec_name_8 + default: + return fmt.Sprintf("Nm80211APSec(%d)", i) + } +} diff --git a/nm80211mode_string.go b/nm80211mode_string.go new file mode 100644 index 0000000..96d38a1 --- /dev/null +++ b/nm80211mode_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=Nm80211Mode"; DO NOT EDIT + +package gonetworkmanager + +import "fmt" + +const _Nm80211Mode_name = "Nm80211ModeUnknownNm80211ModeAdhocNm80211ModeInfraNm80211ModeAp" + +var _Nm80211Mode_index = [...]uint8{0, 18, 34, 50, 63} + +func (i Nm80211Mode) String() string { + if i >= Nm80211Mode(len(_Nm80211Mode_index)-1) { + return fmt.Sprintf("Nm80211Mode(%d)", i) + } + return _Nm80211Mode_name[_Nm80211Mode_index[i]:_Nm80211Mode_index[i+1]] +} diff --git a/utils.go b/utils.go index 2f94979..b754260 100644 --- a/utils.go +++ b/utils.go @@ -2,11 +2,16 @@ package gonetworkmanager import ( "encoding/binary" + "fmt" "net" "github.com/godbus/dbus" ) +const ( + dbusMethodAddMatch = "org.freedesktop.DBus.AddMatch" +) + type dbusBase struct { conn *dbus.Conn obj dbus.BusObject @@ -36,6 +41,17 @@ func (d *dbusBase) callError(value interface{}, method string, args ...interface return d.obj.Call(method, 0, args...).Store(value) } +func (d *dbusBase) subscribe(iface, member string) { + rule := fmt.Sprintf("type='signal',interface='%s',path='%s',member='%s'", + iface, d.obj.Path(), NetworkManagerInterface) + d.conn.BusObject().Call(dbusMethodAddMatch, 0, rule) +} + +func (d *dbusBase) subscribeNamespace(namespace string) { + rule := fmt.Sprintf("type='signal',path_namespace='%s'", namespace) + d.conn.BusObject().Call(dbusMethodAddMatch, 0, rule) +} + func (d *dbusBase) getProperty(iface string) interface{} { variant, err := d.obj.GetProperty(iface) if err != nil { @@ -47,7 +63,7 @@ func (d *dbusBase) getProperty(iface string) interface{} { func (d *dbusBase) getObjectProperty(iface string) dbus.ObjectPath { value, ok := d.getProperty(iface).(dbus.ObjectPath) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) } return value } @@ -55,7 +71,7 @@ func (d *dbusBase) getObjectProperty(iface string) dbus.ObjectPath { func (d *dbusBase) getSliceObjectProperty(iface string) []dbus.ObjectPath { value, ok := d.getProperty(iface).([]dbus.ObjectPath) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) } return value } @@ -63,7 +79,7 @@ func (d *dbusBase) getSliceObjectProperty(iface string) []dbus.ObjectPath { func (d *dbusBase) getStringProperty(iface string) string { value, ok := d.getProperty(iface).(string) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) } return value } @@ -71,7 +87,15 @@ func (d *dbusBase) getStringProperty(iface string) string { func (d *dbusBase) getSliceStringProperty(iface string) []string { value, ok := d.getProperty(iface).([]string) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) + } + return value +} + +func (d *dbusBase) getUint8Property(iface string) uint8 { + value, ok := d.getProperty(iface).(uint8) + if !ok { + panic(makeErrVariantType(iface)) } return value } @@ -79,7 +103,7 @@ func (d *dbusBase) getSliceStringProperty(iface string) []string { func (d *dbusBase) getUint32Property(iface string) uint32 { value, ok := d.getProperty(iface).(uint32) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) } return value } @@ -87,7 +111,7 @@ func (d *dbusBase) getUint32Property(iface string) uint32 { func (d *dbusBase) getSliceUint32Property(iface string) []uint32 { value, ok := d.getProperty(iface).([]uint32) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) } return value } @@ -95,7 +119,7 @@ func (d *dbusBase) getSliceUint32Property(iface string) []uint32 { func (d *dbusBase) getSliceSliceUint32Property(iface string) [][]uint32 { value, ok := d.getProperty(iface).([][]uint32) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) } return value } @@ -103,11 +127,15 @@ func (d *dbusBase) getSliceSliceUint32Property(iface string) [][]uint32 { func (d *dbusBase) getSliceByteProperty(iface string) []byte { value, ok := d.getProperty(iface).([]byte) if !ok { - panic(ErrVariantType) + panic(makeErrVariantType(iface)) } return value } +func makeErrVariantType(iface string) error { + return fmt.Errorf("unexpected variant type for '%s'", iface) +} + func ip4ToString(ip uint32) string { bs := []byte{0, 0, 0, 0} binary.LittleEndian.PutUint32(bs, ip)