diff --git a/cmd/spaceDevices.go b/cmd/spaceDevices.go index 93a0779..af24d67 100644 --- a/cmd/spaceDevices.go +++ b/cmd/spaceDevices.go @@ -1,28 +1,68 @@ package main import ( + "fmt" + "os" + "github.com/ktt-ol/spaceDevices/conf" "github.com/ktt-ol/spaceDevices/db" "github.com/ktt-ol/spaceDevices/mqtt" "github.com/ktt-ol/spaceDevices/webService" - log "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus" ) const CONFIG_FILE = "config.toml" func main() { - log.SetLevel(log.DebugLevel) - log.SetFormatter(&log.TextFormatter{DisableColors: true}) - config := conf.LoadConfig(CONFIG_FILE) + setupLogging(config.Misc) + //spaceDevices.EnableMqttDebugLogging() userDb := db.NewUserDb(config.MacDb) masterDb := db.NewMasterDb(config.MacDb) mqttHandler := mqtt.NewMqttHandler(config.Mqtt) - data := mqtt.NewDeviceData(mqttHandler, masterDb, userDb) + data := mqtt.NewDeviceData(config.Locations, mqttHandler, masterDb, userDb) webService.StartWebService(config.Server, data, userDb) } + +type StdErrLogHook struct { +} + +func (h *StdErrLogHook) Levels() []logrus.Level { + return []logrus.Level{logrus.WarnLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel} +} +func (h *StdErrLogHook) Fire(entry *logrus.Entry) error { + line, err := entry.String() + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) + return err + } + fmt.Fprintf(os.Stderr, line) + return nil +} + +func setupLogging(config conf.MiscConf) { + logrus.SetFormatter(&logrus.TextFormatter{DisableColors: true}) + if config.DebugLogging { + logrus.SetLevel(logrus.DebugLevel) + } else { + logrus.SetLevel(logrus.InfoLevel) + } + + if config.Logfile == "" { + logrus.SetOutput(os.Stdout) + } else { + // https://github.com/sirupsen/logrus/issues/227 + file, err := os.OpenFile(config.Logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) + if err == nil { + logrus.SetOutput(file) + } else { + logrus.Warnf("Failed to log to file '%s', using default stderr.", config.Logfile) + } + logrus.AddHook(&StdErrLogHook{}) + } +} diff --git a/conf/config.go b/conf/config.go index c0d7eb7..31825c2 100644 --- a/conf/config.go +++ b/conf/config.go @@ -16,12 +16,18 @@ func LoadConfig(configFile string) TomlConfig { } type TomlConfig struct { + Misc MiscConf Server ServerConf MacDb MacDbConf Mqtt MqttConf Locations []Location `toml:"location"` } +type MiscConf struct { + DebugLogging bool + Logfile string +} + type ServerConf struct { Host string Port int diff --git a/conf/config_test.go b/conf/config_test.go index c9952ba..70685d1 100644 --- a/conf/config_test.go +++ b/conf/config_test.go @@ -11,6 +11,8 @@ func Test_exampleConfig(t *testing.T) { assert := assert.New(t) config := conf.LoadConfig("../config.example.toml") + assert.Equal(false, config.Misc.DebugLogging) + assert.Equal("", config.Misc.Logfile) assert.Equal(2, len(config.Locations)) assert.Equal("Bar", config.Locations[0].Name) assert.Equal(2, len(config.Locations[0].Ids)) diff --git a/config.example.toml b/config.example.toml index eb1ce9b..ab40e9e 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,4 +1,8 @@ # COPY THIS TO config.toml +[misc] +debugLogging = false +# if enabled, all logging goes to the file. Warn and up goes to stderr, too. +# logfile = "/var/log/spaceDevices2.log" [server] host = "0.0.0.0" diff --git a/db/utils_test.go b/db/utils_test.go index 8913900..c748b43 100644 --- a/db/utils_test.go +++ b/db/utils_test.go @@ -9,4 +9,6 @@ func Test_IsMacLocallyAdministered(t *testing.T) { assert.True(t, IsMacLocallyAdministered("06:00:00:00:00:00")) assert.True(t, IsMacLocallyAdministered("62:01:0f:b5:f2:d9")) assert.False(t, IsMacLocallyAdministered("20:c9:d0:7a:fa:31")) + + assert.True(t, IsMacLocallyAdministered("d4:38:9c:01:dd:03")) } diff --git a/do.sh b/do.sh index bc5214e..d7949d0 100755 --- a/do.sh +++ b/do.sh @@ -15,6 +15,9 @@ while (( "$#" )); do build-linux) env GOOS=linux GOARCH=amd64 go build cmd/spaceDevices.go ;; + test-sync) + rsync -n -avzi --delete spaceDevices webUI root@spacegate:/home/status/spaceDevices2/ + ;; sync) rsync -avzi --delete spaceDevices webUI root@spacegate:/home/status/spaceDevices2/ ;; diff --git a/mqtt/deviceData.go b/mqtt/deviceData.go index b0e5866..0688861 100644 --- a/mqtt/deviceData.go +++ b/mqtt/deviceData.go @@ -2,12 +2,15 @@ package mqtt import ( "encoding/json" - "fmt" "github.com/ktt-ol/spaceDevices/conf" "github.com/ktt-ol/spaceDevices/db" "github.com/sirupsen/logrus" + "crypto/md5" + "fmt" + "bytes" + "sort" ) var ignoredVisibility = [...]db.Visibility{db.VisibilityCriticalInfrastructure, db.VisibilityImportantInfrastructure, @@ -27,6 +30,9 @@ type DeviceData struct { masterDb db.MasterDb userDb db.UserDb wifiSessionList []WifiSession + + lastSentHash []byte + // more to come, e.g. LanSessions } @@ -44,12 +50,32 @@ func NewDeviceData(locations []conf.Location, mqttHandler *MqttHandler, masterDb } func (d *DeviceData) newData(data []byte) { - fmt.Println("got data, len ", len(data)) sessionsList, peopleAndDevices, ok := d.parseWifiSessions(data) if ok { d.wifiSessionList = sessionsList - fmt.Printf("peopleAndDevices: %+v\n", peopleAndDevices) - d.mqttHandler.SendPeopleAndDevices(peopleAndDevices) + if ddLogger.Logger.Level >= logrus.DebugLevel { + people := make(map[string]interface{}) + for _, p := range peopleAndDevices.People { + people[p.Name] = nil + } + peopleList := make([]string, len(people)) + for k, _ := range people { + peopleList = append(peopleList, k) + } + sort.Strings(peopleList) + ddLogger.Debugf("PeopleCount: %d, DeviceCount: %d, UnknownDevicesCount: %d, Persons: %s", + peopleAndDevices.PeopleCount, peopleAndDevices.DeviceCount, peopleAndDevices.UnknownDevicesCount, peopleList) + } + h := md5.New() + s := fmt.Sprintf("%v", peopleAndDevices) + hash := h.Sum([]byte(s)) + if bytes.Equal(hash, d.lastSentHash) { + ddLogger.Debug("Nothing changed in people count, skipping mqtt") + } else { + d.mqttHandler.SendPeopleAndDevices(peopleAndDevices) + d.lastSentHash = hash + } + } } @@ -139,11 +165,13 @@ SESSION_LOOP: var person Person if devicesEntry.showDevices { person = Person{Name: username, Devices: devicesEntry.devices} + sort.Sort(DevicesSorter(person.Devices)) } else { person = Person{Name: username} } peopleAndDevices.People = append(peopleAndDevices.People, person) } + sort.Sort(PersonSorter(peopleAndDevices.People)) success = true return diff --git a/mqtt/deviceData_test.go b/mqtt/deviceData_test.go index 648533c..4456f62 100644 --- a/mqtt/deviceData_test.go +++ b/mqtt/deviceData_test.go @@ -189,10 +189,21 @@ func Test_peopleCalculation(t *testing.T) { entry.Visibility = db.VisibilityCriticalInfrastructure masterMap["00:00:00:00:00:05"] = entry _, peopleAndDevices, _ = dd.parseWifiSessions(testData) - fmt.Printf("peopleAndDevices: %+v\n", peopleAndDevices) assertPeopleAndDevices(assert, 2, 3, 5, 0, peopleAndDevices) - t.Fail() + // add a second device for olaf + testData = newSessionTestData(stt("1", "01"), stt("2", "02"), stt("3", "03"), stt("4", "04"), stt("5", "05"), stt("6", "06")) + userMap["00:00:00:00:00:06"] = db.UserDbEntry{Name: "olaf", DeviceName: "mac", Visibility: db.VisibilityAll} + _, peopleAndDevices, _ = dd.parseWifiSessions(testData) + fmt.Printf("peopleAndDevices: %+v\n", peopleAndDevices) + assertPeopleAndDevices(assert, 2, 3, 6, 0, peopleAndDevices) + for _, p := range peopleAndDevices.People { + if p.Name == "olaf" { + assert.Equal(2, len(p.Devices)) + } else { + assert.Equal(0, len(p.Devices)) + } + } } func assertPeopleAndDevices(assert *assert.Assertions, peopleArrayCount int, peopleCount uint, deviceCount uint, unknownDevicesCount uint, test PeopleAndDevices) { diff --git a/mqtt/mqtt.go b/mqtt/mqtt.go index 795c0f5..97b2492 100644 --- a/mqtt/mqtt.go +++ b/mqtt/mqtt.go @@ -16,7 +16,8 @@ const CLIENT_ID = "spaceDevices2" const TOPIC_SESSIONS = "/net/wlan-sessions" // ATTENTION: test topic! -const TOPIC_DEVICES = "/test/net_devices" +//const TOPIC_DEVICES = "/test/net_devices" +const TOPIC_DEVICES = "/net/devices" var mqttLogger = log.WithField("where", "mqtt") @@ -108,7 +109,7 @@ func (h *MqttHandler) onConnect(client mqtt.Client) { err := subscribe(client, TOPIC_SESSIONS, func(client mqtt.Client, message mqtt.Message) { mqttLogger.Debug("new wifi sessions") - /* + ///* mock := []byte(`{ "38134": { "last-auth": 1509211121, "vlan": "default", @@ -126,7 +127,7 @@ func (h *MqttHandler) onConnect(client mqtt.Client) { "last-snr": 47, "last-rate-mbits": "6", "ap": 1, - "mac": "10:68:3f:bb:bb:bb", + "mac": "d4:38:9c:01:dd:03", "radio": 2, "userinfo": { "name": "Holger", @@ -137,10 +138,10 @@ func (h *MqttHandler) onConnect(client mqtt.Client) { "last-rssi-dbm": -48, "last-activity": 1509211584 }}`) - */ + //*/ select { - // case h.newDataChan <- mock: - case h.newDataChan <- message.Payload(): + case h.newDataChan <- mock: + //case h.newDataChan <- message.Payload(): break default: mqttLogger.Println("No one receives the message.") diff --git a/mqtt/structs.go b/mqtt/structs.go index fb57fc9..7aada9a 100644 --- a/mqtt/structs.go +++ b/mqtt/structs.go @@ -1,5 +1,9 @@ package mqtt +import ( + "strings" +) + type WifiSession struct { Ip string Mac string @@ -12,14 +16,38 @@ type Devices struct { Location string `json:"location"` } +type DevicesSorter []Devices + +func (s DevicesSorter) Len() int { + return len(s) +} +func (s DevicesSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s DevicesSorter) Less(i, j int) bool { + return strings.Compare(s[i].Name, s[j].Name) < 0 +} + type Person struct { Name string `json:"name"` Devices []Devices `json:"devices"` } +type PersonSorter []Person + +func (s PersonSorter) Len() int { + return len(s) +} +func (s PersonSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s PersonSorter) Less(i, j int) bool { + return strings.Compare(s[i].Name, s[j].Name) < 0 +} + type PeopleAndDevices struct { People []Person `json:"people"` - PeopleCount uint `json:"peopleCount"` - DeviceCount uint `json:"deviceCount"` - UnknownDevicesCount uint `json:"unknownDevicesCount"` + PeopleCount uint16 `json:"peopleCount"` + DeviceCount uint16 `json:"deviceCount"` + UnknownDevicesCount uint16 `json:"unknownDevicesCount"` } diff --git a/webService/web.go b/webService/web.go index a9b5009..3267ab2 100644 --- a/webService/web.go +++ b/webService/web.go @@ -60,12 +60,12 @@ func overviewPageHandler(c *gin.Context) { isLocallyAdministered := false macNotFound := false if info, ok := devices.GetByIp(ip); ok { + mac = info.Mac + isLocallyAdministered = db.IsMacLocallyAdministered(mac) if userInfo, ok := macDb.Get(info.Mac); ok { - mac = info.Mac name = userInfo.Name deviceName = userInfo.DeviceName visibility = userInfo.Visibility - isLocallyAdministered = db.IsMacLocallyAdministered(mac) } } else { macNotFound = true diff --git a/webUI/assets/images/example.jpg b/webUI/assets/images/example.jpg new file mode 100644 index 0000000..aba40e6 Binary files /dev/null and b/webUI/assets/images/example.jpg differ diff --git a/webUI/assets/images/screenshot.png b/webUI/assets/images/screenshot.png deleted file mode 100644 index 2eab11e..0000000 Binary files a/webUI/assets/images/screenshot.png and /dev/null differ diff --git a/webUI/templates/help.html b/webUI/templates/help.html index b871683..a347c10 100644 --- a/webUI/templates/help.html +++ b/webUI/templates/help.html @@ -25,11 +25,12 @@ von der Status Seite.
- +