forked from Elara6331/itd
Compare commits
11 Commits
4cdd47311f
...
e9d7cf1770
Author | SHA1 | Date | |
---|---|---|---|
e9d7cf1770 | |||
1f15d0ae51 | |||
6113ac019e | |||
947ab7fbcb | |||
948678790a | |||
8fb430a359 | |||
6ac4ab9f4d | |||
f25a893475 | |||
0defa1ce91 | |||
ee4f563b05 | |||
9ecd45dadd |
@ -22,6 +22,7 @@
|
|||||||
- Weather
|
- Weather
|
||||||
- BLE Filesystem
|
- BLE Filesystem
|
||||||
- Navigation (PureMaps)
|
- Navigation (PureMaps)
|
||||||
|
- FUSE Filesystem
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
88
calls.go
88
calls.go
@ -10,7 +10,7 @@ import (
|
|||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initCallNotifs(ctx context.Context, dev *infinitime.Device) error {
|
func initCallNotifs(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
// Connect to system bus. This connection is for method calls.
|
// Connect to system bus. This connection is for method calls.
|
||||||
conn, err := utils.NewSystemBusConn(ctx)
|
conn, err := utils.NewSystemBusConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -53,49 +53,55 @@ func initCallNotifs(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
var respHandlerOnce sync.Once
|
var respHandlerOnce sync.Once
|
||||||
var callObj dbus.BusObject
|
var callObj dbus.BusObject
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
// For every message received
|
defer wg.Done("callNotifs")
|
||||||
for event := range callCh {
|
for {
|
||||||
// Get path to call object
|
select {
|
||||||
callPath := event.Body[0].(dbus.ObjectPath)
|
case event := <-callCh:
|
||||||
// Get call object
|
// Get path to call object
|
||||||
callObj = conn.Object("org.freedesktop.ModemManager1", callPath)
|
callPath := event.Body[0].(dbus.ObjectPath)
|
||||||
|
// Get call object
|
||||||
|
callObj = conn.Object("org.freedesktop.ModemManager1", callPath)
|
||||||
|
|
||||||
// Get phone number from call object using method call connection
|
// Get phone number from call object using method call connection
|
||||||
phoneNum, err := getPhoneNum(conn, callObj)
|
phoneNum, err := getPhoneNum(conn, callObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error getting phone number").Err(err).Send()
|
log.Error("Error getting phone number").Err(err).Send()
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
// Send call notification to InfiniTime
|
|
||||||
resCh, err := dev.NotifyCall(phoneNum)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go respHandlerOnce.Do(func() {
|
|
||||||
// Wait for PineTime response
|
|
||||||
for res := range resCh {
|
|
||||||
switch res {
|
|
||||||
case infinitime.CallStatusAccepted:
|
|
||||||
// Attempt to accept call
|
|
||||||
err = acceptCall(ctx, conn, callObj)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Error accepting call").Err(err).Send()
|
|
||||||
}
|
|
||||||
case infinitime.CallStatusDeclined:
|
|
||||||
// Attempt to decline call
|
|
||||||
err = declineCall(ctx, conn, callObj)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Error declining call").Err(err).Send()
|
|
||||||
}
|
|
||||||
case infinitime.CallStatusMuted:
|
|
||||||
// Warn about unimplemented muting
|
|
||||||
log.Warn("Muting calls is not implemented").Send()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// Send call notification to InfiniTime
|
||||||
|
resCh, err := dev.NotifyCall(phoneNum)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go respHandlerOnce.Do(func() {
|
||||||
|
// Wait for PineTime response
|
||||||
|
for res := range resCh {
|
||||||
|
switch res {
|
||||||
|
case infinitime.CallStatusAccepted:
|
||||||
|
// Attempt to accept call
|
||||||
|
err = acceptCall(ctx, conn, callObj)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Error accepting call").Err(err).Send()
|
||||||
|
}
|
||||||
|
case infinitime.CallStatusDeclined:
|
||||||
|
// Attempt to decline call
|
||||||
|
err = declineCall(ctx, conn, callObj)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Error declining call").Err(err).Send()
|
||||||
|
}
|
||||||
|
case infinitime.CallStatusMuted:
|
||||||
|
// Warn about unimplemented muting
|
||||||
|
log.Warn("Muting calls is not implemented").Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -106,5 +106,8 @@ func setCfgDefaults() {
|
|||||||
"notifs.ignore.body": []string{},
|
"notifs.ignore.body": []string{},
|
||||||
|
|
||||||
"music.vol.interval": 5,
|
"music.vol.interval": 5,
|
||||||
|
|
||||||
|
"fuse.enabled": false,
|
||||||
|
"fuse.mountpoint": "/tmp/itd/mnt",
|
||||||
}, "."), nil)
|
}, "."), nil)
|
||||||
}
|
}
|
||||||
|
66
fuse.go
Normal file
66
fuse.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
|
"github.com/hanwen/go-fuse/v2/fuse"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
"go.arsenm.dev/itd/internal/fusefs"
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startFUSE(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
|
// This is where we'll mount the FS
|
||||||
|
err := os.MkdirAll(k.String("fuse.mountpoint"), 0o755)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the error because nothing might be mounted on the mountpoint
|
||||||
|
_ = fusefs.Unmount(k.String("fuse.mountpoint"))
|
||||||
|
|
||||||
|
root, err := fusefs.BuildRootNode(dev)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Building root node failed").
|
||||||
|
Err(err).
|
||||||
|
Send()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := fs.Mount(k.String("fuse.mountpoint"), root, &fs.Options{
|
||||||
|
MountOptions: fuse.MountOptions{
|
||||||
|
// Set to true to see how the file system works.
|
||||||
|
Debug: false,
|
||||||
|
SingleThreaded: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Mounting failed").
|
||||||
|
Str("target", k.String("fuse.mountpoint")).
|
||||||
|
Err(err).
|
||||||
|
Send()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Mounted on target").
|
||||||
|
Str("target", k.String("fuse.mountpoint")).
|
||||||
|
Send()
|
||||||
|
|
||||||
|
fusefs.BuildProperties(dev)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Error getting BLE filesystem").Err(err).Send()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done("fuse")
|
||||||
|
<-ctx.Done()
|
||||||
|
server.Unmount()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
5
go.mod
5
go.mod
@ -10,11 +10,12 @@ require (
|
|||||||
github.com/cheggaaa/pb/v3 v3.1.0
|
github.com/cheggaaa/pb/v3 v3.1.0
|
||||||
github.com/gen2brain/dlgs v0.0.0-20220603100644-40c77870fa8d
|
github.com/gen2brain/dlgs v0.0.0-20220603100644-40c77870fa8d
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
|
github.com/hanwen/go-fuse/v2 v2.2.0
|
||||||
github.com/knadh/koanf v1.4.4
|
github.com/knadh/koanf v1.4.4
|
||||||
github.com/mattn/go-isatty v0.0.17
|
github.com/mattn/go-isatty v0.0.17
|
||||||
github.com/mozillazg/go-pinyin v0.19.0
|
github.com/mozillazg/go-pinyin v0.19.0
|
||||||
github.com/urfave/cli/v2 v2.23.7
|
github.com/urfave/cli/v2 v2.23.7
|
||||||
go.arsenm.dev/drpc v0.0.0-20230104221210-aa7525743d98
|
go.arsenm.dev/drpc v0.0.0-20230328202554-c1f2aa71e794
|
||||||
go.arsenm.dev/infinitime v0.0.0-20230104230015-512d48bc2469
|
go.arsenm.dev/infinitime v0.0.0-20230104230015-512d48bc2469
|
||||||
go.arsenm.dev/logger v0.0.0-20230104225304-d706171ea6df
|
go.arsenm.dev/logger v0.0.0-20230104225304-d706171ea6df
|
||||||
golang.org/x/text v0.5.0
|
golang.org/x/text v0.5.0
|
||||||
@ -73,7 +74,7 @@ require (
|
|||||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099 // indirect
|
golang.org/x/mobile v0.0.0-20221110043201-43a038452099 // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
golang.org/x/mod v0.7.0 // indirect
|
||||||
golang.org/x/net v0.4.0 // indirect
|
golang.org/x/net v0.4.0 // indirect
|
||||||
golang.org/x/sys v0.3.0 // indirect
|
golang.org/x/sys v0.6.0 // indirect
|
||||||
golang.org/x/tools v0.4.0 // indirect
|
golang.org/x/tools v0.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
honnef.co/go/js/dom v0.0.0-20221001195520-26252dedbe70 // indirect
|
honnef.co/go/js/dom v0.0.0-20221001195520-26252dedbe70 // indirect
|
||||||
|
12
go.sum
12
go.sum
@ -257,6 +257,8 @@ github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfI
|
|||||||
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
|
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
|
github.com/hanwen/go-fuse/v2 v2.2.0 h1:jo5QZYmBLNcl9ovypWaQ5yXMSSV+Ch68xoC3rtZvvBM=
|
||||||
|
github.com/hanwen/go-fuse/v2 v2.2.0/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc=
|
||||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
|
github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
|
||||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
@ -339,6 +341,8 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
|
|||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||||
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||||
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
||||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
@ -525,8 +529,8 @@ github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5ta
|
|||||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||||
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
||||||
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||||
go.arsenm.dev/drpc v0.0.0-20230104221210-aa7525743d98 h1:Tt/05IEOeIEfovj8YLxDaZz+SP8eR1uPLtcgD5Pf4EI=
|
go.arsenm.dev/drpc v0.0.0-20230328202554-c1f2aa71e794 h1:8KQpRoQmCTgDvyHFStaIiz5NUNNqMHqVlZoGvIk+OwQ=
|
||||||
go.arsenm.dev/drpc v0.0.0-20230104221210-aa7525743d98/go.mod h1:K5cFls42m5q1RIphTVojRdXLaoCknq/kBqQt8Ow3XuA=
|
go.arsenm.dev/drpc v0.0.0-20230328202554-c1f2aa71e794/go.mod h1:K5cFls42m5q1RIphTVojRdXLaoCknq/kBqQt8Ow3XuA=
|
||||||
go.arsenm.dev/infinitime v0.0.0-20230104230015-512d48bc2469 h1:LsJHg+8rQSYnTE1sSCjBCACxUUVMZIOQani8J6wF2/E=
|
go.arsenm.dev/infinitime v0.0.0-20230104230015-512d48bc2469 h1:LsJHg+8rQSYnTE1sSCjBCACxUUVMZIOQani8J6wF2/E=
|
||||||
go.arsenm.dev/infinitime v0.0.0-20230104230015-512d48bc2469/go.mod h1:scUyDmLmCHn6CanGbau8yjTjzyhUbLJcsjmDCCKMIII=
|
go.arsenm.dev/infinitime v0.0.0-20230104230015-512d48bc2469/go.mod h1:scUyDmLmCHn6CanGbau8yjTjzyhUbLJcsjmDCCKMIII=
|
||||||
go.arsenm.dev/logger v0.0.0-20230104225304-d706171ea6df h1:8mBHvEe7BJmpOeKSMA5YLqrGo9dCpePocTeR0C1+/2w=
|
go.arsenm.dev/logger v0.0.0-20230104225304-d706171ea6df h1:8mBHvEe7BJmpOeKSMA5YLqrGo9dCpePocTeR0C1+/2w=
|
||||||
@ -752,8 +756,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
607
internal/fusefs/fuse.go
Normal file
607
internal/fusefs/fuse.go
Normal file
@ -0,0 +1,607 @@
|
|||||||
|
package fusefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
|
"github.com/hanwen/go-fuse/v2/fuse"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
"go.arsenm.dev/infinitime/blefs"
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ITProperty struct {
|
||||||
|
name string
|
||||||
|
Ino uint64
|
||||||
|
gen func() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DirEntry struct {
|
||||||
|
isDir bool
|
||||||
|
modtime uint64
|
||||||
|
size uint32
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ITNode struct {
|
||||||
|
fs.Inode
|
||||||
|
kind nodeKind
|
||||||
|
Ino uint64
|
||||||
|
|
||||||
|
lst []DirEntry
|
||||||
|
self DirEntry
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeKind uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
nodeKindRoot = iota
|
||||||
|
nodeKindInfo
|
||||||
|
nodeKindFS
|
||||||
|
nodeKindReadOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
myfs *blefs.FS = nil
|
||||||
|
inodemap map[string]uint64 = nil
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildRootNode(dev *infinitime.Device) (*ITNode, error) {
|
||||||
|
var err error
|
||||||
|
inodemap = make(map[string]uint64)
|
||||||
|
myfs, err = dev.FS()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Failed to get filesystem").Err(err).Send()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ITNode{kind: nodeKindRoot}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = make([]ITProperty, 6)
|
||||||
|
|
||||||
|
func BuildProperties(dev *infinitime.Device) {
|
||||||
|
properties[0] = ITProperty{
|
||||||
|
"heartrate", 2,
|
||||||
|
func() ([]byte, error) {
|
||||||
|
ans, err := dev.HeartRate()
|
||||||
|
return []byte(strconv.Itoa(int(ans)) + "\n"), err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
properties[1] = ITProperty{
|
||||||
|
"battery", 3,
|
||||||
|
func() ([]byte, error) {
|
||||||
|
ans, err := dev.BatteryLevel()
|
||||||
|
return []byte(strconv.Itoa(int(ans)) + "\n"), err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
properties[2] = ITProperty{
|
||||||
|
"motion", 4,
|
||||||
|
func() ([]byte, error) {
|
||||||
|
ans, err := dev.Motion()
|
||||||
|
return []byte(strconv.Itoa(int(ans.X)) + " " + strconv.Itoa(int(ans.Y)) + " " + strconv.Itoa(int(ans.Z)) + "\n"), err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
properties[3] = ITProperty{
|
||||||
|
"stepcount", 6,
|
||||||
|
func() ([]byte, error) {
|
||||||
|
ans, err := dev.StepCount()
|
||||||
|
return []byte(strconv.Itoa(int(ans)) + "\n"), err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
properties[4] = ITProperty{
|
||||||
|
"version", 7,
|
||||||
|
func() ([]byte, error) {
|
||||||
|
ans, err := dev.Version()
|
||||||
|
return []byte(ans + "\n"), err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
properties[5] = ITProperty{
|
||||||
|
"address", 8,
|
||||||
|
func() ([]byte, error) {
|
||||||
|
ans := dev.Address()
|
||||||
|
return []byte(ans + "\n"), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeReaddirer = (*ITNode)(nil)
|
||||||
|
|
||||||
|
// Readdir is part of the NodeReaddirer interface
|
||||||
|
func (n *ITNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
||||||
|
switch n.kind {
|
||||||
|
case 0:
|
||||||
|
// root folder
|
||||||
|
r := make([]fuse.DirEntry, 2)
|
||||||
|
r[0] = fuse.DirEntry{
|
||||||
|
Name: "info",
|
||||||
|
Ino: 0,
|
||||||
|
Mode: fuse.S_IFDIR,
|
||||||
|
}
|
||||||
|
r[1] = fuse.DirEntry{
|
||||||
|
Name: "fs",
|
||||||
|
Ino: 1,
|
||||||
|
Mode: fuse.S_IFDIR,
|
||||||
|
}
|
||||||
|
return fs.NewListDirStream(r), 0
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// info folder
|
||||||
|
r := make([]fuse.DirEntry, 6)
|
||||||
|
for ind, value := range properties {
|
||||||
|
r[ind] = fuse.DirEntry{
|
||||||
|
Name: value.name,
|
||||||
|
Ino: value.Ino,
|
||||||
|
Mode: fuse.S_IFREG,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewListDirStream(r), 0
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// on info
|
||||||
|
files, err := myfs.ReadDir(n.path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE ReadDir failed").Str("path", n.path).Err(err).Send()
|
||||||
|
return nil, syscallErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("FUSE ReadDir succeeded").Str("path", n.path).Int("objects", len(files)).Send()
|
||||||
|
r := make([]fuse.DirEntry, len(files))
|
||||||
|
n.lst = make([]DirEntry, len(files))
|
||||||
|
for ind, entry := range files {
|
||||||
|
info, err := entry.Info()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Info failed").Str("path", n.path).Err(err).Send()
|
||||||
|
return nil, syscallErr(err)
|
||||||
|
}
|
||||||
|
name := info.Name()
|
||||||
|
|
||||||
|
file := DirEntry{
|
||||||
|
path: n.path + "/" + name,
|
||||||
|
size: uint32(info.Size()),
|
||||||
|
modtime: uint64(info.ModTime().Unix()),
|
||||||
|
isDir: info.IsDir(),
|
||||||
|
}
|
||||||
|
n.lst[ind] = file
|
||||||
|
|
||||||
|
ino := inodemap[file.path]
|
||||||
|
if ino == 0 {
|
||||||
|
ino = uint64(len(inodemap)) + 1
|
||||||
|
inodemap[file.path] = ino
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.isDir {
|
||||||
|
r[ind] = fuse.DirEntry{
|
||||||
|
Name: name,
|
||||||
|
Mode: fuse.S_IFDIR,
|
||||||
|
Ino: ino + 10,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r[ind] = fuse.DirEntry{
|
||||||
|
Name: name,
|
||||||
|
Mode: fuse.S_IFREG,
|
||||||
|
Ino: ino + 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fs.NewListDirStream(r), 0
|
||||||
|
}
|
||||||
|
r := make([]fuse.DirEntry, 0)
|
||||||
|
return fs.NewListDirStream(r), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeLookuper = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (n *ITNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
|
||||||
|
switch n.kind {
|
||||||
|
case 0:
|
||||||
|
// root folder
|
||||||
|
if name == "info" {
|
||||||
|
stable := fs.StableAttr{
|
||||||
|
Mode: fuse.S_IFDIR,
|
||||||
|
Ino: uint64(0),
|
||||||
|
}
|
||||||
|
operations := &ITNode{kind: nodeKindInfo, Ino: 0}
|
||||||
|
child := n.NewInode(ctx, operations, stable)
|
||||||
|
return child, 0
|
||||||
|
} else if name == "fs" {
|
||||||
|
stable := fs.StableAttr{
|
||||||
|
Mode: fuse.S_IFDIR,
|
||||||
|
Ino: uint64(1),
|
||||||
|
}
|
||||||
|
operations := &ITNode{kind: nodeKindFS, Ino: 1, path: ""}
|
||||||
|
child := n.NewInode(ctx, operations, stable)
|
||||||
|
return child, 0
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
// info folder
|
||||||
|
for _, value := range properties {
|
||||||
|
if value.name == name {
|
||||||
|
stable := fs.StableAttr{
|
||||||
|
Mode: fuse.S_IFREG,
|
||||||
|
Ino: uint64(value.Ino),
|
||||||
|
}
|
||||||
|
operations := &ITNode{kind: nodeKindReadOnly, Ino: value.Ino}
|
||||||
|
child := n.NewInode(ctx, operations, stable)
|
||||||
|
return child, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// FS object
|
||||||
|
if len(n.lst) == 0 {
|
||||||
|
n.Readdir(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range n.lst {
|
||||||
|
if file.path != n.path+"/"+name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Debug("FUSE Lookup successful").Str("path", file.path).Send()
|
||||||
|
|
||||||
|
if file.isDir {
|
||||||
|
stable := fs.StableAttr{
|
||||||
|
Mode: fuse.S_IFDIR,
|
||||||
|
Ino: inodemap[file.path],
|
||||||
|
}
|
||||||
|
operations := &ITNode{kind: nodeKindFS, path: file.path}
|
||||||
|
child := n.NewInode(ctx, operations, stable)
|
||||||
|
return child, 0
|
||||||
|
} else {
|
||||||
|
stable := fs.StableAttr{
|
||||||
|
Mode: fuse.S_IFREG,
|
||||||
|
Ino: inodemap[file.path],
|
||||||
|
}
|
||||||
|
operations := &ITNode{
|
||||||
|
kind: nodeKindFS, path: file.path,
|
||||||
|
self: file,
|
||||||
|
}
|
||||||
|
child := n.NewInode(ctx, operations, stable)
|
||||||
|
return child, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Warn("FUSE Lookup failed").Str("path", n.path+"/"+name).Send()
|
||||||
|
}
|
||||||
|
return nil, syscall.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
|
type bytesFileReadHandle struct {
|
||||||
|
content []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.FileReader = (*bytesFileReadHandle)(nil)
|
||||||
|
|
||||||
|
func (fh *bytesFileReadHandle) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
|
||||||
|
log.Debug("FUSE Executing Read").Int("size", len(fh.content)).Send()
|
||||||
|
end := off + int64(len(dest))
|
||||||
|
if end > int64(len(fh.content)) {
|
||||||
|
end = int64(len(fh.content))
|
||||||
|
}
|
||||||
|
return fuse.ReadResultData(fh.content[off:end]), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type sensorFileReadHandle struct {
|
||||||
|
content []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.FileReader = (*sensorFileReadHandle)(nil)
|
||||||
|
|
||||||
|
func (fh *sensorFileReadHandle) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
|
||||||
|
log.Debug("FUSE Executing Read").Int("size", len(fh.content)).Send()
|
||||||
|
end := off + int64(len(dest))
|
||||||
|
if end > int64(len(fh.content)) {
|
||||||
|
end = int64(len(fh.content))
|
||||||
|
}
|
||||||
|
return fuse.ReadResultData(fh.content[off:end]), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.FileFlusher = (*sensorFileReadHandle)(nil)
|
||||||
|
|
||||||
|
func (fh *sensorFileReadHandle) Flush(ctx context.Context) (errno syscall.Errno) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type bytesFileWriteHandle struct {
|
||||||
|
content []byte
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.FileWriter = (*bytesFileWriteHandle)(nil)
|
||||||
|
|
||||||
|
func (fh *bytesFileWriteHandle) Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno) {
|
||||||
|
log.Debug("FUSE Executing Write").Str("path", fh.path).Int("prev_size", len(fh.content)).Int("next_size", len(data)).Send()
|
||||||
|
if off != int64(len(fh.content)) {
|
||||||
|
log.Error("FUSE Write file size changed unexpectedly").Int("expect", int(off)).Int("received", len(fh.content)).Send()
|
||||||
|
return 0, syscall.ENXIO
|
||||||
|
}
|
||||||
|
fh.content = append(fh.content[:], data[:]...)
|
||||||
|
return uint32(len(data)), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.FileFlusher = (*bytesFileWriteHandle)(nil)
|
||||||
|
|
||||||
|
func (fh *bytesFileWriteHandle) Flush(ctx context.Context) (errno syscall.Errno) {
|
||||||
|
log.Debug("FUSE Attempting flush").Str("path", fh.path).Send()
|
||||||
|
fp, err := myfs.Create(fh.path, uint32(len(fh.content)))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Flush failed: create").Str("path", fh.path).Err(err).Send()
|
||||||
|
return syscallErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fh.content) == 0 {
|
||||||
|
log.Debug("FUSE Flush no data to write").Str("path", fh.path).Send()
|
||||||
|
err = fp.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Flush failed during close").Str("path", fh.path).Err(err).Send()
|
||||||
|
return syscallErr(err)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// For every progress event
|
||||||
|
for sent := range fp.Progress() {
|
||||||
|
log.Debug("FUSE Flush progress").Int("bytes", int(sent)).Int("total", len(fh.content)).Send()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r := bytes.NewReader(fh.content)
|
||||||
|
nread, err := io.Copy(fp, r)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Flush failed during write").Str("path", fh.path).Err(err).Send()
|
||||||
|
fp.Close()
|
||||||
|
return syscallErr(err)
|
||||||
|
}
|
||||||
|
if int(nread) != len(fh.content) {
|
||||||
|
log.Error("FUSE Flush failed during write").Str("path", fh.path).Int("expect", len(fh.content)).Int("got", int(nread)).Send()
|
||||||
|
fp.Close()
|
||||||
|
return syscall.EIO
|
||||||
|
}
|
||||||
|
err = fp.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Flush failed during close").Str("path", fh.path).Err(err).Send()
|
||||||
|
return syscallErr(err)
|
||||||
|
}
|
||||||
|
log.Debug("FUSE Flush done").Str("path", fh.path).Int("size", len(fh.content)).Send()
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.FileFsyncer = (*bytesFileWriteHandle)(nil)
|
||||||
|
|
||||||
|
func (fh *bytesFileWriteHandle) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
|
||||||
|
return fh.Flush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeGetattrer = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (bn *ITNode) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
|
||||||
|
log.Debug("FUSE getattr").Str("path", bn.path).Send()
|
||||||
|
out.Ino = bn.Ino
|
||||||
|
out.Mtime = bn.self.modtime
|
||||||
|
out.Ctime = bn.self.modtime
|
||||||
|
out.Atime = bn.self.modtime
|
||||||
|
out.Size = uint64(bn.self.size)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeSetattrer = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (bn *ITNode) Setattr(ctx context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
|
||||||
|
log.Debug("FUSE setattr").Str("path", bn.path).Send()
|
||||||
|
out.Size = 0
|
||||||
|
out.Mtime = 0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeOpener = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (f *ITNode) Open(ctx context.Context, openFlags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||||
|
switch f.kind {
|
||||||
|
case 2:
|
||||||
|
// FS file
|
||||||
|
if openFlags&syscall.O_RDWR != 0 {
|
||||||
|
log.Error("FUSE Open failed: RDWR").Str("path", f.path).Send()
|
||||||
|
return nil, 0, syscall.EROFS
|
||||||
|
}
|
||||||
|
|
||||||
|
if openFlags&syscall.O_WRONLY != 0 {
|
||||||
|
log.Debug("FUSE Opening for write").Str("path", f.path).Send()
|
||||||
|
fh = &bytesFileWriteHandle{
|
||||||
|
path: f.path,
|
||||||
|
content: make([]byte, 0),
|
||||||
|
}
|
||||||
|
return fh, fuse.FOPEN_DIRECT_IO, 0
|
||||||
|
} else {
|
||||||
|
log.Debug("FUSE Opening for read").Str("path", f.path).Send()
|
||||||
|
fp, err := myfs.Open(f.path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE: Opening failed").Str("path", f.path).Err(err).Send()
|
||||||
|
return nil, 0, syscallErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// For every progress event
|
||||||
|
for sent := range fp.Progress() {
|
||||||
|
log.Debug("FUSE Read progress").Int("bytes", int(sent)).Int("total", int(f.self.size)).Send()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(b, fp)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Read failed").Str("path", f.path).Err(err).Send()
|
||||||
|
fp.Close()
|
||||||
|
return nil, 0, syscallErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = &bytesFileReadHandle{
|
||||||
|
content: b.Bytes(),
|
||||||
|
}
|
||||||
|
return fh, fuse.FOPEN_DIRECT_IO, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
// Device file
|
||||||
|
|
||||||
|
// disallow writes
|
||||||
|
if openFlags&(syscall.O_RDWR|syscall.O_WRONLY) != 0 {
|
||||||
|
return nil, 0, syscall.EROFS
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range properties {
|
||||||
|
if value.Ino == f.Ino {
|
||||||
|
ans, err := value.gen()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, syscallErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = &sensorFileReadHandle{
|
||||||
|
content: ans,
|
||||||
|
}
|
||||||
|
return fh, fuse.FOPEN_DIRECT_IO, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeCreater = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (f *ITNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *fs.Inode, fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||||
|
if f.kind != 2 {
|
||||||
|
return nil, nil, 0, syscall.EROFS
|
||||||
|
}
|
||||||
|
|
||||||
|
path := f.path + "/" + name
|
||||||
|
ino := uint64(len(inodemap)) + 11
|
||||||
|
inodemap[path] = ino
|
||||||
|
|
||||||
|
stable := fs.StableAttr{
|
||||||
|
Mode: fuse.S_IFREG,
|
||||||
|
Ino: ino,
|
||||||
|
}
|
||||||
|
operations := &ITNode{
|
||||||
|
kind: nodeKindFS, Ino: ino,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
node = f.NewInode(ctx, operations, stable)
|
||||||
|
|
||||||
|
fh = &bytesFileWriteHandle{
|
||||||
|
path: path,
|
||||||
|
content: make([]byte, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("FUSE Creating file").Str("path", path).Send()
|
||||||
|
|
||||||
|
errno = 0
|
||||||
|
return node, fh, fuseFlags, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeMkdirer = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (f *ITNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
|
||||||
|
if f.kind != 2 {
|
||||||
|
return nil, syscall.EROFS
|
||||||
|
}
|
||||||
|
|
||||||
|
path := f.path + "/" + name
|
||||||
|
err := myfs.Mkdir(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Mkdir failed").
|
||||||
|
Str("path", path).
|
||||||
|
Err(err).
|
||||||
|
Send()
|
||||||
|
return nil, syscallErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ino := uint64(len(inodemap)) + 11
|
||||||
|
inodemap[path] = ino
|
||||||
|
|
||||||
|
stable := fs.StableAttr{
|
||||||
|
Mode: fuse.S_IFDIR,
|
||||||
|
Ino: ino,
|
||||||
|
}
|
||||||
|
operations := &ITNode{
|
||||||
|
kind: nodeKindFS, Ino: ino,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
node := f.NewInode(ctx, operations, stable)
|
||||||
|
|
||||||
|
log.Debug("FUSE Mkdir success").
|
||||||
|
Str("path", path).
|
||||||
|
Int("ino", int(ino)).
|
||||||
|
Send()
|
||||||
|
return node, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeRenamer = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (f *ITNode) Rename(ctx context.Context, name string, newParent fs.InodeEmbedder, newName string, flags uint32) syscall.Errno {
|
||||||
|
if f.kind != 2 {
|
||||||
|
return syscall.EROFS
|
||||||
|
}
|
||||||
|
|
||||||
|
p1 := f.path + "/" + name
|
||||||
|
p2 := newParent.EmbeddedInode().Path(nil)[2:] + "/" + newName
|
||||||
|
|
||||||
|
err := myfs.Rename(p1, p2)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Rename failed").
|
||||||
|
Str("src", p1).
|
||||||
|
Str("dest", p2).
|
||||||
|
Err(err).
|
||||||
|
Send()
|
||||||
|
|
||||||
|
return syscallErr(err)
|
||||||
|
}
|
||||||
|
log.Debug("FUSE Rename sucess").
|
||||||
|
Str("src", p1).
|
||||||
|
Str("dest", p2).
|
||||||
|
Send()
|
||||||
|
|
||||||
|
ino := inodemap[p1]
|
||||||
|
delete(inodemap, p1)
|
||||||
|
inodemap[p2] = ino
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeUnlinker = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (f *ITNode) Unlink(ctx context.Context, name string) syscall.Errno {
|
||||||
|
if f.kind != 2 {
|
||||||
|
return syscall.EROFS
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(inodemap, f.path+"/"+name)
|
||||||
|
err := myfs.Remove(f.path + "/" + name)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FUSE Unlink failed").
|
||||||
|
Str("file", f.path+"/"+name).
|
||||||
|
Err(err).
|
||||||
|
Send()
|
||||||
|
|
||||||
|
return syscallErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("FUSE Unlink success").
|
||||||
|
Str("file", f.path+"/"+name).
|
||||||
|
Send()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeRmdirer = (*ITNode)(nil)
|
||||||
|
|
||||||
|
func (f *ITNode) Rmdir(ctx context.Context, name string) syscall.Errno {
|
||||||
|
return f.Unlink(ctx, name)
|
||||||
|
}
|
74
internal/fusefs/syscallerr.go
Normal file
74
internal/fusefs/syscallerr.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package fusefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"go.arsenm.dev/infinitime/blefs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func syscallErr(err error) syscall.Errno {
|
||||||
|
if err == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
switch err := err.(type) {
|
||||||
|
case blefs.FSError:
|
||||||
|
switch err.Code {
|
||||||
|
case 0x02: // filesystem error
|
||||||
|
return syscall.EIO // TODO
|
||||||
|
case 0x05: // read-only filesystem
|
||||||
|
return syscall.EROFS
|
||||||
|
case 0x03: // no such file
|
||||||
|
return syscall.ENOENT
|
||||||
|
case 0x04: // protocol error
|
||||||
|
return syscall.EPROTO
|
||||||
|
case -5: // input/output error
|
||||||
|
return syscall.EIO
|
||||||
|
case -84: // filesystem is corrupted
|
||||||
|
return syscall.ENOTRECOVERABLE // TODO
|
||||||
|
case -2: // no such directory entry
|
||||||
|
return syscall.ENOENT
|
||||||
|
case -17: // entry already exists
|
||||||
|
return syscall.EEXIST
|
||||||
|
case -20: // entry is not a directory
|
||||||
|
return syscall.ENOTDIR
|
||||||
|
case -39: // directory is not empty
|
||||||
|
return syscall.ENOTEMPTY
|
||||||
|
case -9: // bad file number
|
||||||
|
return syscall.EBADF
|
||||||
|
case -27: // file is too large
|
||||||
|
return syscall.EFBIG
|
||||||
|
case -22: // invalid parameter
|
||||||
|
return syscall.EINVAL
|
||||||
|
case -28: // no space left on device
|
||||||
|
return syscall.ENOSPC
|
||||||
|
case -12: // no more memory available
|
||||||
|
return syscall.ENOMEM
|
||||||
|
case -61: // no attr available
|
||||||
|
return syscall.ENODATA // TODO
|
||||||
|
case -36: // file name is too long
|
||||||
|
return syscall.ENAMETOOLONG
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
switch err {
|
||||||
|
case blefs.ErrFileNotExists: // file does not exist
|
||||||
|
return syscall.ENOENT
|
||||||
|
case blefs.ErrFileReadOnly: // file is read only
|
||||||
|
return syscall.EACCES
|
||||||
|
case blefs.ErrFileWriteOnly: // file is write only
|
||||||
|
return syscall.EACCES
|
||||||
|
case blefs.ErrInvalidOffset: // invalid file offset
|
||||||
|
return syscall.EINVAL
|
||||||
|
case blefs.ErrOffsetChanged: // offset has already been changed
|
||||||
|
return syscall.ESPIPE
|
||||||
|
case blefs.ErrReadOpen: // only one file can be opened for reading at a time
|
||||||
|
return syscall.ENFILE
|
||||||
|
case blefs.ErrWriteOpen: // only one file can be opened for writing at a time
|
||||||
|
return syscall.ENFILE
|
||||||
|
case blefs.ErrNoRemoveRoot: // refusing to remove root directory
|
||||||
|
return syscall.EPERM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.EIO
|
||||||
|
}
|
17
internal/fusefs/unmount.go
Normal file
17
internal/fusefs/unmount.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package fusefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/hanwen/go-fuse/v2/fuse"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Unmount(mountPoint string) error {
|
||||||
|
return unmount(mountPoint, &fuse.MountOptions{DirectMount: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately, the FUSE library does not export its unmount function,
|
||||||
|
// so this is required until that changes
|
||||||
|
//
|
||||||
|
//go:linkname unmount github.com/hanwen/go-fuse/v2/fuse.unmount
|
||||||
|
func unmount(mountPoint string, opts *fuse.MountOptions) error
|
77
main.go
77
main.go
@ -26,6 +26,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -74,21 +76,7 @@ func main() {
|
|||||||
LogLevel: level,
|
LogLevel: level,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx := context.Background()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
|
||||||
go func() {
|
|
||||||
<-sigCh
|
|
||||||
cancel()
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
os.Exit(0)
|
|
||||||
}()
|
|
||||||
signal.Notify(
|
|
||||||
sigCh,
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Connect to InfiniTime with default options
|
// Connect to InfiniTime with default options
|
||||||
dev, err := infinitime.Connect(ctx, opts)
|
dev, err := infinitime.Connect(ctx, opts)
|
||||||
@ -145,49 +133,88 @@ func main() {
|
|||||||
log.Error("Error setting current time on connected InfiniTime").Err(err).Send()
|
log.Error("Error setting current time on connected InfiniTime").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sig := <-sigCh
|
||||||
|
log.Warn("Signal received, shutting down").Stringer("signal", sig).Send()
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg := WaitGroup{&sync.WaitGroup{}}
|
||||||
|
|
||||||
// Initialize music controls
|
// Initialize music controls
|
||||||
err = initMusicCtrl(ctx, dev)
|
err = initMusicCtrl(ctx, wg, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error initializing music control").Err(err).Send()
|
log.Error("Error initializing music control").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start control socket
|
// Start control socket
|
||||||
err = initCallNotifs(ctx, dev)
|
err = initCallNotifs(ctx, wg, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error initializing call notifications").Err(err).Send()
|
log.Error("Error initializing call notifications").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize notification relay
|
// Initialize notification relay
|
||||||
err = initNotifRelay(ctx, dev)
|
err = initNotifRelay(ctx, wg, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error initializing notification relay").Err(err).Send()
|
log.Error("Error initializing notification relay").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializa weather
|
// Initializa weather
|
||||||
err = initWeather(ctx, dev)
|
err = initWeather(ctx, wg, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error initializing weather").Err(err).Send()
|
log.Error("Error initializing weather").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize metrics collection
|
// Initialize metrics collection
|
||||||
err = initMetrics(ctx, dev)
|
err = initMetrics(ctx, wg, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error intializing metrics collection").Err(err).Send()
|
log.Error("Error intializing metrics collection").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize metrics collection
|
// Initialize puremaps integration
|
||||||
err = initPureMaps(ctx, dev)
|
err = initPureMaps(ctx, wg, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error intializing puremaps integration").Err(err).Send()
|
log.Error("Error intializing puremaps integration").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start fuse socket
|
||||||
|
if k.Bool("fuse.enabled") {
|
||||||
|
err = startFUSE(ctx, wg, dev)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error starting fuse socket").Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start control socket
|
// Start control socket
|
||||||
err = startSocket(ctx, dev)
|
err = startSocket(ctx, wg, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error starting socket").Err(err).Send()
|
log.Error("Error starting socket").Err(err).Send()
|
||||||
}
|
}
|
||||||
// Block forever
|
|
||||||
select {}
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
type x struct {
|
||||||
|
n int
|
||||||
|
*sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xy *x) Add(i int) {
|
||||||
|
xy.n += i
|
||||||
|
xy.WaitGroup.Add(i)
|
||||||
|
fmt.Println("add: counter:", xy.n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xy *x) Done() {
|
||||||
|
xy.n -= 1
|
||||||
|
xy.WaitGroup.Done()
|
||||||
|
fmt.Println("done: counter:", xy.n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func onReqPasskey() (uint32, error) {
|
func onReqPasskey() (uint32, error) {
|
||||||
|
5
maps.go
5
maps.go
@ -18,7 +18,7 @@ const (
|
|||||||
progressProperty = interfaceName + ".progress"
|
progressProperty = interfaceName + ".progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initPureMaps(ctx context.Context, dev *infinitime.Device) error {
|
func initPureMaps(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
// Connect to session bus. This connection is for method calls.
|
// Connect to session bus. This connection is for method calls.
|
||||||
conn, err := utils.NewSessionBusConn(ctx)
|
conn, err := utils.NewSessionBusConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -59,7 +59,10 @@ func initPureMaps(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done("pureMaps")
|
||||||
|
|
||||||
signalCh := make(chan *dbus.Message, 10)
|
signalCh := make(chan *dbus.Message, 10)
|
||||||
monitorConn.Eavesdrop(signalCh)
|
monitorConn.Eavesdrop(signalCh)
|
||||||
|
|
||||||
|
10
metrics.go
10
metrics.go
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
@ -11,7 +12,7 @@ import (
|
|||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initMetrics(ctx context.Context, dev *infinitime.Device) error {
|
func initMetrics(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
// If metrics disabled, return nil
|
// If metrics disabled, return nil
|
||||||
if !k.Bool("metrics.enabled") {
|
if !k.Bool("metrics.enabled") {
|
||||||
return nil
|
return nil
|
||||||
@ -125,6 +126,13 @@ func initMetrics(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done("metrics")
|
||||||
|
<-ctx.Done()
|
||||||
|
db.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
log.Info("Initialized metrics collection").Send()
|
log.Info("Initialized metrics collection").Send()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
41
music.go
41
music.go
@ -21,13 +21,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
"go.arsenm.dev/itd/mpris"
|
"go.arsenm.dev/itd/mpris"
|
||||||
"go.arsenm.dev/itd/translit"
|
"go.arsenm.dev/itd/translit"
|
||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initMusicCtrl(ctx context.Context, dev *infinitime.Device) error {
|
func initMusicCtrl(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
mpris.Init(ctx)
|
mpris.Init(ctx)
|
||||||
|
|
||||||
maps := k.Strings("notifs.translit.use")
|
maps := k.Strings("notifs.translit.use")
|
||||||
@ -54,23 +55,31 @@ func initMusicCtrl(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done("musicCtrl")
|
||||||
// For every music event received
|
// For every music event received
|
||||||
for musicEvt := range musicEvtCh {
|
for {
|
||||||
// Perform appropriate action based on event
|
select {
|
||||||
switch musicEvt {
|
case musicEvt := <-musicEvtCh:
|
||||||
case infinitime.MusicEventPlay:
|
// Perform appropriate action based on event
|
||||||
mpris.Play()
|
switch musicEvt {
|
||||||
case infinitime.MusicEventPause:
|
case infinitime.MusicEventPlay:
|
||||||
mpris.Pause()
|
mpris.Play()
|
||||||
case infinitime.MusicEventNext:
|
case infinitime.MusicEventPause:
|
||||||
mpris.Next()
|
mpris.Pause()
|
||||||
case infinitime.MusicEventPrev:
|
case infinitime.MusicEventNext:
|
||||||
mpris.Prev()
|
mpris.Next()
|
||||||
case infinitime.MusicEventVolUp:
|
case infinitime.MusicEventPrev:
|
||||||
mpris.VolUp(uint(k.Int("music.vol.interval")))
|
mpris.Prev()
|
||||||
case infinitime.MusicEventVolDown:
|
case infinitime.MusicEventVolUp:
|
||||||
mpris.VolDown(uint(k.Int("music.vol.interval")))
|
mpris.VolUp(uint(k.Int("music.vol.interval")))
|
||||||
|
case infinitime.MusicEventVolDown:
|
||||||
|
mpris.VolDown(uint(k.Int("music.vol.interval")))
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
78
notifs.go
78
notifs.go
@ -29,7 +29,7 @@ import (
|
|||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initNotifRelay(ctx context.Context, dev *infinitime.Device) error {
|
func initNotifRelay(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
// Connect to dbus session bus
|
// Connect to dbus session bus
|
||||||
bus, err := utils.NewSessionBusConn(ctx)
|
bus, err := utils.NewSessionBusConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,43 +54,51 @@ func initNotifRelay(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
// Send events to channel
|
// Send events to channel
|
||||||
bus.Eavesdrop(notifCh)
|
bus.Eavesdrop(notifCh)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done("notifRelay")
|
||||||
// For every event sent to channel
|
// For every event sent to channel
|
||||||
for v := range notifCh {
|
for {
|
||||||
// If firmware is updating, skip
|
select {
|
||||||
if firmwareUpdating {
|
case v := <-notifCh:
|
||||||
continue
|
// If firmware is updating, skip
|
||||||
|
if firmwareUpdating {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If body does not contain 5 elements, skip
|
||||||
|
if len(v.Body) < 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get requred fields
|
||||||
|
sender, summary, body := v.Body[0].(string), v.Body[3].(string), v.Body[4].(string)
|
||||||
|
|
||||||
|
// If fields are ignored in config, skip
|
||||||
|
if ignored(sender, summary, body) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
maps := k.Strings("notifs.translit.use")
|
||||||
|
translit.Transliterators["custom"] = translit.Map(k.Strings("notifs.translit.custom"))
|
||||||
|
sender = translit.Transliterate(sender, maps...)
|
||||||
|
summary = translit.Transliterate(summary, maps...)
|
||||||
|
body = translit.Transliterate(body, maps...)
|
||||||
|
|
||||||
|
var msg string
|
||||||
|
// If summary does not exist, set message to body.
|
||||||
|
// If it does, set message to summary, two newlines, and then body
|
||||||
|
if summary == "" {
|
||||||
|
msg = body
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf("%s\n\n%s", summary, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
dev.Notify(sender, msg)
|
||||||
|
case <-ctx.Done():
|
||||||
|
bus.Close()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If body does not contain 5 elements, skip
|
|
||||||
if len(v.Body) < 5 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get requred fields
|
|
||||||
sender, summary, body := v.Body[0].(string), v.Body[3].(string), v.Body[4].(string)
|
|
||||||
|
|
||||||
// If fields are ignored in config, skip
|
|
||||||
if ignored(sender, summary, body) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
maps := k.Strings("notifs.translit.use")
|
|
||||||
translit.Transliterators["custom"] = translit.Map(k.Strings("notifs.translit.custom"))
|
|
||||||
sender = translit.Transliterate(sender, maps...)
|
|
||||||
summary = translit.Transliterate(summary, maps...)
|
|
||||||
body = translit.Transliterate(body, maps...)
|
|
||||||
|
|
||||||
var msg string
|
|
||||||
// If summary does not exist, set message to body.
|
|
||||||
// If it does, set message to summary, two newlines, and then body
|
|
||||||
if summary == "" {
|
|
||||||
msg = body
|
|
||||||
} else {
|
|
||||||
msg = fmt.Sprintf("%s\n\n%s", summary, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
dev.Notify(sender, msg)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
12
socket.go
12
socket.go
@ -25,6 +25,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.arsenm.dev/drpc/muxserver"
|
"go.arsenm.dev/drpc/muxserver"
|
||||||
@ -41,7 +42,7 @@ var (
|
|||||||
ErrDFUInvalidUpgType = errors.New("invalid upgrade type")
|
ErrDFUInvalidUpgType = errors.New("invalid upgrade type")
|
||||||
)
|
)
|
||||||
|
|
||||||
func startSocket(ctx context.Context, dev *infinitime.Device) error {
|
func startSocket(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
// Make socket directory if non-existant
|
// Make socket directory if non-existant
|
||||||
err := os.MkdirAll(filepath.Dir(k.String("socket.path")), 0o755)
|
err := os.MkdirAll(filepath.Dir(k.String("socket.path")), 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -77,10 +78,13 @@ func startSocket(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go muxserver.New(mux).Serve(ctx, ln)
|
log.Info("Starting control socket").Str("path", k.String("socket.path")).Send()
|
||||||
|
|
||||||
// Log socket start
|
wg.Add(1)
|
||||||
log.Info("Started control socket").Str("path", k.String("socket.path")).Send()
|
go func() {
|
||||||
|
defer wg.Done("socket")
|
||||||
|
muxserver.New(mux).Serve(ctx, ln)
|
||||||
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
16
waitgroup.go
Normal file
16
waitgroup.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WaitGroup struct {
|
||||||
|
*sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wg WaitGroup) Done(c string) {
|
||||||
|
log.Info("Component stopped").Str("name", c).Send()
|
||||||
|
wg.WaitGroup.Done()
|
||||||
|
}
|
21
weather.go
21
weather.go
@ -9,6 +9,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
@ -61,7 +62,14 @@ type OSMData []struct {
|
|||||||
|
|
||||||
var sendWeatherCh = make(chan struct{}, 1)
|
var sendWeatherCh = make(chan struct{}, 1)
|
||||||
|
|
||||||
func initWeather(ctx context.Context, dev *infinitime.Device) error {
|
func sleepCtx(ctx context.Context, d time.Duration) {
|
||||||
|
select {
|
||||||
|
case <-time.After(d):
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initWeather(ctx context.Context, wg WaitGroup, dev *infinitime.Device) error {
|
||||||
if !k.Bool("weather.enabled") {
|
if !k.Bool("weather.enabled") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -74,14 +82,21 @@ func initWeather(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
|
|
||||||
timer := time.NewTimer(time.Hour)
|
timer := time.NewTimer(time.Hour)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done("weather")
|
||||||
for {
|
for {
|
||||||
|
_, ok := <-ctx.Done()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to get weather
|
// Attempt to get weather
|
||||||
data, err := getWeather(ctx, lat, lon)
|
data, err := getWeather(ctx, lat, lon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Error getting weather data").Err(err).Send()
|
log.Warn("Error getting weather data").Err(err).Send()
|
||||||
// Wait 15 minutes before retrying
|
// Wait 15 minutes before retrying
|
||||||
time.Sleep(15 * time.Minute)
|
sleepCtx(ctx, 15*time.Minute)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +189,8 @@ func initWeather(ctx context.Context, dev *infinitime.Device) error {
|
|||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
case <-sendWeatherCh:
|
case <-sendWeatherCh:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
Loading…
Reference in New Issue
Block a user