Initial commit
This commit is contained in:
commit
9ac048861f
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
9
.idea/daemonSocketExample.iml
generated
Normal file
9
.idea/daemonSocketExample.iml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/daemonSocketExample.iml" filepath="$PROJECT_DIR$/.idea/daemonSocketExample.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module daemonSocketExample
|
||||||
|
|
||||||
|
go 1.23.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||||
|
github.com/sevlyar/go-daemon v0.1.6 // indirect
|
||||||
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
|
)
|
6
go.sum
Normal file
6
go.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||||
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
|
github.com/sevlyar/go-daemon v0.1.6 h1:EUh1MDjEM4BI109Jign0EaknA2izkOyi0LV3ro3QQGs=
|
||||||
|
github.com/sevlyar/go-daemon v0.1.6/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
||||||
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
5
liberum-daemon.log
Normal file
5
liberum-daemon.log
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
2024/12/14 17:34:21 Daemon started
|
||||||
|
2024/12/14 17:34:22 INFO Got echo request EchoByte=123
|
||||||
|
2024/12/14 17:34:53 Daemon started
|
||||||
|
2024/12/14 17:34:54 INFO Got echo request EchoByte=123
|
||||||
|
2024/12/14 17:34:59 INFO Stopping daemon
|
257
main.go
Normal file
257
main.go
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"github.com/sevlyar/go-daemon"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Message interface {
|
||||||
|
ID() uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
EchoRequestID = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
type EchoRequest struct {
|
||||||
|
EchoByte byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (EchoRequest) ID() uint32 {
|
||||||
|
return EchoRequestID
|
||||||
|
}
|
||||||
|
|
||||||
|
func unixSocketListen() error {
|
||||||
|
listener, err := net.Listen("unix", "/tmp/liberum.sock")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for conn, err := listener.Accept(); err == nil; {
|
||||||
|
handleConnection(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConnection(conn net.Conn) {
|
||||||
|
msgChan := readSocketMessages(conn)
|
||||||
|
|
||||||
|
for msg := range msgChan {
|
||||||
|
handleMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSocketMessages(conn net.Conn) chan Message {
|
||||||
|
msgChan := make(chan Message, 64)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
msgBytes, err := readMessage(conn)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error reading message", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := decodeMessage(msgBytes)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error parsing message", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgChan <- msg
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return msgChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMessage(msgBytes []byte) (Message, error) {
|
||||||
|
if len(msgBytes) < 4 {
|
||||||
|
return nil, errors.New("message too short to have type ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
msgID := binary.LittleEndian.Uint32(msgBytes[0:4])
|
||||||
|
msgRest := msgBytes[4:]
|
||||||
|
var err error
|
||||||
|
var msg Message
|
||||||
|
|
||||||
|
switch msgID {
|
||||||
|
case EchoRequestID:
|
||||||
|
var echoReq EchoRequest
|
||||||
|
err = json.Unmarshal(msgRest, &echoReq)
|
||||||
|
msg = echoReq
|
||||||
|
default:
|
||||||
|
err = errors.New("unknown message type ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readMessage(conn net.Conn) ([]byte, error) {
|
||||||
|
msgLenBuf := make([]byte, 4)
|
||||||
|
n, err := io.ReadFull(conn, msgLenBuf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != 4 {
|
||||||
|
return nil, errors.New("could not read message length")
|
||||||
|
}
|
||||||
|
|
||||||
|
msgLen := binary.LittleEndian.Uint32(msgLenBuf)
|
||||||
|
msgContent := make([]byte, msgLen)
|
||||||
|
n, err = io.ReadFull(conn, msgContent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint32(n) != msgLen {
|
||||||
|
return nil, errors.New("could not read full message")
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgContent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMessage(msg Message) {
|
||||||
|
switch msg.ID() {
|
||||||
|
case EchoRequestID:
|
||||||
|
handleEchoRequest(msg.(EchoRequest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleEchoRequest(req EchoRequest) {
|
||||||
|
slog.Info("Got echo request", "EchoByte", req.EchoByte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMessage(conn net.Conn, msg Message) error {
|
||||||
|
msgBytes, err := encodeMessage(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := conn.Write(msgBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(msgBytes) {
|
||||||
|
return errors.New("could not write full message")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMessage(msg Message) ([]byte, error) {
|
||||||
|
var msgBytes []byte
|
||||||
|
var msgJsonBytes []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch msg.ID() {
|
||||||
|
case EchoRequestID:
|
||||||
|
msgJsonBytes, err = json.Marshal(msg.(EchoRequest))
|
||||||
|
default:
|
||||||
|
err = errors.New("unknown message type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// +4 for type field length
|
||||||
|
msgLen := len(msgJsonBytes) + 4
|
||||||
|
msgLenBytes := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(msgLenBytes, uint32(msgLen))
|
||||||
|
msgBytes = append(msgBytes, msgLenBytes...)
|
||||||
|
|
||||||
|
msgTypeBytes := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(msgTypeBytes, msg.ID())
|
||||||
|
msgBytes = append(msgBytes, msgTypeBytes...)
|
||||||
|
|
||||||
|
msgBytes = append(msgBytes, msgJsonBytes...)
|
||||||
|
|
||||||
|
return msgBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSocketMessages(conn net.Conn, msgChan <-chan Message) error {
|
||||||
|
for msg := range msgChan {
|
||||||
|
err := writeMessage(conn, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unixSocketConnect() (net.Conn, error) {
|
||||||
|
conn, err := net.Dial("unix", "/tmp/liberum.sock")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := &daemon.Context{
|
||||||
|
PidFileName: "liberum-daemon.pid",
|
||||||
|
PidFilePerm: 0644,
|
||||||
|
LogFileName: "liberum-daemon.log",
|
||||||
|
LogFilePerm: 0640,
|
||||||
|
WorkDir: "./",
|
||||||
|
Umask: 027,
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := ctx.Reborn()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func(ctx *daemon.Context) {
|
||||||
|
slog.Info("Stopping daemon")
|
||||||
|
_ = ctx.Release()
|
||||||
|
}(ctx)
|
||||||
|
log.Println("Daemon started")
|
||||||
|
|
||||||
|
_ = os.Remove("/tmp/liberum.sock")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err = unixSocketListen()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error listening unix socket", "error", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
conn, err := unixSocketConnect()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgWriteChan := make(chan Message, 64)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err = writeSocketMessages(conn, msgWriteChan)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
msgWriteChan <- EchoRequest{123}
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user