Initial commit

This commit is contained in:
Maciej Krzyżanowski 2024-12-14 17:35:23 +01:00
commit 9ac048861f
7 changed files with 302 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}