Working communication code using socket
Sockets are sooo much better, waow I like easy code Closes #11 (even if I didn't implement all the proposal) Closes #16
This commit is contained in:
parent
4f3d3952f0
commit
1d95c7bef6
72
app/cli.go
72
app/cli.go
@ -2,13 +2,10 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -18,7 +15,6 @@ const (
|
||||
|
||||
type Request struct {
|
||||
Request int
|
||||
Id int
|
||||
Pattern string
|
||||
}
|
||||
|
||||
@ -27,65 +23,27 @@ type Response struct {
|
||||
Actions ReadableMap
|
||||
}
|
||||
|
||||
// Runtime files:
|
||||
// /run/user/<uid>/reaction/reaction.pipe
|
||||
// /run/user/<uid>/reaction/id.response
|
||||
|
||||
func RuntimeDirectory() string {
|
||||
return fmt.Sprintf("/run/user/%v/reaction/", os.Getuid())
|
||||
}
|
||||
|
||||
func PipePath() string {
|
||||
return path.Join(RuntimeDirectory(), "reaction.pipe")
|
||||
}
|
||||
|
||||
func (r Request) ResponsePath() string {
|
||||
return path.Join(RuntimeDirectory(), string(r.Id))
|
||||
}
|
||||
|
||||
func Send(data Request) {
|
||||
pipePath := PipePath()
|
||||
pipe, err := os.OpenFile(pipePath, os.O_RDWR, os.ModeNamedPipe)
|
||||
if err != nil {
|
||||
log.Println("Failed to open", pipePath, ":", err)
|
||||
log.Fatalln("Is the reaction daemon running? Does the CLI run as the same user?")
|
||||
}
|
||||
log.Println("DEBUG opening ok, encoding...")
|
||||
enc := gob.NewEncoder(pipe)
|
||||
err = enc.Encode(data)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write to %s: %s", pipePath, err)
|
||||
}
|
||||
func SocketPath() string {
|
||||
return fmt.Sprintf("/run/user/%v/reaction.sock", os.Getuid())
|
||||
}
|
||||
|
||||
func SendAndRetrieve(data Request) Response {
|
||||
if data.Id == 0 {
|
||||
data.Id = rand.Int()
|
||||
}
|
||||
log.Println("DEBUG sending:", data)
|
||||
Send(data)
|
||||
responsePath := data.ResponsePath()
|
||||
d, _ := time.ParseDuration("100ms")
|
||||
for tries := 20; tries > 0; tries-- {
|
||||
log.Println("DEBUG waiting for answer...")
|
||||
file, err := os.Open(responsePath)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
time.Sleep(d)
|
||||
continue
|
||||
}
|
||||
defer os.Remove(responsePath)
|
||||
conn, err := net.Dial("unix", SocketPath())
|
||||
if err != nil {
|
||||
log.Fatalf("Error opening daemon answer: %s", err)
|
||||
log.Fatalln("Error opening connection top daemon:", err)
|
||||
}
|
||||
|
||||
err = gob.NewEncoder(conn).Encode(data)
|
||||
if err != nil {
|
||||
log.Fatalln("Can't send message:", err)
|
||||
}
|
||||
|
||||
var response Response
|
||||
err = gob.NewDecoder(file).Decode(&response)
|
||||
err = gob.NewDecoder(conn).Decode(&response)
|
||||
if err != nil {
|
||||
log.Fatalf("Error parsing daemon answer: %s", err)
|
||||
log.Fatalln("Invalid answer from daemon:", err)
|
||||
}
|
||||
return response
|
||||
}
|
||||
log.Fatalln("Timeout while waiting answer from the daemon")
|
||||
return Response{errors.New("unreachable code"), nil}
|
||||
}
|
||||
|
||||
func usage(err string) {
|
||||
@ -96,7 +54,7 @@ func usage(err string) {
|
||||
|
||||
func CLI() {
|
||||
if len(os.Args) <= 1 {
|
||||
response := SendAndRetrieve(Request{Query, 0, ""})
|
||||
response := SendAndRetrieve(Request{Query, ""})
|
||||
if response.Err != nil {
|
||||
log.Fatalln("Received error from daemon:", response.Err)
|
||||
}
|
||||
@ -108,7 +66,7 @@ func CLI() {
|
||||
if len(os.Args) != 3 {
|
||||
usage("flush takes one <PATTERN> argument")
|
||||
}
|
||||
response := SendAndRetrieve(Request{Flush, 0, os.Args[2]})
|
||||
response := SendAndRetrieve(Request{Flush, os.Args[2]})
|
||||
if response.Err != nil {
|
||||
log.Fatalln("Received error from daemon:", response.Err)
|
||||
}
|
||||
|
90
app/pipe.go
90
app/pipe.go
@ -2,12 +2,10 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@ -109,78 +107,60 @@ func (r ReadableMap) ToString() string {
|
||||
return string(text)
|
||||
}
|
||||
|
||||
// Pipe-related, server-related functions
|
||||
// Socket-related, server-related functions
|
||||
|
||||
func createOpenPipe() *os.File {
|
||||
err := os.Mkdir(RuntimeDirectory(), 0755)
|
||||
if err != nil && !errors.Is(err, os.ErrExist) {
|
||||
log.Fatalln("FATAL Failed to create runtime directory", err)
|
||||
}
|
||||
pipePath := PipePath()
|
||||
_, err = os.Stat(pipePath)
|
||||
func createOpenSocket() net.Listener {
|
||||
socketPath := SocketPath()
|
||||
_, err := os.Stat(socketPath)
|
||||
if err == nil {
|
||||
log.Println("WARN Runtime file", pipePath, "already exists: Is the daemon already running? Deleting.")
|
||||
err = os.Remove(pipePath)
|
||||
log.Println("WARN socket", socketPath, "already exists: Is the daemon already running? Deleting.")
|
||||
err = os.Remove(socketPath)
|
||||
if err != nil {
|
||||
log.Println("FATAL Failed to remove runtime file:", err)
|
||||
log.Println("FATAL Failed to remove socket:", err)
|
||||
}
|
||||
}
|
||||
err = syscall.Mkfifo(pipePath, 0600)
|
||||
ln, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
log.Println("FATAL Failed to create runtime file:", err)
|
||||
}
|
||||
file, err := os.OpenFile(pipePath, os.O_RDONLY, os.ModeNamedPipe)
|
||||
if err != nil {
|
||||
log.Println("FATAL Failed to open runtime file:", err)
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
func Respond(request Request, response Response) {
|
||||
file, err := os.Create(request.ResponsePath())
|
||||
if err != nil {
|
||||
log.Println("WARN Can't respond to message:", err)
|
||||
return
|
||||
}
|
||||
err = gob.NewEncoder(file).Encode(response)
|
||||
if err != nil {
|
||||
log.Println("WARN Can't respond to message:", err)
|
||||
return
|
||||
log.Println("FATAL Failed to create socket:", err)
|
||||
}
|
||||
return ln
|
||||
}
|
||||
|
||||
// Handle connections
|
||||
func Serve() {
|
||||
pipe := createOpenPipe()
|
||||
ln := createOpenSocket()
|
||||
defer ln.Close()
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
log.Println("ERROR Failed to open connection from cli:", err)
|
||||
continue
|
||||
}
|
||||
go func(conn net.Conn) {
|
||||
var request Request
|
||||
err := gob.NewDecoder(pipe).Decode(&request)
|
||||
if err != nil {
|
||||
d, _ := time.ParseDuration("1s")
|
||||
if err.Error() == "EOF" {
|
||||
log.Println("DEBUG received EOF, seeking one byte")
|
||||
_, err = pipe.Seek(1, 1)
|
||||
if err != nil {
|
||||
log.Println("DEBUG failed to seek:", err)
|
||||
}
|
||||
time.Sleep(d)
|
||||
continue
|
||||
}
|
||||
log.Println("WARN Invalid Message received:", err)
|
||||
time.Sleep(d)
|
||||
continue
|
||||
}
|
||||
go func(request Request) {
|
||||
var response Response
|
||||
|
||||
err := gob.NewDecoder(conn).Decode(&request)
|
||||
if err != nil {
|
||||
log.Println("ERROR Invalid Message from cli:", err)
|
||||
return
|
||||
}
|
||||
|
||||
switch request.Request {
|
||||
case Query:
|
||||
response.Actions = actionStore.store.ToReadable()
|
||||
case Flush:
|
||||
actionStore.Flush(request.Pattern)
|
||||
default:
|
||||
log.Println("WARN Invalid Message: unrecognised Request type")
|
||||
log.Println("ERROR Invalid Message from cli: unrecognised Request type")
|
||||
return
|
||||
}
|
||||
Respond(request, response)
|
||||
}(request)
|
||||
|
||||
gob.NewEncoder(conn).Encode(response)
|
||||
if err != nil {
|
||||
log.Println("ERROR Can't respond to cli:", err)
|
||||
return
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ func cmdStdout(commandline []string) chan *string {
|
||||
cmd := exec.Command(commandline[0], commandline[1:]...)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Fatalln("couldn't open stdout on command:", err)
|
||||
log.Fatalln("FATAL couldn't open stdout on command:", err)
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalln("couldn't start command:", err)
|
||||
log.Fatalln("FATAL couldn't start command:", err)
|
||||
}
|
||||
defer stdout.Close()
|
||||
|
||||
@ -221,7 +221,7 @@ func Main() {
|
||||
quit()
|
||||
}
|
||||
case <-sigs:
|
||||
log.Printf("Received SIGINT or SIGTERM, exiting")
|
||||
log.Printf("INFO Received SIGINT/SIGTERM, exiting")
|
||||
quit()
|
||||
}
|
||||
}
|
||||
@ -235,7 +235,7 @@ func quit() {
|
||||
// wait for them to complete
|
||||
wgActions.Wait()
|
||||
// delete pipe
|
||||
os.Remove(PipePath())
|
||||
os.Remove(SocketPath())
|
||||
|
||||
os.Exit(3)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user