From cc98b12e78df32f1559daa3a96e089c50380b4fe Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Thu, 16 May 2024 11:11:22 -0300 Subject: Rename source files to "lib.go" and "main.go" --- src/cmd/main.go | 7 + src/cmd/papod.go | 7 - src/lib.go | 601 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/papod.go | 601 ------------------------------------------------------- 4 files changed, 608 insertions(+), 608 deletions(-) create mode 100644 src/cmd/main.go delete mode 100644 src/cmd/papod.go create mode 100644 src/lib.go delete mode 100644 src/papod.go (limited to 'src') diff --git a/src/cmd/main.go b/src/cmd/main.go new file mode 100644 index 0000000..2f78c4d --- /dev/null +++ b/src/cmd/main.go @@ -0,0 +1,7 @@ +package main + +import "euandre.org/papo/src" + +func main() { + papo.Main() +} diff --git a/src/cmd/papod.go b/src/cmd/papod.go deleted file mode 100644 index 2f78c4d..0000000 --- a/src/cmd/papod.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "euandre.org/papo/src" - -func main() { - papo.Main() -} diff --git a/src/lib.go b/src/lib.go new file mode 100644 index 0000000..38f6d4c --- /dev/null +++ b/src/lib.go @@ -0,0 +1,601 @@ +package papo + +import ( + "bufio" + "bytes" + "crypto/rand" + "database/sql" + "encoding/hex" + "errors" + "flag" + "fmt" + "io" + // "log" + "log/slog" + "math/big" + "net" + "os" + "regexp" + "runtime/debug" + "strings" + "sync" + "syscall" + "time" + + _ "github.com/mattn/go-sqlite3" +) + + +/* Global variables */ +var ( + Hostname string + Version string + Colour string +) + + +// FIXME: finish rewriting +// +// lastV7time is the last time we returned stored as: +// +// 52 bits of time in milliseconds since epoch +// 12 bits of (fractional nanoseconds) >> 8 +var lastV7Time int64 +var timeMu sync.Mutex +// getV7Time returns the time in milliseconds and nanoseconds / 256. +// The returned (milli << (12 + seq)) is guaranteed to be greater than +// (milli << (12 + seq)) returned by any previous call to getV7Time. +// `seq` Sequence number is between 0 and 3906 (nanoPerMilli >> 8) +func getV7Time(nano int64) (int64, int64) { + const nanoPerMilli = 1000000 + + milli := nano / nanoPerMilli + seq := (nano - (milli * nanoPerMilli)) >> 8 + now := milli << (12 + seq) + + timeMu.Lock() + defer timeMu.Unlock() + if now <= lastV7Time { + now = lastV7Time + 1 + milli = now >> 12 + seq = now & 0xfff + } + lastV7Time = now + return milli, seq +} + +const lengthUUID = 16 + +type UUID struct { + bytes [lengthUUID]byte +} + +func NewUUID() UUID { + var buf [lengthUUID]byte + _, err := io.ReadFull(rand.Reader, buf[7:]) + FatalIf(err) + + buf[6] = (buf[6] & 0x0f) | 0x40 // Version 4 + buf[8] = (buf[8] & 0x3f) | 0x80 // Variant is 10 + + t, s := getV7Time(time.Now().UnixNano()) + + buf[0] = byte(t >> 40) + buf[1] = byte(t >> 32) + buf[2] = byte(t >> 24) + buf[3] = byte(t >> 16) + buf[4] = byte(t >> 8) + buf[5] = byte(t >> 0) + + buf[6] = 0x70 | (0x0f & byte(s >> 8)) + buf[7] = byte(s) + return UUID { bytes: buf } +} + +func UUIDToString(uuid UUID) string { + const dashCount = 4 + const encodedLength = (lengthUUID * 2) + dashCount + dst := [encodedLength]byte { + 0, 0, 0, 0, + 0, 0, 0, 0, + '-', + 0, 0, 0, 0, + '-', + 0, 0, 0, 0, + '-', + 0, 0, 0, 0, + '-', + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + } + + hex.Encode(dst[ 0:8], uuid.bytes[0:4]) + hex.Encode(dst[ 9:13], uuid.bytes[4:6]) + hex.Encode(dst[14:18], uuid.bytes[6:8]) + hex.Encode(dst[19:23], uuid.bytes[8:10]) + hex.Encode(dst[24:36], uuid.bytes[10:]) + + return string(dst[:]) +} + + +func Debug(message string, type_ string, args ...any) { + slog.Debug( + message, + append( + []any { + "id", UUIDToString(NewUUID()), + "kind", "log", + "type", type_, + }, + args..., + )..., + ) +} + +func Info(message string, type_ string, args ...any) { + slog.Info( + message, + append( + []any { + "id", UUIDToString(NewUUID()), + "kind", "log", + "type", type_, + }, + args..., + )..., + ) +} + +func Warning(message string, type_ string, args ...any) { + slog.Warn( + message, + append( + []any { + "id", UUIDToString(NewUUID()), + "kind", "log", + "type", type_, + }, + args..., + )..., + ) +} + +func Error(message string, type_ string, args ...any) { + slog.Error( + message, + append( + []any { + "id", UUIDToString(NewUUID()), + "kind", "log", + "type", type_, + }, + args..., + )..., + ) +} + +func Metric(type_ string, label string, args ...any) { + slog.Info( + "_", + append( + []any { + "id", UUIDToString(NewUUID()), + "kind", "metric", + "type", type_, + "label", label, + }, + args..., + )..., + ) +} + +type Gauge struct { + Inc func(...any) + Dec func(...any) +} + +var zero = big.NewInt(0) +var one = big.NewInt(1) +func MakeGauge(label string, staticArgs ...any) Gauge { + count := big.NewInt(0) + emitGauge := func(dynamicArgs ...any) { + if count.Cmp(zero) == -1 { + Error( + "Gauge went negative", + "process-metric", + append( + []any { "value", count }, + append( + staticArgs, + dynamicArgs..., + )..., + )..., + ) + return // avoid wrong metrics being emitted + } + Metric( + "gauge", label, + // TODO: we'll have slices.Concat on Go 1.22 + append( + []any { "value", count }, + append( + staticArgs, + dynamicArgs..., + )..., + )..., + ) + } + return Gauge { + Inc: func(dynamicArgs ...any) { + count.Add(count, one) + emitGauge(dynamicArgs...) + }, + Dec: func(dynamicArgs ...any) { + count.Sub(count, one) + emitGauge(dynamicArgs...) + }, + } +} + +func MakeCounter(label string) func(...any) { + return func(args ...any) { + Metric( + "counter", label, + append([]any { "value", 1 }, args...)..., + ) + } +} + +var EmitActiveConnection = MakeGauge("active-connections") +var EmitNicksInChannel = MakeGauge("nicks-in-channel") +var EmitReceivedMessage = MakeCounter("received-message") + +const pingFrequency = time.Duration(30) * time.Second +const pongMaxLatency = time.Duration(5) * time.Second + + +func Fatal(err error) { + Error( + "Fatal error", "fatal-error", + "error", err, + "stack", string(debug.Stack()), + ) + syscall.Kill(os.Getpid(), syscall.SIGABRT) + os.Exit(3) +} + +func FatalIf(err error) { + if err != nil { + Fatal(err) + } +} + + +type Channel struct { +} + +type Context struct { + dbConn *sql.DB + tx chan int +} + +type Connection struct { + conn net.Conn + // id *UUID + id string + isAuthenticated bool +} + +type MessageParams struct { + Middle []string + Trailing string +} + +type Message struct { + Prefix string + Command string + Params MessageParams + Raw string +} + +var ( + CmdUser = Message { Command: "USER" } +) + +func SplitOnCRLF(data []byte, _atEOF bool) (int, []byte, error) { + idx := bytes.Index(data, []byte { '\r', '\n' }) + if idx == -1 { + return 0, nil, nil + } + + return idx + 2, data[0:idx], nil +} + +func SplitOnRawMessage(data []byte, atEOF bool) (int, []byte, error) { + advance, token, error := SplitOnCRLF(data, atEOF) + + if len(token) == 0 { + return advance, nil, error + } + + return advance, token, error +} + +func SplitSpaces(r rune) bool { + return r == ' ' +} + +func ParseMessageParams(params string) MessageParams { + const sep = " :" + + var middle string + var trailing string + + idx := strings.Index(params, sep) + if idx == -1 { + middle = params + trailing = "" + } else { + middle = params[:idx] + trailing = params[idx + len(sep):] + } + + return MessageParams { + Middle: strings.FieldsFunc(middle, SplitSpaces), + Trailing: trailing, + } +} + +var MessageRegex = regexp.MustCompilePOSIX( + // + //1 2 3 4 + `^(:([^ ]+) +)?([a-zA-Z]+|[0-9]{3}) *( .*)$`, + // ^^^^ FIXME: test these spaces +) +func ParseMessage(rawMessage string) (Message, error) { + var msg Message + + components := MessageRegex.FindStringSubmatch(rawMessage) + if components == nil { + return msg, nil + } + + msg = Message { + Prefix: components[2], + Command: components[3], + Params: ParseMessageParams(components[4]), + Raw: rawMessage, + } + return msg, nil +} + +func HandleMessage(msg Message) { + fmt.Printf("msg: %#v\n", msg) +} + +func ReplyAnonymous() { +} + +func PersistMessage(msg Message) { +} + +func ActionsFor(msg Message) []int { + return []int { } +} + +func RunAction(action int) { +} + +func ProcessMessage(ctx *Context, connection *Connection, rawMessage string) { + msg, err := ParseMessage(rawMessage) + if err != nil { + return + } + + if msg.Command == CmdUser.Command { + connection.id = msg.Params.Middle[0] + connection.isAuthenticated = true + } + + if !connection.isAuthenticated { + go ReplyAnonymous() + return + } + + for _, action := range ActionsFor(msg) { + RunAction(action) + } +} + +func ReadLoop(ctx *Context, connection *Connection) { + scanner := bufio.NewScanner(connection.conn) + scanner.Split(SplitOnRawMessage) + for scanner.Scan() { + ProcessMessage(ctx, connection, scanner.Text()) + } +} + +func WriteLoop(ctx *Context, connection *Connection) { + fmt.Println("WriteLoop") +} + +func PingLoop(ctx *Context, connection *Connection) { + fmt.Println("PingLoop") +} + +func HandleConnection(ctx *Context, conn net.Conn) { + EmitActiveConnection.Inc() + // FIXME: WaitGroup here? + connection := Connection { + conn: conn, + isAuthenticated: false, + } + go ReadLoop(ctx, &connection) + go WriteLoop(ctx, &connection) + go PingLoop(ctx, &connection) +} + +func IRCdLoop(ctx *Context, publicSocketPath string) { + listener, err := net.Listen("unix", publicSocketPath) + FatalIf(err) + Info("IRCd started", "component-up", "component", "ircd") + + for { + conn, err := listener.Accept() + if err != nil { + Warning( + "Error accepting a public IRCd connection", + "accept-connection", + "err", err, + ) + // conn.Close() // FIXME: is conn nil? + continue + } + go HandleConnection(ctx, conn) // FIXME: where does it get closed + } +} + +func CommandListenerLoop(ctx *Context, commandSocketPath string) { + listener, err := net.Listen("unix", commandSocketPath) + FatalIf(err) + Info("command listener started", "component-up", "component", "command-listener") + + for { + conn, err := listener.Accept() + if err != nil { + Warning( + "Error accepting a command connection", + "accept-command", + "err", err, + ) + continue + } + defer conn.Close() + + // TODO: handle commands + } +} + +func TransactorLoop(ctx *Context) { + Info("transactor started", "component-up", "component", "transactor") + EmitActiveConnection.Inc() + + for tx := range ctx.tx { + fmt.Println(tx) + } +} + +func SetLoggerOutput(w io.Writer) { + slog.SetDefault(slog.New(slog.NewJSONHandler(w, &slog.HandlerOptions { + AddSource: true, + })).With( + slog.Group( + "info", + "pid", os.Getpid(), + "ppid", os.Getppid(), + "puuid", UUIDToString(NewUUID()), + ), + )) +} + +func SetTraceback() { + if os.Getenv("GOTRACEBACK") == "" { + debug.SetTraceback("crash") + } +} + +func SetHostname() { + var err error + Hostname, err = os.Hostname() + FatalIf(err) +} + +func SetEnvironmentVariables() { + Version = os.Getenv("PAPO_VERSION") + if Version == "" { + Version = "PAPO-VERSION-UNKNOWN" + } + + Colour = os.Getenv("PAPO_COLOUR") + if Colour == "" { + Colour = "PAPO-COLOUR-UNKNOWN" + } +} + +func InitDB(databasePath string) *sql.DB { + DB, err := sql.Open("sqlite3", databasePath) + FatalIf(err) + return DB +} + +func init() { + SetLoggerOutput(os.Stdout) + SetTraceback() + SetHostname() + SetEnvironmentVariables() +} + +func Start(ctx *Context, publicSocketPath string, commandSocketPath string) { + buildInfo, ok := debug.ReadBuildInfo() + if !ok { + Fatal(errors.New("error on debug.ReadBuildInfo()")) + } + + Info("-", "lifecycle-event", + "event", "starting-server", + slog.Group( + "go", + "version", buildInfo.GoVersion, + "settings", buildInfo.Settings, + "deps", buildInfo.Deps, + ), + ) + + var wg sync.WaitGroup + bgRun := func(f func()) { + wg.Add(1) + go func() { + f() + wg.Done() + }() + } + bgRun(func() { IRCdLoop(ctx, publicSocketPath) }) + bgRun(func() { CommandListenerLoop(ctx, commandSocketPath) }) + bgRun(func() { TransactorLoop(ctx) }) + wg.Wait() +} + +func BuildContext(databasePath string) *Context { + dbConn := InitDB(databasePath) + tx := make(chan int, 100) + return &Context { + dbConn, + tx, + } +} + +var ( + databasePath = flag.String( + "f", + "papo.db", + "The path to the database file", + ) + publicSocketPath = flag.String( + "s", + "papo.public.socket", + "The path to the socket that handles the public traffic", + ) + commandSocketPath = flag.String( + "S", + "papo.command.socket", + "The path to the private IPC commands socket", + ) +) + +func Main() { + flag.Parse() + ctx := BuildContext(*databasePath) + Start(ctx, *publicSocketPath, *commandSocketPath) +} diff --git a/src/papod.go b/src/papod.go deleted file mode 100644 index 38f6d4c..0000000 --- a/src/papod.go +++ /dev/null @@ -1,601 +0,0 @@ -package papo - -import ( - "bufio" - "bytes" - "crypto/rand" - "database/sql" - "encoding/hex" - "errors" - "flag" - "fmt" - "io" - // "log" - "log/slog" - "math/big" - "net" - "os" - "regexp" - "runtime/debug" - "strings" - "sync" - "syscall" - "time" - - _ "github.com/mattn/go-sqlite3" -) - - -/* Global variables */ -var ( - Hostname string - Version string - Colour string -) - - -// FIXME: finish rewriting -// -// lastV7time is the last time we returned stored as: -// -// 52 bits of time in milliseconds since epoch -// 12 bits of (fractional nanoseconds) >> 8 -var lastV7Time int64 -var timeMu sync.Mutex -// getV7Time returns the time in milliseconds and nanoseconds / 256. -// The returned (milli << (12 + seq)) is guaranteed to be greater than -// (milli << (12 + seq)) returned by any previous call to getV7Time. -// `seq` Sequence number is between 0 and 3906 (nanoPerMilli >> 8) -func getV7Time(nano int64) (int64, int64) { - const nanoPerMilli = 1000000 - - milli := nano / nanoPerMilli - seq := (nano - (milli * nanoPerMilli)) >> 8 - now := milli << (12 + seq) - - timeMu.Lock() - defer timeMu.Unlock() - if now <= lastV7Time { - now = lastV7Time + 1 - milli = now >> 12 - seq = now & 0xfff - } - lastV7Time = now - return milli, seq -} - -const lengthUUID = 16 - -type UUID struct { - bytes [lengthUUID]byte -} - -func NewUUID() UUID { - var buf [lengthUUID]byte - _, err := io.ReadFull(rand.Reader, buf[7:]) - FatalIf(err) - - buf[6] = (buf[6] & 0x0f) | 0x40 // Version 4 - buf[8] = (buf[8] & 0x3f) | 0x80 // Variant is 10 - - t, s := getV7Time(time.Now().UnixNano()) - - buf[0] = byte(t >> 40) - buf[1] = byte(t >> 32) - buf[2] = byte(t >> 24) - buf[3] = byte(t >> 16) - buf[4] = byte(t >> 8) - buf[5] = byte(t >> 0) - - buf[6] = 0x70 | (0x0f & byte(s >> 8)) - buf[7] = byte(s) - return UUID { bytes: buf } -} - -func UUIDToString(uuid UUID) string { - const dashCount = 4 - const encodedLength = (lengthUUID * 2) + dashCount - dst := [encodedLength]byte { - 0, 0, 0, 0, - 0, 0, 0, 0, - '-', - 0, 0, 0, 0, - '-', - 0, 0, 0, 0, - '-', - 0, 0, 0, 0, - '-', - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - } - - hex.Encode(dst[ 0:8], uuid.bytes[0:4]) - hex.Encode(dst[ 9:13], uuid.bytes[4:6]) - hex.Encode(dst[14:18], uuid.bytes[6:8]) - hex.Encode(dst[19:23], uuid.bytes[8:10]) - hex.Encode(dst[24:36], uuid.bytes[10:]) - - return string(dst[:]) -} - - -func Debug(message string, type_ string, args ...any) { - slog.Debug( - message, - append( - []any { - "id", UUIDToString(NewUUID()), - "kind", "log", - "type", type_, - }, - args..., - )..., - ) -} - -func Info(message string, type_ string, args ...any) { - slog.Info( - message, - append( - []any { - "id", UUIDToString(NewUUID()), - "kind", "log", - "type", type_, - }, - args..., - )..., - ) -} - -func Warning(message string, type_ string, args ...any) { - slog.Warn( - message, - append( - []any { - "id", UUIDToString(NewUUID()), - "kind", "log", - "type", type_, - }, - args..., - )..., - ) -} - -func Error(message string, type_ string, args ...any) { - slog.Error( - message, - append( - []any { - "id", UUIDToString(NewUUID()), - "kind", "log", - "type", type_, - }, - args..., - )..., - ) -} - -func Metric(type_ string, label string, args ...any) { - slog.Info( - "_", - append( - []any { - "id", UUIDToString(NewUUID()), - "kind", "metric", - "type", type_, - "label", label, - }, - args..., - )..., - ) -} - -type Gauge struct { - Inc func(...any) - Dec func(...any) -} - -var zero = big.NewInt(0) -var one = big.NewInt(1) -func MakeGauge(label string, staticArgs ...any) Gauge { - count := big.NewInt(0) - emitGauge := func(dynamicArgs ...any) { - if count.Cmp(zero) == -1 { - Error( - "Gauge went negative", - "process-metric", - append( - []any { "value", count }, - append( - staticArgs, - dynamicArgs..., - )..., - )..., - ) - return // avoid wrong metrics being emitted - } - Metric( - "gauge", label, - // TODO: we'll have slices.Concat on Go 1.22 - append( - []any { "value", count }, - append( - staticArgs, - dynamicArgs..., - )..., - )..., - ) - } - return Gauge { - Inc: func(dynamicArgs ...any) { - count.Add(count, one) - emitGauge(dynamicArgs...) - }, - Dec: func(dynamicArgs ...any) { - count.Sub(count, one) - emitGauge(dynamicArgs...) - }, - } -} - -func MakeCounter(label string) func(...any) { - return func(args ...any) { - Metric( - "counter", label, - append([]any { "value", 1 }, args...)..., - ) - } -} - -var EmitActiveConnection = MakeGauge("active-connections") -var EmitNicksInChannel = MakeGauge("nicks-in-channel") -var EmitReceivedMessage = MakeCounter("received-message") - -const pingFrequency = time.Duration(30) * time.Second -const pongMaxLatency = time.Duration(5) * time.Second - - -func Fatal(err error) { - Error( - "Fatal error", "fatal-error", - "error", err, - "stack", string(debug.Stack()), - ) - syscall.Kill(os.Getpid(), syscall.SIGABRT) - os.Exit(3) -} - -func FatalIf(err error) { - if err != nil { - Fatal(err) - } -} - - -type Channel struct { -} - -type Context struct { - dbConn *sql.DB - tx chan int -} - -type Connection struct { - conn net.Conn - // id *UUID - id string - isAuthenticated bool -} - -type MessageParams struct { - Middle []string - Trailing string -} - -type Message struct { - Prefix string - Command string - Params MessageParams - Raw string -} - -var ( - CmdUser = Message { Command: "USER" } -) - -func SplitOnCRLF(data []byte, _atEOF bool) (int, []byte, error) { - idx := bytes.Index(data, []byte { '\r', '\n' }) - if idx == -1 { - return 0, nil, nil - } - - return idx + 2, data[0:idx], nil -} - -func SplitOnRawMessage(data []byte, atEOF bool) (int, []byte, error) { - advance, token, error := SplitOnCRLF(data, atEOF) - - if len(token) == 0 { - return advance, nil, error - } - - return advance, token, error -} - -func SplitSpaces(r rune) bool { - return r == ' ' -} - -func ParseMessageParams(params string) MessageParams { - const sep = " :" - - var middle string - var trailing string - - idx := strings.Index(params, sep) - if idx == -1 { - middle = params - trailing = "" - } else { - middle = params[:idx] - trailing = params[idx + len(sep):] - } - - return MessageParams { - Middle: strings.FieldsFunc(middle, SplitSpaces), - Trailing: trailing, - } -} - -var MessageRegex = regexp.MustCompilePOSIX( - // - //1 2 3 4 - `^(:([^ ]+) +)?([a-zA-Z]+|[0-9]{3}) *( .*)$`, - // ^^^^ FIXME: test these spaces -) -func ParseMessage(rawMessage string) (Message, error) { - var msg Message - - components := MessageRegex.FindStringSubmatch(rawMessage) - if components == nil { - return msg, nil - } - - msg = Message { - Prefix: components[2], - Command: components[3], - Params: ParseMessageParams(components[4]), - Raw: rawMessage, - } - return msg, nil -} - -func HandleMessage(msg Message) { - fmt.Printf("msg: %#v\n", msg) -} - -func ReplyAnonymous() { -} - -func PersistMessage(msg Message) { -} - -func ActionsFor(msg Message) []int { - return []int { } -} - -func RunAction(action int) { -} - -func ProcessMessage(ctx *Context, connection *Connection, rawMessage string) { - msg, err := ParseMessage(rawMessage) - if err != nil { - return - } - - if msg.Command == CmdUser.Command { - connection.id = msg.Params.Middle[0] - connection.isAuthenticated = true - } - - if !connection.isAuthenticated { - go ReplyAnonymous() - return - } - - for _, action := range ActionsFor(msg) { - RunAction(action) - } -} - -func ReadLoop(ctx *Context, connection *Connection) { - scanner := bufio.NewScanner(connection.conn) - scanner.Split(SplitOnRawMessage) - for scanner.Scan() { - ProcessMessage(ctx, connection, scanner.Text()) - } -} - -func WriteLoop(ctx *Context, connection *Connection) { - fmt.Println("WriteLoop") -} - -func PingLoop(ctx *Context, connection *Connection) { - fmt.Println("PingLoop") -} - -func HandleConnection(ctx *Context, conn net.Conn) { - EmitActiveConnection.Inc() - // FIXME: WaitGroup here? - connection := Connection { - conn: conn, - isAuthenticated: false, - } - go ReadLoop(ctx, &connection) - go WriteLoop(ctx, &connection) - go PingLoop(ctx, &connection) -} - -func IRCdLoop(ctx *Context, publicSocketPath string) { - listener, err := net.Listen("unix", publicSocketPath) - FatalIf(err) - Info("IRCd started", "component-up", "component", "ircd") - - for { - conn, err := listener.Accept() - if err != nil { - Warning( - "Error accepting a public IRCd connection", - "accept-connection", - "err", err, - ) - // conn.Close() // FIXME: is conn nil? - continue - } - go HandleConnection(ctx, conn) // FIXME: where does it get closed - } -} - -func CommandListenerLoop(ctx *Context, commandSocketPath string) { - listener, err := net.Listen("unix", commandSocketPath) - FatalIf(err) - Info("command listener started", "component-up", "component", "command-listener") - - for { - conn, err := listener.Accept() - if err != nil { - Warning( - "Error accepting a command connection", - "accept-command", - "err", err, - ) - continue - } - defer conn.Close() - - // TODO: handle commands - } -} - -func TransactorLoop(ctx *Context) { - Info("transactor started", "component-up", "component", "transactor") - EmitActiveConnection.Inc() - - for tx := range ctx.tx { - fmt.Println(tx) - } -} - -func SetLoggerOutput(w io.Writer) { - slog.SetDefault(slog.New(slog.NewJSONHandler(w, &slog.HandlerOptions { - AddSource: true, - })).With( - slog.Group( - "info", - "pid", os.Getpid(), - "ppid", os.Getppid(), - "puuid", UUIDToString(NewUUID()), - ), - )) -} - -func SetTraceback() { - if os.Getenv("GOTRACEBACK") == "" { - debug.SetTraceback("crash") - } -} - -func SetHostname() { - var err error - Hostname, err = os.Hostname() - FatalIf(err) -} - -func SetEnvironmentVariables() { - Version = os.Getenv("PAPO_VERSION") - if Version == "" { - Version = "PAPO-VERSION-UNKNOWN" - } - - Colour = os.Getenv("PAPO_COLOUR") - if Colour == "" { - Colour = "PAPO-COLOUR-UNKNOWN" - } -} - -func InitDB(databasePath string) *sql.DB { - DB, err := sql.Open("sqlite3", databasePath) - FatalIf(err) - return DB -} - -func init() { - SetLoggerOutput(os.Stdout) - SetTraceback() - SetHostname() - SetEnvironmentVariables() -} - -func Start(ctx *Context, publicSocketPath string, commandSocketPath string) { - buildInfo, ok := debug.ReadBuildInfo() - if !ok { - Fatal(errors.New("error on debug.ReadBuildInfo()")) - } - - Info("-", "lifecycle-event", - "event", "starting-server", - slog.Group( - "go", - "version", buildInfo.GoVersion, - "settings", buildInfo.Settings, - "deps", buildInfo.Deps, - ), - ) - - var wg sync.WaitGroup - bgRun := func(f func()) { - wg.Add(1) - go func() { - f() - wg.Done() - }() - } - bgRun(func() { IRCdLoop(ctx, publicSocketPath) }) - bgRun(func() { CommandListenerLoop(ctx, commandSocketPath) }) - bgRun(func() { TransactorLoop(ctx) }) - wg.Wait() -} - -func BuildContext(databasePath string) *Context { - dbConn := InitDB(databasePath) - tx := make(chan int, 100) - return &Context { - dbConn, - tx, - } -} - -var ( - databasePath = flag.String( - "f", - "papo.db", - "The path to the database file", - ) - publicSocketPath = flag.String( - "s", - "papo.public.socket", - "The path to the socket that handles the public traffic", - ) - commandSocketPath = flag.String( - "S", - "papo.command.socket", - "The path to the private IPC commands socket", - ) -) - -func Main() { - flag.Parse() - ctx := BuildContext(*databasePath) - Start(ctx, *publicSocketPath, *commandSocketPath) -} -- cgit v1.2.3