Skip to content

Remove global clients #1014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
90cce2c
fix
dido18 Jan 20, 2025
c96490b
feat(config): add default configuration file and update config handling
dido18 Jan 21, 2025
7b4859a
fix(.gitignore): add entry to ignore config.ini file
dido18 Jan 21, 2025
279de2a
test(config): add test for writing default config.ini file and update…
dido18 Jan 21, 2025
4f3ac5a
test(config): add tests for retrieving config paths from XDG_CONFIG_H…
dido18 Jan 21, 2025
c41b815
feat(config): pass config path to main loop and update handling
dido18 Jan 22, 2025
55ca9e5
remove global systray
dido18 Jan 22, 2025
f07103e
WIP: remove hub and sh globals
dido18 Jan 22, 2025
ef94d97
remove global clients: use callback to avoid nesting import
dido18 Jan 24, 2025
d2d3aa6
feat(workflow): add Go installation step to release workflow
dido18 Mar 31, 2025
640e644
refactor: rename variable 'h' to 'hub' for clarity in main.go and upd…
dido18 Mar 31, 2025
99f8235
docs: add comments to clarify functions and handlers in config, hub, …
dido18 Mar 31, 2025
5e2161e
refactor: rename 'hub' and 'serialhub' types to 'Hub' and 'Serialhub'…
dido18 Mar 31, 2025
910bac2
refactor: rename 'Hub' and 'Serialhub' types to 'hub' and 'serialhub'…
dido18 Mar 31, 2025
17ed6b3
revert config changes
dido18 Mar 31, 2025
96f0f51
refactor: update logger handling and improve config path management
dido18 Mar 31, 2025
36af1ed
remove globas tools and index
dido18 Mar 31, 2025
f939e32
refactor: enhance logging mechanism by implementing logWriter and upd…
dido18 Apr 1, 2025
e22281f
remove log websocket
dido18 Apr 1, 2025
3bbf371
remove unused global clients comment from hub struct
dido18 Apr 1, 2025
0773f99
refactor: integrate systray into hub structure and update newHub func…
dido18 Apr 1, 2025
9c2ca54
pass tests
dido18 Apr 1, 2025
7e73b6c
Merge branch 'main' into remove-global-clients
dido18 Apr 1, 2025
fc51194
chore: add comment regarding potential removal of sysStray dependencies
dido18 Apr 1, 2025
8106c84
feat: move serial port into new file
dido18 Apr 2, 2025
4dba1a8
feat: add serialhub implementation for managing serial ports
dido18 Apr 2, 2025
7ea21c0
refactor: remove serialhub parameter from newHub function and instant…
dido18 Apr 2, 2025
a152b02
feat: move `spErr` `spClose` `spWrite` into hub (from serialhub)
dido18 Apr 2, 2025
fac0b10
refactor: remove newSerialHub instantiation from hub initialization i…
dido18 Apr 2, 2025
f04d5cc
move `spHandlerOpen` to hub from serialport.go
dido18 Apr 2, 2025
47213d1
refactor: remove unused serial package import from serialport.go
dido18 Apr 2, 2025
14ddd43
renamed h into hub
dido18 Apr 2, 2025
1f9b999
fix: add hub reference to PLogger initialization in uploadHandler
dido18 Apr 2, 2025
4585769
move `spErr, spWrite, spClose spHandlerOpen at the end for redaibility
dido18 Apr 2, 2025
283064c
refactor: clean up serialport.go and serialportlist.go for clarity
dido18 Apr 2, 2025
14d72e8
refactor: clarify the impact of removing serialPorts.List() in writer…
dido18 Apr 2, 2025
9e13dcb
move serialportlist inside hub
dido18 Apr 2, 2025
a69c0dc
refactor on callback
dido18 Apr 2, 2025
7e34071
refactor: simplify hub initialization in upload handler tests
dido18 Apr 3, 2025
815a791
refactor: remove unused port listing in checkCmd function
dido18 Apr 3, 2025
9535905
rename into `h` and revert split of serial.go
dido18 Apr 16, 2025
0699e93
refactor: rename hub parameter in send and wsHandler functions for cl…
dido18 Apr 16, 2025
062bca0
refactor: move spHandlerOpen function from serialport.go to hub.go fo…
dido18 Apr 16, 2025
2bcc06d
remove comment
dido18 Apr 16, 2025
4da1624
refactor: remove unused serialPorts variable and instance of serialhu…
dido18 Apr 16, 2025
effe50d
remove commented-out spHandlerClose call in FindPortByName for cleane…
dido18 Apr 16, 2025
d68fc7a
fix: correct GitHub link in comment for clarity
dido18 Apr 16, 2025
fd3b2d7
refactor: replace logWriter with ChanWriter for improved logging mech…
dido18 Apr 16, 2025
0370591
refactor: move spErr, spWrite, and spClose functions from serial.go t…
dido18 Apr 16, 2025
a50aa86
refactor: remove logger from comment
dido18 Apr 16, 2025
65218a6
refactor: replace OnMessage with ChanWriter for improved message hand…
dido18 Apr 16, 2025
9cbe49e
fix tests
dido18 Apr 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/config.ini
Original file line number Diff line number Diff line change
@@ -7,4 +7,4 @@ updateUrl = https://downloads.arduino.cc/
origins = https://local.arduino.cc:8000
#httpProxy = http://your.proxy:port # Proxy server for HTTP requests
crashreport = false # enable crashreport logging
autostartMacOS = true # the Arduino Create Agent is able to start automatically after login on macOS (launchd agent)
autostartMacOS = true # the Arduino Create Agent is able to start automatically after login on macOS (launchd agent)
22 changes: 12 additions & 10 deletions conn.go
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ import (
"os"
"path/filepath"

"github.com/arduino/arduino-create-agent/tools"
"github.com/arduino/arduino-create-agent/upload"
"github.com/arduino/arduino-create-agent/utilities"
"github.com/gin-gonic/gin"
@@ -80,7 +81,7 @@ type Upload struct {

var uploadStatusStr = "ProgrammerStatus"

func uploadHandler(pubKey *rsa.PublicKey) func(*gin.Context) {
func uploadHandler(hub *hub, pubKey *rsa.PublicKey, tools *tools.Tools) func(*gin.Context) {
return func(c *gin.Context) {
data := new(Upload)
if err := c.BindJSON(data); err != nil {
@@ -162,28 +163,28 @@ func uploadHandler(pubKey *rsa.PublicKey) func(*gin.Context) {

go func() {
// Resolve commandline
commandline, err := upload.PartiallyResolve(data.Board, filePath, tmpdir, data.Commandline, data.Extra, Tools)
commandline, err := upload.PartiallyResolve(data.Board, filePath, tmpdir, data.Commandline, data.Extra, tools)
if err != nil {
send(map[string]string{uploadStatusStr: "Error", "Msg": err.Error()})
send(hub, map[string]string{uploadStatusStr: "Error", "Msg": err.Error()})
return
}

l := PLogger{Verbose: true}
l := PLogger{Verbose: true, hub: hub}

// Upload
if data.Extra.Network {
err = errors.New("network upload is not supported anymore, pease use OTA instead")
} else {
send(map[string]string{uploadStatusStr: "Starting", "Cmd": "Serial"})
send(hub, map[string]string{uploadStatusStr: "Starting", "Cmd": "Serial"})
err = upload.Serial(data.Port, commandline, data.Extra, l)
}

// Handle result
if err != nil {
send(map[string]string{uploadStatusStr: "Error", "Msg": err.Error()})
send(hub, map[string]string{uploadStatusStr: "Error", "Msg": err.Error()})
return
}
send(map[string]string{uploadStatusStr: "Done", "Flash": "Ok"})
send(hub, map[string]string{uploadStatusStr: "Done", "Flash": "Ok"})
}()

c.String(http.StatusAccepted, "")
@@ -193,6 +194,7 @@ func uploadHandler(pubKey *rsa.PublicKey) func(*gin.Context) {
// PLogger sends the info from the upload to the websocket
type PLogger struct {
Verbose bool
hub *hub
}

// Debug only sends messages if verbose is true (always true for now)
@@ -206,15 +208,15 @@ func (l PLogger) Debug(args ...interface{}) {
func (l PLogger) Info(args ...interface{}) {
output := fmt.Sprint(args...)
log.Println(output)
send(map[string]string{uploadStatusStr: "Busy", "Msg": output})
send(l.hub, map[string]string{uploadStatusStr: "Busy", "Msg": output})
}

func send(args map[string]string) {
func send(h *hub, args map[string]string) {
mapB, _ := json.Marshal(args)
h.broadcastSys <- mapB
}

func wsHandler() *WsServer {
func wsHandler(h *hub) *WsServer {
server, err := socketio.NewServer(nil)
if err != nil {
log.Fatal(err)
120 changes: 87 additions & 33 deletions hub.go
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@ import (
"strconv"
"strings"

"github.com/arduino/arduino-create-agent/systray"
"github.com/arduino/arduino-create-agent/tools"
"github.com/arduino/arduino-create-agent/upload"
log "github.com/sirupsen/logrus"
)
@@ -45,14 +47,47 @@ type hub struct {

// Unregister requests from connections.
unregister chan *connection

// Serial hub to communicate with serial ports
serialHub *serialhub

serialPortList *SerialPortList

tools *tools.Tools

systray *systray.Systray
}

var h = hub{
broadcast: make(chan []byte, 1000),
broadcastSys: make(chan []byte, 1000),
register: make(chan *connection),
unregister: make(chan *connection),
connections: make(map[*connection]bool),
func newHub(tools *tools.Tools, systray *systray.Systray) *hub {
broadcastSys := make(chan []byte, 1000)

onRegister := func(port *serport) {
broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Got register/open on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + ",\"BufferType\":\"" + port.BufferType + "\"}")
}
onUnregister := func(port *serport) {
broadcastSys <- []byte("{\"Cmd\":\"Close\",\"Desc\":\"Got unregister/close on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + "}")
}
serialHubub := newSerialHub(onRegister, onUnregister)

onList := func(data []byte) {
broadcastSys <- data
}
onErr := func(err string) {
broadcastSys <- []byte("{\"Error\":\"" + err + "\"}")
}
serialPortList := newSerialPortList(tools, onList, onErr)
Comment on lines +64 to +78
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would simplify both of this two with the ChanWriter

Suggested change
onRegister := func(port *serport) {
broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Got register/open on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + ",\"BufferType\":\"" + port.BufferType + "\"}")
}
onUnregister := func(port *serport) {
broadcastSys <- []byte("{\"Cmd\":\"Close\",\"Desc\":\"Got unregister/close on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + "}")
}
serialHubub := newSerialHub(onRegister, onUnregister)
onList := func(data []byte) {
broadcastSys <- data
}
onErr := func(err string) {
broadcastSys <- []byte("{\"Error\":\"" + err + "\"}")
}
serialPortList := newSerialPortList(tools, onList, onErr)
serialHubub := newSerialHub(ChanWriter{Ch:broadcastSys})
serialPortList := newSerialPortList(tools, ChanWriter{Ch:broadcastSys})


return &hub{
broadcast: make(chan []byte, 1000),
broadcastSys: broadcastSys,
register: make(chan *connection),
unregister: make(chan *connection),
connections: make(map[*connection]bool),
serialHub: serialHubub,
serialPortList: serialPortList,
tools: tools,
systray: systray,
}
}

const commands = `{
@@ -95,6 +130,8 @@ func (h *hub) sendToRegisteredConnections(data []byte) {
}

func (h *hub) run() {
go h.serialPortList.Run()

for {
select {
case c := <-h.register:
@@ -108,7 +145,7 @@ func (h *hub) run() {
h.unregisterConnection(c)
case m := <-h.broadcast:
if len(m) > 0 {
checkCmd(m)
h.checkCmd(m)
h.sendToRegisteredConnections(m)
}
case m := <-h.broadcastSys:
@@ -117,7 +154,7 @@ func (h *hub) run() {
}
}

func checkCmd(m []byte) {
func (h *hub) checkCmd(m []byte) {
//log.Print("Inside checkCmd")
s := string(m[:])

@@ -132,18 +169,18 @@ func checkCmd(m []byte) {

args := strings.Split(s, " ")
if len(args) < 3 {
go spErr("You did not specify a port and baud rate in your open cmd")
go h.spErr("You did not specify a port and baud rate in your open cmd")
return
}
if len(args[1]) < 1 {
go spErr("You did not specify a serial port")
go h.spErr("You did not specify a serial port")
return
}

baudStr := strings.Replace(args[2], "\n", "", -1)
baud, err := strconv.Atoi(baudStr)
if err != nil {
go spErr("Problem converting baud rate " + args[2])
go h.spErr("Problem converting baud rate " + args[2])
return
}
// pass in buffer type now as string. if user does not
@@ -154,15 +191,15 @@ func checkCmd(m []byte) {
buftype := strings.Replace(args[3], "\n", "", -1)
bufferAlgorithm = buftype
}
go spHandlerOpen(args[1], baud, bufferAlgorithm)
go h.spHandlerOpen(args[1], baud, bufferAlgorithm)

} else if strings.HasPrefix(sl, "close") {

args := strings.Split(s, " ")
if len(args) > 1 {
go spClose(args[1])
go h.spClose(args[1])
} else {
go spErr("You did not specify a port to close")
go h.spErr("You did not specify a port to close")
}

} else if strings.HasPrefix(sl, "killupload") {
@@ -175,9 +212,9 @@ func checkCmd(m []byte) {

} else if strings.HasPrefix(sl, "send") {
// will catch send and sendnobuf and sendraw
go spWrite(s)
go h.spWrite(s)
} else if strings.HasPrefix(sl, "list") {
go serialPorts.List()
go h.serialPortList.List()
} else if strings.HasPrefix(sl, "downloadtool") {
go func() {
args := strings.Split(s, " ")
@@ -208,7 +245,12 @@ func checkCmd(m []byte) {
behaviour = args[4]
}

err := Tools.Download(pack, tool, toolVersion, behaviour)
reportPendingProgress := func(msg string) {
mapD := map[string]string{"DownloadStatus": "Pending", "Msg": msg}
mapB, _ := json.Marshal(mapD)
h.broadcastSys <- mapB
}
err := h.tools.Download(pack, tool, toolVersion, behaviour, reportPendingProgress)
if err != nil {
mapD := map[string]string{"DownloadStatus": "Error", "Msg": err.Error()}
mapB, _ := json.Marshal(mapD)
@@ -220,29 +262,41 @@ func checkCmd(m []byte) {
}
}()
} else if strings.HasPrefix(sl, "log") {
go logAction(sl)
go h.logAction(sl)
} else if strings.HasPrefix(sl, "restart") {
// potentially, the sysStray dependencies can be removed https://github.com/arduino/arduino-create-agent/issues/1013
log.Println("Received restart from the daemon. Why? Boh")
Systray.Restart()
h.systray.Restart()
} else if strings.HasPrefix(sl, "exit") {
Systray.Quit()
h.systray.Quit()
} else if strings.HasPrefix(sl, "memstats") {
memoryStats()
h.memoryStats()
} else if strings.HasPrefix(sl, "gc") {
garbageCollection()
h.garbageCollection()
} else if strings.HasPrefix(sl, "hostname") {
getHostname()
h.getHostname()
} else if strings.HasPrefix(sl, "version") {
getVersion()
h.getVersion()
} else {
go spErr("Could not understand command.")
go h.spErr("Could not understand command.")
}
}

func logAction(sl string) {
// ChanWriter is a simple io.Writer that sends data to a channel.
type ChanWriter struct {
Ch chan<- []byte
}

func (u *ChanWriter) Write(p []byte) (n int, err error) {
u.Ch <- p
return len(p), nil
}

func (h *hub) logAction(sl string) {
if strings.HasPrefix(sl, "log on") {
*logDump = "on"
multiWriter := io.MultiWriter(&loggerWs, os.Stderr)

multiWriter := io.MultiWriter(&ChanWriter{Ch: h.broadcastSys}, os.Stderr)
log.SetOutput(multiWriter)
} else if strings.HasPrefix(sl, "log off") {
*logDump = "off"
@@ -253,30 +307,30 @@ func logAction(sl string) {
}
}

func memoryStats() {
func (h *hub) memoryStats() {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
json, _ := json.Marshal(memStats)
log.Printf("memStats:%v\n", string(json))
h.broadcastSys <- json
}

func getHostname() {
func (h *hub) getHostname() {
h.broadcastSys <- []byte("{\"Hostname\" : \"" + *hostname + "\"}")
}

func getVersion() {
func (h *hub) getVersion() {
h.broadcastSys <- []byte("{\"Version\" : \"" + version + "\"}")
}

func garbageCollection() {
func (h *hub) garbageCollection() {
log.Printf("Starting garbageCollection()\n")
h.broadcastSys <- []byte("{\"gc\":\"starting\"}")
memoryStats()
h.memoryStats()
debug.SetGCPercent(100)
debug.FreeOSMemory()
debug.SetGCPercent(-1)
log.Printf("Done with garbageCollection()\n")
h.broadcastSys <- []byte("{\"gc\":\"done\"}")
memoryStats()
h.memoryStats()
}
23 changes: 13 additions & 10 deletions info.go
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import (
"runtime"
"strings"

"github.com/arduino/arduino-create-agent/systray"
"github.com/gin-gonic/gin"
"go.bug.st/serial"
)
@@ -40,14 +41,16 @@ func infoHandler(c *gin.Context) {
})
}

func pauseHandler(c *gin.Context) {
go func() {
ports, _ := serial.GetPortsList()
for _, element := range ports {
spClose(element)
}
*hibernate = true
Systray.Pause()
}()
c.JSON(200, nil)
func pauseHandler(hub *hub, s *systray.Systray) func(c *gin.Context) {
return func(c *gin.Context) {
go func() {
ports, _ := serial.GetPortsList()
for _, element := range ports {
hub.spClose(element)
}
*hibernate = true
s.Pause()
}()
c.JSON(200, nil)
}
}
74 changes: 25 additions & 49 deletions main.go
Original file line number Diff line number Diff line change
@@ -20,7 +20,6 @@ package main

import (
_ "embed"
"encoding/json"
"flag"
"html/template"
"io"
@@ -100,22 +99,6 @@ var homeTemplate = template.Must(template.New("home").Parse(homeTemplateHTML))
//go:embed home.html
var homeTemplateHTML string

// global clients
var (
Tools *tools.Tools
Systray systray.Systray
Index *index.Resource
)

type logWriter struct{}

func (u *logWriter) Write(p []byte) (n int, err error) {
h.broadcastSys <- p
return len(p), nil
}

var loggerWs logWriter

func homeHandler(c *gin.Context) {
homeTemplate.Execute(c.Writer, c.Request.Host)
}
@@ -141,12 +124,9 @@ func main() {
// Check if certificates made with Agent <=1.2.7 needs to be moved over the new location
cert.MigrateCertificatesGeneratedWithOldAgentVersions(config.GetCertificatesDir())

// Launch main loop in a goroutine
go loop()

// SetupSystray is the main thread
configDir := config.GetDefaultConfigDir()
Systray = systray.Systray{
stray := systray.Systray{
Hibernate: *hibernate,
Version: version + "-" + commit,
DebugURL: func() string {
@@ -156,16 +136,19 @@ func main() {
ConfigDir: configDir,
}

// Launch main loop in a goroutine
go loop(&stray)

if src, err := os.Executable(); err != nil {
panic(err)
} else if restartPath := updater.Start(src); restartPath != "" {
Systray.RestartWith(restartPath)
stray.RestartWith(restartPath)
} else {
Systray.Start()
stray.Start()
}
}

func loop() {
func loop(stray *systray.Systray) {
if *hibernate {
return
}
@@ -182,11 +165,18 @@ func loop() {
os.Exit(0)
}

logger := func(msg string) {
mapD := map[string]string{"DownloadStatus": "Pending", "Msg": msg}
mapB, _ := json.Marshal(mapD)
h.broadcastSys <- mapB
// Instantiate Index and Tools
index := index.Init(*indexURL, config.GetDataDir())
if signatureKey == nil || len(*signatureKey) == 0 {
log.Panicf("signature public key should be set")
}
signaturePubKey, err := utilities.ParseRsaPublicKey([]byte(*signatureKey))
if err != nil {
log.Panicf("cannot parse signature key '%s'. %s", *signatureKey, err)
}
tools := tools.New(config.GetDataDir(), index, signaturePubKey)

hub := newHub(tools, stray)

// Let's handle the config
configDir := config.GetDefaultConfigDir()
@@ -258,7 +248,7 @@ func loop() {
if err != nil {
log.Panicf("cannot parse arguments: %s", err)
}
Systray.SetCurrentConfigFile(configPath)
stray.SetCurrentConfigFile(configPath)

// Parse additional ini config if defined
if len(*additionalConfig) > 0 {
@@ -278,18 +268,6 @@ func loop() {
}
}

if signatureKey == nil || len(*signatureKey) == 0 {
log.Panicf("signature public key should be set")
}
signaturePubKey, err := utilities.ParseRsaPublicKey([]byte(*signatureKey))
if err != nil {
log.Panicf("cannot parse signature key '%s'. %s", *signatureKey, err)
}

// Instantiate Index and Tools
Index = index.Init(*indexURL, config.GetDataDir())
Tools = tools.New(config.GetDataDir(), Index, logger, signaturePubKey)

// see if we are supposed to wait 5 seconds
if *isLaunchSelf {
launchSelfLater()
@@ -414,16 +392,14 @@ func loop() {
}
}

// launch the discoveries for the running system
go serialPorts.Run()
// launch the hub routine which is the singleton for the websocket server
go h.run()
go hub.run()
// launch our dummy data routine
//go d.run()

r := gin.New()

socketHandler := wsHandler().ServeHTTP
socketHandler := wsHandler(hub).ServeHTTP

extraOrigins := []string{
"https://create.arduino.cc",
@@ -462,17 +438,17 @@ func loop() {
r.LoadHTMLFiles("templates/nofirefox.html")

r.GET("/", homeHandler)
r.POST("/upload", uploadHandler(signaturePubKey))
r.POST("/upload", uploadHandler(hub, signaturePubKey, tools))
r.GET("/socket.io/", socketHandler)
r.POST("/socket.io/", socketHandler)
r.Handle("WS", "/socket.io/", socketHandler)
r.Handle("WSS", "/socket.io/", socketHandler)
r.GET("/info", infoHandler)
r.POST("/pause", pauseHandler)
r.POST("/update", updateHandler)
r.POST("/pause", pauseHandler(hub, stray))
r.POST("/update", updateHandler(stray))

// Mount goa handlers
goa := v2.Server(config.GetDataDir().String(), Index, signaturePubKey)
goa := v2.Server(config.GetDataDir().String(), index, signaturePubKey)
r.Any("/v2/*path", gin.WrapH(goa))

go func() {
36 changes: 27 additions & 9 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -29,9 +29,11 @@ import (
"testing"

"github.com/arduino/arduino-create-agent/config"
"github.com/arduino/arduino-create-agent/gen/tools"
genTools "github.com/arduino/arduino-create-agent/gen/tools"
"github.com/arduino/arduino-create-agent/globals"
"github.com/arduino/arduino-create-agent/index"
"github.com/arduino/arduino-create-agent/systray"
"github.com/arduino/arduino-create-agent/tools"
"github.com/arduino/arduino-create-agent/upload"
"github.com/arduino/arduino-create-agent/utilities"
v2 "github.com/arduino/arduino-create-agent/v2"
@@ -56,7 +58,15 @@ func TestValidSignatureKey(t *testing.T) {

func TestUploadHandlerAgainstEvilFileNames(t *testing.T) {
r := gin.New()
r.POST("/", uploadHandler(utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey))))

index := index.Init(*indexURL, config.GetDataDir())
signaturePubKey, err := utilities.ParseRsaPublicKey([]byte(*signatureKey))
require.NoError(t, err)
tools := tools.New(config.GetDataDir(), index, signaturePubKey)
hub := newHub(tools, &systray.Systray{})
pubkey := utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey))

r.POST("/", uploadHandler(hub, pubkey, tools))
ts := httptest.NewServer(r)

uploadEvilFileName := Upload{
@@ -92,7 +102,15 @@ func TestUploadHandlerAgainstEvilFileNames(t *testing.T) {

func TestUploadHandlerAgainstBase64WithoutPaddingMustFail(t *testing.T) {
r := gin.New()
r.POST("/", uploadHandler(utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey))))

index := index.Init(*indexURL, config.GetDataDir())
signaturePubKey, err := utilities.ParseRsaPublicKey([]byte(*signatureKey))
require.NoError(t, err)
tools := tools.New(config.GetDataDir(), index, signaturePubKey)
hub := newHub(tools, &systray.Systray{})
pubkey := utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey))

r.POST("/", uploadHandler(hub, pubkey, tools))
ts := httptest.NewServer(r)
defer ts.Close()

@@ -126,15 +144,15 @@ func TestInstallToolV2(t *testing.T) {
ts := httptest.NewServer(r)

type test struct {
request tools.ToolPayload
request genTools.ToolPayload
responseCode int
responseBody string
}

bossacURL := "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linux64.tar.gz"
bossacChecksum := "SHA-256:1ae54999c1f97234a5c603eb99ad39313b11746a4ca517269a9285afa05f9100"
bossacSignature := "382898a97b5a86edd74208f10107d2fecbf7059ffe9cc856e045266fb4db4e98802728a0859cfdcda1c0b9075ec01e42dbea1f430b813530d5a6ae1766dfbba64c3e689b59758062dc2ab2e32b2a3491dc2b9a80b9cda4ae514fbe0ec5af210111b6896976053ab76bac55bcecfcececa68adfa3299e3cde6b7f117b3552a7d80ca419374bb497e3c3f12b640cf5b20875416b45e662fc6150b99b178f8e41d6982b4c0a255925ea39773683f9aa9201dc5768b6fc857c87ff602b6a93452a541b8ec10ca07f166e61a9e9d91f0a6090bd2038ed4427af6251039fb9fe8eb62ec30d7b0f3df38bc9de7204dec478fb86f8eb3f71543710790ee169dce039d3e0"
bossacInstallURLOK := tools.ToolPayload{
bossacInstallURLOK := genTools.ToolPayload{
Name: "bossac",
Version: "1.7.0-arduino3",
Packager: "arduino",
@@ -146,7 +164,7 @@ func TestInstallToolV2(t *testing.T) {
esptoolURL := "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-3/x86_64-linux-gnu.esptool-f80ae31.tar.gz"
esptoolChecksum := "SHA-256:bded1dca953377838b6086a9bcd40a1dc5286ba5f69f2372c22a1d1819baad24"
esptoolSignature := "852b58871419ce5e5633ecfaa72c0f0fa890ceb51164b362b8133bc0e3e003a21cec48935b8cdc078f4031219cbf17fb7edd9d7c9ca8ed85492911c9ca6353c9aa4691eb91fda99563a6bd49aeca0d9981fb05ec76e45c6024f8a6822862ad1e34ddc652fbbf4fa909887a255d4f087398ec386577efcec523c21203be3d10fc9e9b0f990a7536875a77dc2bc5cbffea7734b62238e31719111b718bacccebffc9be689545540e81d23b81caa66214376f58a0d6a45cf7efc5d3af62ab932b371628162fffe403906f41d5534921e5be081c5ac2ecc9db5caec03a105cc44b00ce19a95ad079843501eb8182e0717ce327867380c0e39d2b48698547fc1d0d66"
esptoolInstallURLOK := tools.ToolPayload{
esptoolInstallURLOK := genTools.ToolPayload{
Name: "esptool",
Version: "2.5.0-3-20ed2b9",
Packager: "esp8266",
@@ -156,7 +174,7 @@ func TestInstallToolV2(t *testing.T) {
}

wrongSignature := "wr0ngs1gn4tur3"
bossacInstallWrongSig := tools.ToolPayload{
bossacInstallWrongSig := genTools.ToolPayload{
Name: "bossac",
Version: "1.7.0-arduino3",
Packager: "arduino",
@@ -166,7 +184,7 @@ func TestInstallToolV2(t *testing.T) {
}

wrongChecksum := "wr0ngch3cksum"
bossacInstallWrongCheck := tools.ToolPayload{
bossacInstallWrongCheck := genTools.ToolPayload{
Name: "bossac",
Version: "1.7.0-arduino3",
Packager: "arduino",
@@ -175,7 +193,7 @@ func TestInstallToolV2(t *testing.T) {
Signature: &bossacSignature,
}

bossacInstallNoURL := tools.ToolPayload{
bossacInstallNoURL := genTools.ToolPayload{
Name: "bossac",
Version: "1.7.0-arduino3",
Packager: "arduino",
71 changes: 41 additions & 30 deletions serial.go
100755 → 100644
Original file line number Diff line number Diff line change
@@ -20,11 +20,11 @@ package main
import (
"encoding/json"
"slices"
"strconv"
"strings"
"sync"
"time"

"github.com/arduino/arduino-create-agent/tools"
discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
"github.com/sirupsen/logrus"
)
@@ -34,12 +34,27 @@ type serialhub struct {
ports map[*serport]bool

mu sync.Mutex

onRegister func(port *serport)
onUnregister func(port *serport)
}

func newSerialHub(onRegister func(port *serport), onUnregister func(port *serport)) *serialhub {
return &serialhub{
ports: make(map[*serport]bool),
onRegister: onRegister,
onUnregister: onUnregister,
}
}

// SerialPortList is the serial port list
type SerialPortList struct {
Ports []*SpPortItem
portsLock sync.Mutex

tools *tools.Tools `json:"-"`
OnList func([]byte) `json:"-"`
OnErr func(string) `json:"-"`
Comment on lines +56 to +57
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
OnList func([]byte) `json:"-"`
OnErr func(string) `json:"-"`
writer io.Writer `json:"-"`

}

// SpPortItem is the serial port item
@@ -56,27 +71,18 @@ type SpPortItem struct {
ProductID string
}

// serialPorts contains the ports attached to the machine
var serialPorts SerialPortList

var sh = serialhub{
ports: make(map[*serport]bool),
}

// Register serial ports from the connections.
func (sh *serialhub) Register(port *serport) {
sh.mu.Lock()
//log.Print("Registering a port: ", p.portConf.Name)
h.broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Got register/open on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + ",\"BufferType\":\"" + port.BufferType + "\"}")
sh.onRegister(port)
Copy link
Contributor

@lucarin91 lucarin91 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sh.onRegister(port)
fmt.Fprintf(sh.writer, `{"Cmd":"Open","Desc":"Got register/open on port.","Port":%q,"Baud":%d,"BufferType":%q}`, port.portConf.Name , port.portConf.Baud, port.BufferType)

sh.ports[port] = true
sh.mu.Unlock()
}

// Unregister requests from connections.
func (sh *serialhub) Unregister(port *serport) {
sh.mu.Lock()
//log.Print("Unregistering a port: ", p.portConf.Name)
h.broadcastSys <- []byte("{\"Cmd\":\"Close\",\"Desc\":\"Got unregister/close on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + "}")
sh.onUnregister(port)
Copy link
Contributor

@lucarin91 lucarin91 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sh.onUnregister(port)
fmt.Fprintf(sh.writer, `{"Cmd":"Close","Desc":"Got unregister/close on port.","Port":%q,"Baud":%d}`, port.portConf.Name, port.portConf.Baud)

delete(sh.ports, port)
close(port.sendBuffered)
close(port.sendNoBuf)
@@ -90,25 +96,30 @@ func (sh *serialhub) FindPortByName(portname string) (*serport, bool) {
for port := range sh.ports {
if strings.EqualFold(port.portConf.Name, portname) {
// we found our port
//spHandlerClose(port)
return port, true
}
}
return nil, false
}

func newSerialPortList(tools *tools.Tools, onList func(data []byte), onErr func(err string)) *SerialPortList {
return &SerialPortList{
tools: tools,
OnList: onList,
OnErr: onErr,
Comment on lines +108 to +109
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
OnList: onList,
OnErr: onErr,
writer: io.Writer,

}
}

// List broadcasts a Json representation of the ports found
func (sp *SerialPortList) List() {
sp.portsLock.Lock()
ls, err := json.MarshalIndent(sp, "", "\t")
sp.portsLock.Unlock()

if err != nil {
//log.Println(err)
h.broadcastSys <- []byte("Error creating json on port list " +
err.Error())
sp.OnErr("Error creating json on port list " + err.Error())
Copy link
Contributor

@lucarin91 lucarin91 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sp.OnErr("Error creating json on port list " + err.Error())
fmt.Fprintf(sp.writer,"Error creating json on port list %s", err.Error())

} else {
h.broadcastSys <- ls
sp.OnList(ls)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sp.OnList(ls)
sp.Write(ls)

}
}

@@ -125,11 +136,12 @@ func (sp *SerialPortList) Run() {

func (sp *SerialPortList) runSerialDiscovery() {
// First ensure that all the discoveries are available
if err := Tools.Download("builtin", "serial-discovery", "latest", "keep"); err != nil {
noOpProgress := func(msg string) {}
if err := sp.tools.Download("builtin", "serial-discovery", "latest", "keep", noOpProgress); err != nil {
logrus.Errorf("Error downloading serial-discovery: %s", err)
panic(err)
}
sd, err := Tools.GetLocation("serial-discovery")
sd, err := sp.tools.GetLocation("serial-discovery")
if err != nil {
logrus.Errorf("Error downloading serial-discovery: %s", err)
panic(err)
@@ -255,22 +267,21 @@ func (sp *SerialPortList) getPortByName(portname string) *SpPortItem {
return nil
}

func spErr(err string) {
//log.Println("Sending err back: ", err)
//h.broadcastSys <- []byte(err)
// FIXME: the spErr, spWrite, spClose should be moved to the 'hub.go' file
func (h *hub) spErr(err string) {
h.broadcastSys <- []byte("{\"Error\" : \"" + err + "\"}")
}

func spClose(portname string) {
if myport, ok := sh.FindPortByName(portname); ok {
func (h *hub) spClose(portname string) {
if myport, ok := h.serialHub.FindPortByName(portname); ok {
h.broadcastSys <- []byte("Closing serial port " + portname)
myport.Close()
} else {
spErr("We could not find the serial port " + portname + " that you were trying to close.")
h.spErr("We could not find the serial port " + portname + " that you were trying to close.")
}
}

func spWrite(arg string) {
func (h *hub) spWrite(arg string) {
// we will get a string of comXX asdf asdf asdf
//log.Println("Inside spWrite arg: " + arg)
arg = strings.TrimPrefix(arg, " ")
@@ -279,7 +290,7 @@ func spWrite(arg string) {
if len(args) != 3 {
errstr := "Could not parse send command: " + arg
//log.Println(errstr)
spErr(errstr)
h.spErr(errstr)
return
}
bufferingMode := args[0]
@@ -290,10 +301,10 @@ func spWrite(arg string) {
//log.Println("The data is:" + data + "---")

// See if we have this port open
port, ok := sh.FindPortByName(portname)
port, ok := h.serialHub.FindPortByName(portname)
if !ok {
// we couldn't find the port, so send err
spErr("We could not find the serial port " + portname + " that you were trying to write to.")
h.spErr("We could not find the serial port " + portname + " that you were trying to write to.")
return
}

@@ -302,7 +313,7 @@ func spWrite(arg string) {
case "send", "sendnobuf", "sendraw":
// valid buffering mode, go ahead
default:
spErr("Unsupported send command:" + args[0] + ". Please specify a valid one")
h.spErr("Unsupported send command:" + args[0] + ". Please specify a valid one")
return
}

56 changes: 36 additions & 20 deletions serialport.go
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ import (
"unicode/utf8"

log "github.com/sirupsen/logrus"
serial "go.bug.st/serial"
"go.bug.st/serial"
)

// SerialConfig is the serial port configuration
@@ -61,6 +61,9 @@ type serport struct {
BufferType string
//bufferwatcher *BufferflowDummypause
bufferwatcher Bufferflow

ChanWriter ChanWriter
OnClose func(*serport)
}

// SpPortMessage is the serial port message
@@ -89,7 +92,7 @@ func (p *serport) reader(buftype string) {
if p.isClosing.Load() {
strmsg := "Shutting down reader on " + p.portConf.Name
log.Println(strmsg)
h.broadcastSys <- []byte(strmsg)
p.ChanWriter.Write([]byte(strmsg))
break
}

@@ -143,15 +146,14 @@ func (p *serport) reader(buftype string) {
if err == io.EOF || err == io.ErrUnexpectedEOF {
// hit end of file
log.Println("Hit end of file on serial port")
h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
p.ChanWriter.Write([]byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}"))

}

if err != nil {
log.Println(err)
h.broadcastSys <- []byte("Error reading on " + p.portConf.Name + " " +
err.Error() + " Closing port.")
h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
p.ChanWriter.Write([]byte("Error reading on " + p.portConf.Name + " " + err.Error() + " Closing port."))
p.ChanWriter.Write([]byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}"))
p.isClosingDueToError = true
break
}
@@ -209,7 +211,7 @@ func (p *serport) writerBuffered() {
}
msgstr := "writerBuffered just got closed. make sure you make a new one. port:" + p.portConf.Name
log.Println(msgstr)
h.broadcastSys <- []byte(msgstr)
p.ChanWriter.Write([]byte(msgstr))
}

// this method runs as its own thread because it's instantiated
@@ -230,15 +232,22 @@ func (p *serport) writerNoBuf() {
if err != nil {
errstr := "Error writing to " + p.portConf.Name + " " + err.Error() + " Closing port."
log.Print(errstr)
h.broadcastSys <- []byte(errstr)
p.ChanWriter.Write([]byte(errstr))
break
}
}
msgstr := "Shutting down writer on " + p.portConf.Name
log.Println(msgstr)
h.broadcastSys <- []byte(msgstr)
p.ChanWriter.Write([]byte(msgstr))

p.portIo.Close()
serialPorts.List()

// NOTE: by removing the 'serialPorts.List()' line,
// the list of serial ports are NOT sent to the websocket clients after a write is completed.
// This should not be an issue since the list are periodically called.
// Note also that the 'writerBuffered' and 'writerRaw' methods do not call it.
// serialPorts.List()

}

// this method runs as its own thread because it's instantiated
@@ -270,10 +279,11 @@ func (p *serport) writerRaw() {
}
msgstr := "writerRaw just got closed. make sure you make a new one. port:" + p.portConf.Name
log.Println(msgstr)
h.broadcastSys <- []byte(msgstr)
p.ChanWriter.Write([]byte(msgstr))
}

func spHandlerOpen(portname string, baud int, buftype string) {
// FIXME: move this into the `hub.go` file
func (h *hub) spHandlerOpen(portname string, baud int, buftype string) {

log.Print("Inside spHandler")

@@ -312,7 +322,14 @@ func spHandlerOpen(portname string, baud int, buftype string) {
portConf: conf,
portIo: sp,
portName: portname,
BufferType: buftype}
BufferType: buftype,
ChanWriter: ChanWriter{h.broadcastSys},
}

p.OnClose = func(port *serport) {
h.serialPortList.MarkPortAsClosed(p.portName)
h.serialPortList.List()
}

var bw Bufferflow

@@ -330,11 +347,11 @@ func spHandlerOpen(portname string, baud int, buftype string) {
bw.Init()
p.bufferwatcher = bw

sh.Register(p)
defer sh.Unregister(p)
h.serialHub.Register(p)
defer h.serialHub.Unregister(p)

serialPorts.MarkPortAsOpened(portname)
serialPorts.List()
h.serialPortList.MarkPortAsOpened(portname)
h.serialPortList.List()

// this is internally buffered thread to not send to serial port if blocked
go p.writerBuffered()
@@ -345,14 +362,13 @@ func spHandlerOpen(portname string, baud int, buftype string) {

p.reader(buftype)

serialPorts.List()
h.serialPortList.List()
}

func (p *serport) Close() {
p.isClosing.Store(true)

p.bufferwatcher.Close()
p.portIo.Close()
serialPorts.MarkPortAsClosed(p.portName)
serialPorts.List()
p.OnClose(p)
}
14 changes: 7 additions & 7 deletions tools/download.go
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ import (
// If version is not "latest" and behaviour is "replace", it will download the
// version again. If instead behaviour is "keep" it will not download the version
// if it already exists.
func (t *Tools) Download(pack, name, version, behaviour string) error {
func (t *Tools) Download(pack, name, version, behaviour string, report func(msg string)) error {

t.tools.SetBehaviour(behaviour)
_, err := t.tools.Install(context.Background(), &tools.ToolPayload{Name: name, Version: version, Packager: pack})
@@ -58,24 +58,24 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {

// if the tool contains a post_install script, run it: it means it is a tool that needs to install drivers
// AFAIK this is only the case for the windows-driver tool
err = t.installDrivers(safePath)
err = t.installDrivers(safePath, report)
if err != nil {
return err
}

// Ensure that the files are executable
t.logger("Ensure that the files are executable")
report("Ensure that the files are executable")

// Update the tool map
t.logger("Updating map with location " + safePath)
report("Updating map with location " + safePath)

t.setMapValue(name, safePath)
t.setMapValue(name+"-"+version, safePath)

return nil
}

func (t *Tools) installDrivers(location string) error {
func (t *Tools) installDrivers(location string, report func(msg string)) error {
OkPressed := 6
extension := ".bat"
// add .\ to force locality
@@ -86,11 +86,11 @@ func (t *Tools) installDrivers(location string) error {
preamble = "./"
}
if _, err := os.Stat(filepath.Join(location, "post_install"+extension)); err == nil {
t.logger("Installing drivers")
report("Installing drivers")
ok := MessageBox("Installing drivers", "We are about to install some drivers needed to use Arduino/Genuino boards\nDo you want to continue?")
if ok == OkPressed {
os.Chdir(location)
t.logger(preamble + "post_install" + extension)
report(preamble + "post_install" + extension)
oscmd := exec.Command(preamble + "post_install" + extension)
if runtime.GOOS != "linux" {
// spawning a shell could be the only way to let the user type his password
8 changes: 4 additions & 4 deletions tools/download_test.go
Original file line number Diff line number Diff line change
@@ -130,12 +130,12 @@ func TestDownload(t *testing.T) {
IndexFile: *paths.New("testdata", "test_tool_index.json"),
LastRefresh: time.Now(),
}
testTools := New(tempDirPath, &testIndex, func(msg string) { t.Log(msg) }, utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey)))
testTools := New(tempDirPath, &testIndex, utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey)))

for _, tc := range testCases {
t.Run(tc.name+"-"+tc.version, func(t *testing.T) {
// Download the tool
err := testTools.Download("arduino-test", tc.name, tc.version, "replace")
err := testTools.Download("arduino-test", tc.name, tc.version, "replace", func(msg string) { t.Log(msg) })
require.NoError(t, err)

// Check that the tool has been downloaded
@@ -177,8 +177,8 @@ func TestCorruptedInstalled(t *testing.T) {
defer fileJSON.Close()
_, err = fileJSON.Write([]byte("Hello"))
require.NoError(t, err)
testTools := New(tempDirPath, &testIndex, func(msg string) { t.Log(msg) }, utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey)))
testTools := New(tempDirPath, &testIndex, utilities.MustParseRsaPublicKey([]byte(globals.ArduinoSignaturePubKey)))
// Download the tool
err = testTools.Download("arduino-test", "avrdude", "6.3.0-arduino17", "keep")
err = testTools.Download("arduino-test", "avrdude", "6.3.0-arduino17", "keep", func(msg string) { t.Log(msg) })
require.NoError(t, err)
}
8 changes: 2 additions & 6 deletions tools/tools.go
Original file line number Diff line number Diff line change
@@ -33,20 +33,18 @@ import (
//
// - *directory* contains the location where the tools are downloaded.
// - *indexURL* contains the url where the tools description is contained.
// - *logger* is a StdLogger used for reporting debug and info messages
// - *installed* contains a map[string]string of the tools installed and their exact location
//
// Usage:
// You have to call the New() function passing it the required parameters:
//
// index = index.Init("https://downloads.arduino.cc/packages/package_index.json", dataDir)
// tools := tools.New(dataDir, index, logger)
// tools := tools.New(dataDir, index)

// Tools will represent the installed tools
type Tools struct {
directory *paths.Path
index *index.Resource
logger func(msg string)
installed map[string]string
mutex sync.RWMutex
tools *pkgs.Tools
@@ -55,12 +53,10 @@ type Tools struct {
// New will return a Tool object, allowing the caller to execute operations on it.
// The New functions accept the directory to use to host the tools,
// an index (used to download the tools),
// and a logger to log the operations
func New(directory *paths.Path, index *index.Resource, logger func(msg string), signPubKey *rsa.PublicKey) *Tools {
func New(directory *paths.Path, index *index.Resource, signPubKey *rsa.PublicKey) *Tools {
t := &Tools{
directory: directory,
index: index,
logger: logger,
installed: map[string]string{},
mutex: sync.RWMutex{},
tools: pkgs.New(index, directory.String(), "replace", signPubKey),
25 changes: 14 additions & 11 deletions update.go
Original file line number Diff line number Diff line change
@@ -30,20 +30,23 @@
package main

import (
"github.com/arduino/arduino-create-agent/systray"
"github.com/arduino/arduino-create-agent/updater"
"github.com/gin-gonic/gin"
)

func updateHandler(c *gin.Context) {
restartPath, err := updater.CheckForUpdates(version, *updateURL, *appName)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"success": "Please wait a moment while the agent reboots itself"})
if restartPath == "quit" {
Systray.Quit()
} else {
Systray.RestartWith(restartPath)
func updateHandler(s *systray.Systray) func(c *gin.Context) {
return func(c *gin.Context) {
restartPath, err := updater.CheckForUpdates(version, *updateURL, *appName)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"success": "Please wait a moment while the agent reboots itself"})
if restartPath == "quit" {
s.Quit()
} else {
s.RestartWith(restartPath)
}
}
}