/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. */ package main import ( "context" "errors" "io" "net" "os" "os/signal" "syscall" "time" "idio.link/go/ipc/internal" ) func main() { log := internal.NewLogger() ctx := context.WithValue(context.Background(), "log", log) ctx, cancel := context.WithCancel(ctx) log.Level(internal.LogLevelDebug) log.Print("establish signal traps") go func() { c := make(chan os.Signal, 1) defer close(c) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) s := <-c log.Print("received %v signal; shutting down", s) cancel() }() log.Print("listen on abstract unix socket idio.link/go/ipc") // see `man 7 unix` for the unix sock abstract namespace abstract := "\000idio.link/go/ipc" addr, err := net.ResolveUnixAddr("unixpacket", abstract) if err != nil { panic(err) } listener, err := net.ListenUnix("unixpacket", addr) if err != nil { panic(err) } // TODO move to goroutine and supervise with restart acceptIPCConnections(ctx, listener) log.Print("exiting") } func acceptIPCConnections( ctx context.Context, l *net.UnixListener, ) { log := ctx.Value("log").(*internal.Logger) log.Print("accepting connections...") // TODO should info bypass the base msg? log.Push("accept ipc connections") defer func() { if err := l.Close(); err != nil { log.Error("failed to close listener: %v", err) } }() for ctx.Err() == nil { // TODO move config into server struct err := l.SetDeadline(time.Now().Add(100 * time.Millisecond)) if err != nil { log.Error("set accept deadline failed: %v", err) continue } conn, err := l.Accept() if err != nil { if !errors.Is(err, os.ErrDeadlineExceeded) { log.Error("listener accept failed: %+v", err) } continue } go handleIPCConnection(ctx, conn) } return } func handleIPCConnection( ctx context.Context, conn net.Conn, ) { log := ctx.Value("log").(*internal.Logger) log.Print("accepted connection; awaiting requests...") log = log.Push("handle ipc connection") defer func() { if err := conn.Close(); err != nil { log.Error("handle ipc connection: failed to close connection: %v", err) } }() cpm := internal.NewClientPM() buf := make([]byte, 1024) for ctx.Err() == nil { // TODO move config into server struct err := conn.SetDeadline(time.Now().Add(100 * time.Millisecond)) if err != nil { log.Error("set connection deadline failed: %v", err) continue } sdu, err := internal.Receive(conn, buf) if errors.Is(err, os.ErrDeadlineExceeded) { continue } if errors.Is(err, io.EOF) { continue } if err != nil { log.Error("%v", err) continue } sdu, err = cpm.Event(sdu) if err := internal.Send(conn, sdu); err != nil { if !errors.Is(err, os.ErrDeadlineExceeded) { log.Error("%v", err) } continue } } }