main.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /*
  2. This Source Code Form is subject to the terms of the Mozilla Public
  3. License, v. 2.0. If a copy of the MPL was not distributed with this
  4. file, You can obtain one at https://mozilla.org/MPL/2.0/.
  5. */
  6. package main
  7. import (
  8. "context"
  9. "errors"
  10. "io"
  11. "net"
  12. "os"
  13. "os/signal"
  14. "syscall"
  15. "time"
  16. "idio.link/go/ipc/internal"
  17. )
  18. func main() {
  19. log := internal.NewLogger()
  20. ctx := context.WithValue(context.Background(), "log", log)
  21. ctx, cancel := context.WithCancel(ctx)
  22. log.Level(internal.LogLevelDebug)
  23. log.Print("establish signal traps")
  24. go func() {
  25. c := make(chan os.Signal, 1)
  26. defer close(c)
  27. signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
  28. s := <-c
  29. log.Print("received %v signal; shutting down", s)
  30. cancel()
  31. }()
  32. log.Print("listen on abstract unix socket idio.link/go/ipc")
  33. // see `man 7 unix` for the unix sock abstract namespace
  34. abstract := "\000idio.link/go/ipc"
  35. addr, err := net.ResolveUnixAddr("unixpacket", abstract)
  36. if err != nil {
  37. panic(err)
  38. }
  39. listener, err := net.ListenUnix("unixpacket", addr)
  40. if err != nil {
  41. panic(err)
  42. }
  43. // TODO move to goroutine and supervise with restart
  44. acceptIPCConnections(ctx, listener)
  45. log.Print("exiting")
  46. }
  47. func acceptIPCConnections(
  48. ctx context.Context,
  49. l *net.UnixListener,
  50. ) {
  51. log := ctx.Value("log").(*internal.Logger)
  52. log.Print("accepting connections...") // TODO should info bypass the base msg?
  53. log.Push("accept ipc connections")
  54. defer func() {
  55. if err := l.Close(); err != nil {
  56. log.Error("failed to close listener: %v", err)
  57. }
  58. }()
  59. for ctx.Err() == nil {
  60. // TODO move config into server struct
  61. err :=
  62. l.SetDeadline(time.Now().Add(100 * time.Millisecond))
  63. if err != nil {
  64. log.Error("set accept deadline failed: %v", err)
  65. continue
  66. }
  67. conn, err := l.Accept()
  68. if err != nil {
  69. if !errors.Is(err, os.ErrDeadlineExceeded) {
  70. log.Error("listener accept failed: %+v", err)
  71. }
  72. continue
  73. }
  74. go handleIPCConnection(ctx, conn)
  75. }
  76. return
  77. }
  78. func handleIPCConnection(
  79. ctx context.Context,
  80. conn net.Conn,
  81. ) {
  82. log := ctx.Value("log").(*internal.Logger)
  83. log.Print("accepted connection; awaiting requests...")
  84. log = log.Push("handle ipc connection")
  85. defer func() {
  86. if err := conn.Close(); err != nil {
  87. log.Error("handle ipc connection: failed to close connection: %v", err)
  88. }
  89. }()
  90. cpm := internal.NewClientPM()
  91. buf := make([]byte, 1024)
  92. for ctx.Err() == nil {
  93. // TODO move config into server struct
  94. err := conn.SetDeadline(time.Now().Add(100 * time.Millisecond))
  95. if err != nil {
  96. log.Error("set connection deadline failed: %v", err)
  97. continue
  98. }
  99. sdu, err := internal.Receive(conn, buf)
  100. if errors.Is(err, os.ErrDeadlineExceeded) {
  101. continue
  102. }
  103. if errors.Is(err, io.EOF) {
  104. continue
  105. }
  106. if err != nil {
  107. log.Error("%v", err)
  108. continue
  109. }
  110. sdu, err = cpm.Event(sdu)
  111. if err := internal.Send(conn, sdu); err != nil {
  112. if !errors.Is(err, os.ErrDeadlineExceeded) {
  113. log.Error("%v", err)
  114. }
  115. continue
  116. }
  117. }
  118. }