Scan routine
This commit is contained in:
parent
73db704cf7
commit
3a3bc5da09
11 changed files with 142 additions and 50 deletions
|
|
@ -18,6 +18,8 @@ func Get(path string) (config models.Conf) {
|
||||||
viper.SetDefault("SCANER", "arpscan")
|
viper.SetDefault("SCANER", "arpscan")
|
||||||
viper.SetDefault("ARPARGS", "")
|
viper.SetDefault("ARPARGS", "")
|
||||||
viper.SetDefault("IFACES", "")
|
viper.SetDefault("IFACES", "")
|
||||||
|
viper.SetDefault("TIMEOUT", 60)
|
||||||
|
viper.SetDefault("IGNOREIP", "no")
|
||||||
|
|
||||||
viper.SetConfigFile(path)
|
viper.SetConfigFile(path)
|
||||||
viper.SetConfigType("yaml")
|
viper.SetConfigType("yaml")
|
||||||
|
|
@ -34,6 +36,8 @@ func Get(path string) (config models.Conf) {
|
||||||
config.Scaner = viper.Get("SCANER").(string)
|
config.Scaner = viper.Get("SCANER").(string)
|
||||||
config.ArpArgs = viper.Get("ARPARGS").(string)
|
config.ArpArgs = viper.Get("ARPARGS").(string)
|
||||||
config.Ifaces = viper.Get("IFACES").(string)
|
config.Ifaces = viper.Get("IFACES").(string)
|
||||||
|
config.Timeout = viper.GetInt("TIMEOUT")
|
||||||
|
config.IgnoreIP = viper.Get("IGNOREIP").(string)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
@ -52,6 +56,8 @@ func Write(config models.Conf) {
|
||||||
viper.Set("SCANER", config.Scaner)
|
viper.Set("SCANER", config.Scaner)
|
||||||
viper.Set("ARPARGS", config.ArpArgs)
|
viper.Set("ARPARGS", config.ArpArgs)
|
||||||
viper.Set("IFACES", config.Ifaces)
|
viper.Set("IFACES", config.Ifaces)
|
||||||
|
viper.Set("TIMEOUT", config.Timeout)
|
||||||
|
viper.Set("IGNOREIP", config.IgnoreIP)
|
||||||
|
|
||||||
err := viper.WriteConfig()
|
err := viper.WriteConfig()
|
||||||
check.IfError(err)
|
check.IfError(err)
|
||||||
|
|
|
||||||
|
|
@ -21,41 +21,55 @@ func Create(path string) {
|
||||||
"NOW" INTEGER DEFAULT 0
|
"NOW" INTEGER DEFAULT 0
|
||||||
);`
|
);`
|
||||||
dbExec(path, sqlStatement)
|
dbExec(path, sqlStatement)
|
||||||
|
|
||||||
|
sqlStatement = `CREATE TABLE IF NOT EXISTS "history" (
|
||||||
|
"ID" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||||
|
"NAME" TEXT NOT NULL,
|
||||||
|
"IFACE" TEXT,
|
||||||
|
"IP" TEXT,
|
||||||
|
"MAC" TEXT,
|
||||||
|
"HW" TEXT,
|
||||||
|
"DATE" TEXT,
|
||||||
|
"KNOWN" INTEGER DEFAULT 0,
|
||||||
|
"NOW" INTEGER DEFAULT 0
|
||||||
|
);`
|
||||||
|
dbExec(path, sqlStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert - insert host into table
|
// Insert - insert host into table
|
||||||
func Insert(path string, oneHost models.Host) {
|
func Insert(path, table string, oneHost models.Host) {
|
||||||
oneHost.Name = quoteStr(oneHost.Name)
|
oneHost.Name = quoteStr(oneHost.Name)
|
||||||
oneHost.Hw = quoteStr(oneHost.Hw)
|
oneHost.Hw = quoteStr(oneHost.Hw)
|
||||||
sqlStatement := `INSERT INTO "now" (NAME, IFACE, IP, MAC, HW, DATE, KNOWN, NOW)
|
sqlStatement := `INSERT INTO '%s' (NAME, IFACE, IP, MAC, HW, DATE, KNOWN, NOW)
|
||||||
VALUES ('%s','%s','%s','%s','%s','%s','%d','%d');`
|
VALUES ('%s','%s','%s','%s','%s','%s','%d','%d');`
|
||||||
sqlStatement = fmt.Sprintf(sqlStatement, oneHost.Name, oneHost.Iface, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now)
|
sqlStatement = fmt.Sprintf(sqlStatement, table, oneHost.Name, oneHost.Iface, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now)
|
||||||
|
|
||||||
dbExec(path, sqlStatement)
|
dbExec(path, sqlStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update - update host
|
// Update - update host
|
||||||
func Update(path string, oneHost models.Host) {
|
func Update(path, table string, oneHost models.Host) {
|
||||||
oneHost.Name = quoteStr(oneHost.Name)
|
oneHost.Name = quoteStr(oneHost.Name)
|
||||||
oneHost.Hw = quoteStr(oneHost.Hw)
|
oneHost.Hw = quoteStr(oneHost.Hw)
|
||||||
sqlStatement := `UPDATE "now" set
|
sqlStatement := `UPDATE '%s' set
|
||||||
NAME = '%s', IFACE = '%s', IP = '%s', MAC = '%s', HW = '%s', DATE = '%s',
|
NAME = '%s', IFACE = '%s', IP = '%s', MAC = '%s', HW = '%s', DATE = '%s',
|
||||||
KNOWN = '%d', NOW = '%d'
|
KNOWN = '%d', NOW = '%d'
|
||||||
WHERE ID = '%d';`
|
WHERE ID = '%d';`
|
||||||
sqlStatement = fmt.Sprintf(sqlStatement, oneHost.Name, oneHost.Iface, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now, oneHost.ID)
|
sqlStatement = fmt.Sprintf(sqlStatement, table, oneHost.Name, oneHost.Iface, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now, oneHost.ID)
|
||||||
|
|
||||||
dbExec(path, sqlStatement)
|
dbExec(path, sqlStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete - delete host from DB
|
// Delete - delete host from DB
|
||||||
func Delete(path string, id int) {
|
func Delete(path, table string, id int) {
|
||||||
sqlStatement := `DELETE FROM "now" WHERE ID='%d';`
|
sqlStatement := `DELETE FROM '%s' WHERE ID='%d';`
|
||||||
sqlStatement = fmt.Sprintf(sqlStatement, id)
|
sqlStatement = fmt.Sprintf(sqlStatement, table, id)
|
||||||
dbExec(path, sqlStatement)
|
dbExec(path, sqlStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear - delete all hosts from table
|
// Clear - delete all hosts from table
|
||||||
func Clear(path string) {
|
func Clear(path, table string) {
|
||||||
sqlStatement := `DELETE FROM "now";`
|
sqlStatement := `DELETE FROM '%s';`
|
||||||
|
sqlStatement = fmt.Sprintf(sqlStatement, table)
|
||||||
dbExec(path, sqlStatement)
|
dbExec(path, sqlStatement)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ func dbExec(path, sqlStatement string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select - select all hosts
|
// Select - select all hosts
|
||||||
func Select(path string) (dbHosts []models.Host) {
|
func Select(path, table string) (dbHosts []models.Host) {
|
||||||
|
|
||||||
sqlStatement := `SELECT * FROM "now" ORDER BY DATE DESC`
|
sqlStatement := "SELECT * FROM " + table + " ORDER BY DATE DESC"
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
db, _ := sqlx.Connect("sqlite", path)
|
db, _ := sqlx.Connect("sqlite", path)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ type Conf struct {
|
||||||
Ifaces string
|
Ifaces string
|
||||||
Scaner string
|
Scaner string
|
||||||
ArpArgs string
|
ArpArgs string
|
||||||
|
Timeout int
|
||||||
|
IgnoreIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Host - one host
|
// Host - one host
|
||||||
|
|
@ -28,24 +30,10 @@ type Host struct {
|
||||||
Now int `db:"NOW"`
|
Now int `db:"NOW"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// History for hosts
|
|
||||||
type History struct {
|
|
||||||
ID int `db:"ID"`
|
|
||||||
Host int `db:"HOST"`
|
|
||||||
Name string `db:"NAME"`
|
|
||||||
IP string `db:"IP"`
|
|
||||||
Mac string `db:"MAC"`
|
|
||||||
Hw string `db:"HW"`
|
|
||||||
Date string `db:"DATE"`
|
|
||||||
Known int `db:"KNOWN"`
|
|
||||||
State int `db:"STATE"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GuiData - all data sent to html page
|
// GuiData - all data sent to html page
|
||||||
type GuiData struct {
|
type GuiData struct {
|
||||||
Config Conf
|
Config Conf
|
||||||
Hosts []Host
|
Hosts []Host
|
||||||
Themes []string
|
Themes []string
|
||||||
Version string
|
Version string
|
||||||
Hist []History
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ var (
|
||||||
appConfig models.Conf
|
appConfig models.Conf
|
||||||
|
|
||||||
allHosts []models.Host
|
allHosts []models.Host
|
||||||
|
// histHosts []models.Host
|
||||||
|
|
||||||
|
quitScan chan bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// templFS - html templates
|
// templFS - html templates
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
.my-button {
|
.my-btn {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #00000000;
|
background-color: #00000000;
|
||||||
color: var(--bs-primary);
|
color: var(--bs-primary);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.my-button:hover {
|
.my-btn:hover {
|
||||||
background-color: #0000001a;
|
background-color: #0000001a;
|
||||||
}
|
}
|
||||||
|
|
@ -11,10 +11,18 @@ function createHTML(addr, i) {
|
||||||
now = `<i class="bi bi-check-circle-fill" style="color:var(--bs-success);"></i>`;
|
now = `<i class="bi bi-check-circle-fill" style="color:var(--bs-success);"></i>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let known = '';
|
||||||
|
|
||||||
|
if (addr.Known == 1) {
|
||||||
|
known = `<button type="button" class="btn btn-success">Yes</button>`;
|
||||||
|
} else {
|
||||||
|
known = `<button type="button" class="btn btn-warning" disabled>No</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
let html = `
|
let html = `
|
||||||
<tr>
|
<tr>
|
||||||
<td style="opacity: 45%;">${i}.</td>
|
<td style="opacity: 45%;">${i}.</td>
|
||||||
<td>${addr.Name}</td>
|
<td><input name="name" type="text" class="form-control" value="${addr.Name}"></td>
|
||||||
<td>${addr.Iface}</td>
|
<td>${addr.Iface}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="http://${addr.IP}">${addr.IP}</a>
|
<a href="http://${addr.IP}">${addr.IP}</a>
|
||||||
|
|
@ -22,7 +30,7 @@ function createHTML(addr, i) {
|
||||||
<td>${addr.Mac}</td>
|
<td>${addr.Mac}</td>
|
||||||
<td>${addr.Hw}</td>
|
<td>${addr.Hw}</td>
|
||||||
<td>${addr.Date}</td>
|
<td>${addr.Date}</td>
|
||||||
<td>${addr.Known}</td>
|
<td>${known}</td>
|
||||||
<td>${now}</td>
|
<td>${now}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
80
internal/web/scan.go
Normal file
80
internal/web/scan.go
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/arp"
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func updateScan() {
|
||||||
|
db.Create(appConfig.DBPath)
|
||||||
|
|
||||||
|
allHosts = db.Select(appConfig.DBPath, "now")
|
||||||
|
// histHosts = db.Select(appConfig.DBPath, "history")
|
||||||
|
|
||||||
|
quitScan = make(chan bool)
|
||||||
|
go startScan()
|
||||||
|
}
|
||||||
|
|
||||||
|
func startScan() {
|
||||||
|
var lastDate time.Time
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-quitScan:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
nowDate := time.Now()
|
||||||
|
plusDate := lastDate.Add(time.Duration(appConfig.Timeout) * time.Second)
|
||||||
|
|
||||||
|
if nowDate.After(plusDate) {
|
||||||
|
|
||||||
|
foundHosts := arp.Scan(appConfig.Ifaces)
|
||||||
|
compareHosts(foundHosts)
|
||||||
|
allHosts = db.Select(appConfig.DBPath, "now")
|
||||||
|
// histHosts = db.Select(appConfig.DBPath, "history")
|
||||||
|
|
||||||
|
lastDate = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(1) * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareHosts(foundHosts []models.Host) {
|
||||||
|
|
||||||
|
// Make map and Insert history
|
||||||
|
foundHostsMap := make(map[string]models.Host)
|
||||||
|
for _, fHost := range foundHosts {
|
||||||
|
foundHostsMap[fHost.Mac] = fHost
|
||||||
|
db.Insert(appConfig.DBPath, "history", fHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, aHost := range allHosts {
|
||||||
|
|
||||||
|
fHost, exists := foundHostsMap[aHost.Mac]
|
||||||
|
if exists && (appConfig.IgnoreIP == "yes" || aHost.IP == fHost.IP) {
|
||||||
|
|
||||||
|
aHost.Iface = fHost.Iface
|
||||||
|
aHost.IP = fHost.IP
|
||||||
|
aHost.Date = fHost.Date
|
||||||
|
aHost.Now = 1
|
||||||
|
|
||||||
|
delete(foundHostsMap, aHost.Mac)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
aHost.Now = 0
|
||||||
|
}
|
||||||
|
db.Update(appConfig.DBPath, "now", aHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fHost := range foundHostsMap {
|
||||||
|
|
||||||
|
// NOTIFY, LOG
|
||||||
|
db.Insert(appConfig.DBPath, "now", fHost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
<link href="{{ .Config.NodePath }}/node_modules/bootswatch/dist/{{ .Config.Theme }}/bootstrap.min.css" rel="stylesheet">
|
<link href="{{ .Config.NodePath }}/node_modules/bootswatch/dist/{{ .Config.Theme }}/bootstrap.min.css" rel="stylesheet">
|
||||||
<script src="{{ .Config.NodePath }}/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="{{ .Config.NodePath }}/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
<!-- CSS -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="/fs/public/css/index.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md navbar-dark bg-primary">
|
<nav class="navbar navbar-expand-md navbar-dark bg-primary">
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,14 @@
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<th style="width: 3em;"></th>
|
<th style="width: 3em;"></th>
|
||||||
<th>Name <i class="bi bi-sort-down-alt" onclick="sortBy('Name')"></i></th>
|
<th>Name <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('Name')"></i></th>
|
||||||
<th>Iface <i class="bi bi-sort-down-alt" onclick="sortBy('Iface')"></i></th>
|
<th>Iface <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('Iface')"></i></th>
|
||||||
<th>IP <i class="bi bi-sort-down-alt" onclick="sortBy('IP')"></i></th>
|
<th>IP <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('IP')"></i></th>
|
||||||
<th>MAC <i class="bi bi-sort-down-alt" onclick="sortBy('Mac')"></i></th>
|
<th>MAC <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('Mac')"></i></th>
|
||||||
<th>Hardware <i class="bi bi-sort-down-alt" onclick="sortBy('Hw')"></i></th>
|
<th>Hardware <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('Hw')"></i></th>
|
||||||
<th>Date <i class="bi bi-sort-down-alt" onclick="sortBy('Date')"></i></th>
|
<th>Date <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('Date')"></i></th>
|
||||||
<th>Known <i class="bi bi-sort-down-alt" onclick="sortBy('Known')"></i></th>
|
<th>Known <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('Known')"></i></th>
|
||||||
<th>Online <i class="bi bi-sort-down-alt" onclick="sortBy('Now')"></i></th>
|
<th>Online <i class="bi bi-sort-down-alt my-btn" onclick="sortBy('Now')"></i></th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="tBody"></tbody>
|
<tbody id="tBody"></tbody>
|
||||||
<!-- index.js -->
|
<!-- index.js -->
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,8 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/aceberg/WatchYourLAN/internal/arp"
|
|
||||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Gui - start web server
|
// Gui - start web server
|
||||||
|
|
@ -26,6 +24,8 @@ func Gui(dirPath, nodePath string) {
|
||||||
appConfig.DBPath = dirPath + "/scan.db"
|
appConfig.DBPath = dirPath + "/scan.db"
|
||||||
appConfig.NodePath = nodePath
|
appConfig.NodePath = nodePath
|
||||||
|
|
||||||
|
updateScan() // scan.go
|
||||||
|
|
||||||
slog.Info("config", "path", appConfig.DirPath)
|
slog.Info("config", "path", appConfig.DirPath)
|
||||||
|
|
||||||
address := appConfig.Host + ":" + appConfig.Port
|
address := appConfig.Host + ":" + appConfig.Port
|
||||||
|
|
@ -34,11 +34,6 @@ func Gui(dirPath, nodePath string) {
|
||||||
slog.Info("Web GUI at http://" + address)
|
slog.Info("Web GUI at http://" + address)
|
||||||
slog.Info("=================================== ")
|
slog.Info("=================================== ")
|
||||||
|
|
||||||
updateAllHosts()
|
|
||||||
// Run scan
|
|
||||||
allHosts = arp.Scan(appConfig.Ifaces)
|
|
||||||
slog.Info("ARPSCAN:", "found", allHosts)
|
|
||||||
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
|
|
@ -57,7 +52,3 @@ func Gui(dirPath, nodePath string) {
|
||||||
err := router.Run(address)
|
err := router.Run(address)
|
||||||
check.IfError(err)
|
check.IfError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateAllHosts() {
|
|
||||||
allHosts = db.Select(appConfig.DBPath)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue