summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.go7
-rw-r--r--src/papod.go1158
2 files changed, 0 insertions, 1165 deletions
diff --git a/src/main.go b/src/main.go
deleted file mode 100644
index b591f5c..0000000
--- a/src/main.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package main
-
-import "papod"
-
-func main() {
- papod.Main()
-}
diff --git a/src/papod.go b/src/papod.go
deleted file mode 100644
index 7db55e8..0000000
--- a/src/papod.go
+++ /dev/null
@@ -1,1158 +0,0 @@
-package papod
-
-import (
- "bufio"
- "bytes"
- "errors"
- "flag"
- "fmt"
- "io"
- "log/slog"
- "net"
- "os"
- "regexp"
- "strings"
- "sync"
-
- "acude"
- "cracha"
- "uuid"
- "pds"
- "stm"
- g "gobang"
- gt "gotext"
-)
-
-
-
-type newUserT struct{
-}
-
-type userT struct{
- username string
-}
-
-type newNetworkT struct{
-}
-
-type networkT struct{
-}
-
-type newMemberT struct{
-}
-
-type memberT struct{
-}
-
-type newChannelT struct{
-}
-
-type channelT struct{
-}
-
-type newEventT struct{
-}
-
-type eventT struct{
-}
-
-type queriesT struct{
- createUser func(newUserT) (userT, error)
- userByUUID func(uuid.UUID) (userT, error)
- editUser func(userT) error
- removeUser func(userT) error
- createNetwork func(userT, newNetworkT) (networkT, error)
- networkByUUID func(userT, uuid.UUID) (networkT, error)
- allNetworks func(userT) (<-chan networkT, error)
- editNetwork func(memberT, networkT) error
- removeNetwork func(memberT) error
- addMember func(memberT, newMemberT) (memberT, error)
- addRoles func(memberT, []string, memberT) error
- removeRoles func(memberT, []string, memberT) error
- memberByUUID func(memberT, uuid.UUID) (memberT, error)
- allMembers func(memberT) (<-chan memberT, error)
- editMember func(memberT, memberT) error
- removeMember func(memberT, memberT) error
- createChannel func(memberT, newChannelT) (channelT, error)
- channelsByName func(memberT, string) (<-chan channelT)
- editChannel func(memberT, channelT) error
- removeChannel func(memberT, channelT) error
- joinChannel func(memberT, channelT) error
- partChannel func(memberT, channelT) error
- addEvent func(memberT, newEventT) (eventT, error)
- eventsAfter func(memberT, eventT) (<-chan eventT, error)
- editEvent func(memberT, eventT) (eventT, error)
- removeEvent func(memberT, uuid.UUID) error
- close func() error
-}
-
-type messageT struct{
- prefix string
- command string
- params []string
- raw string
-}
-
-type replyT struct{
- command string
- params []string
-}
-
-type taskT struct{
-}
-
-type actionT struct{
- replies []replyT
- shouldClose bool
- err error
- task []taskT
-}
-
-type listenersT struct{
- daemon net.Listener
- commander net.Listener
- close func() error
-}
-
-type netConnI interface{
- Write(p []byte) (n int, err error)
- Close() error
-}
-
-type connectionT struct{
- uuid uuid.UUID
- user *userT
- conn netConnI
- send func(messageT)
-}
-
-type metricsT struct{
- activeConnections g.Gauge
- nicksInChannel g.Gauge
- sendToClientError func(...any)
- receivedMessage func(...any)
- sentReply func(...any)
-}
-
-type stateT struct{
- members *pds.Map[string, []string]
- users *pds.Map[string, []uuid.UUID]
- connections *pds.Map[uuid.UUID, connectionT]
-}
-
-type papodT struct{
- acude acude.AcudeI
- cracha cracha.CrachaI
- listeners listenersT
- state *stm.AtomT[stateT]
- metrics metricsT
-}
-
-type PapoI interface{
- Start() error
- Close() error
-}
-
-type argsT struct{
- allArgs []string
- subArgs []string
- baseDir string
- tag string
-}
-
-type envT struct{
- args argsT
- in io.Reader
- out io.Writer
- err io.Writer
-}
-
-
-
-func initListeners(
- daemonSocketPath string,
- commanderSocketPath string,
-) (listenersT, error) {
- _ = os.Remove(daemonSocketPath)
- _ = os.Remove(commanderSocketPath)
-
- daemon, err := net.Listen("unix", daemonSocketPath)
- if err != nil {
- return listenersT{}, err
- }
-
- commander, err := net.Listen("unix", commanderSocketPath)
- if err != nil {
- daemon.Close()
- return listenersT{}, err
- }
-
- return listenersT{
- daemon: daemon,
- commander: commander,
- close: func() error {
- return g.SomeFnError(
- daemon.Close,
- commander.Close,
- )
- },
- }, nil
-}
-
-func newState() stateT {
- return stateT{
- members: pds.NewMap[string, []string](nil),
- users: pds.NewMap[string, []uuid.UUID](nil),
- connections: pds.NewMap[uuid.UUID, connectionT](nil),
- }
-}
-
-func buildMetrics(tag string) metricsT {
- return metricsT{
- activeConnections: g.MakeGauge(
- "active-connection",
- "tag", tag,
- ),
- nicksInChannel: g.MakeGauge(
- "nicks-in-channel",
- "tag", tag,
- ),
- sendToClientError: g.MakeCounter(
- "send-to-client-error",
- "tag", tag,
- ),
- receivedMessage: g.MakeCounter(
- "received-message",
- "tag", tag,
- ),
- sentReply: g.MakeCounter(
- "sent-reply",
- "tag", tag,
- ),
- }
-}
-
-func initDB(path string) (acude.AcudeI, error) {
- return nil, nil
-}
-
-func newPapod(baseDir string, tag string) (papodT, error) {
- databasePath := baseDir + "/papod.dedo"
- daemonSocketPath := baseDir + "/papod.daemon.socket"
- commanderSocketPath := baseDir + "/papod.commander.socket"
-
- cracha, err := cracha.New(databasePath)
- if err != nil {
- return papodT{}, err
- }
-
- listeners, err := initListeners(daemonSocketPath, commanderSocketPath)
- if err != nil {
- cracha.Close()
- return papodT{}, err
- }
-
- acude, err := initDB(databasePath)
- if err != nil {
- cracha.Close()
- listeners.close()
- return papodT{}, err
- }
-
- return papodT{
- acude: acude,
- cracha: cracha,
- listeners: listeners,
- state: stm.Atom(newState()),
- metrics: buildMetrics(tag),
- }, nil
-}
-
-func NewWith(baseDir string, tag string) (PapoI, error) {
- return newPapod(baseDir, tag)
-}
-
-func New() (PapoI, error) {
- return NewWith(".", "papod-default")
-}
-
-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 splitCommas(r rune) bool {
- return r == ','
-}
-
-func splitSpaces(r rune) bool {
- return r == ' '
-}
-
-func parseMessageParams(params string) []string {
- const sep = " :"
-
- idx := strings.Index(params, sep)
- if idx == -1 {
- return strings.FieldsFunc(params, splitSpaces)
- } else {
- middle := params[:idx]
- trailing := params[idx + len(sep):]
- return append(
- strings.FieldsFunc(middle, splitSpaces),
- trailing,
- )
- }
-}
-
-func stripBlankParams(params []string) []string {
- if len(params) == 1 && len(params[0]) == 0 {
- return []string{}
- }
-
- return params
-}
-
-var messageRegex = regexp.MustCompilePOSIX(
- // <prefix> <command> <params>
- //1 2 3 4
- `^(:([^ ]+) +)?([a-zA-Z]+) *( .*)$`,
-)
-func parseMessage(rawMessage string) (messageT, error) {
- var msg messageT
-
- components := messageRegex.FindStringSubmatch(rawMessage)
- if components == nil {
- return msg, errors.New("Can't parse message")
- }
-
- msg = messageT{
- prefix: components[2],
- command: components[3],
- params: stripBlankParams(parseMessageParams(components[4])),
- raw: rawMessage,
- }
- return msg, nil
-}
-
-func removeConnection(state stateT, connection *connectionT) stateT {
- return state // FIXME
-}
-
-/// Is this death by a thousand goroutines? Is the runtime able to handle the
-/// creation and destruction of hundreds of thousands of goroutines per second?
-/// For now, we'll assume that Go's (gc) runtime, scheduler and garbage
-/// collector are capable of working together to make sure this isn't a
-/// catastrophe.
-func broadcastMessage(
- message messageT,
- channelName string,
- state stateT,
-) {
- usernames, _ := state.members.Get(channelName)
- for _, username := range usernames {
- connectionIDs, _ := state.users.Get(username)
- for _, connectionID := range connectionIDs {
- connection, ok := state.connections.Get(connectionID)
- if !ok {
- continue
- }
-
- go connection.send(message)
- }
- }
-}
-
-
-/*
-Intentionally not implemented:
-
-// FIXME: why?
-- RPL_BOUNCE
-
-*/
-
-// FIXME: add check for minRPL...
-const minRPL_WELCOME = 0
-func _RPL_WELCOME(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "001",
- params: []string{
- connection.user.username,
- "Welcome to the Internet Relay Network " +
- connection.user.username,
- },
- }
-}
-
-const minRPL_YOURHOST = 0
-func _RPL_YOURHOST(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "002",
- params: []string{
- connection.user.username,
- "Your host is FIXME, running version " +
- Version,
- },
- }
-}
-
-const minRPL_CREATED = 0
-func _RPL_CREATED(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "003",
- params: []string{
- connection.user.username,
- "This server was create FIXME",
- },
- }
-}
-
-const minRPL_MYINFO = 0
-func _RPL_MYINFO(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "004",
- params: []string{
- connection.user.username,
- "FIXME " + Version + " i x",
- },
- }
-}
-
-const minRPL_UNAWAY = 0
-func _RPL_UNAWAY(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "305",
- params: []string{
- connection.user.username,
- "You are no longer marked as away",
- },
- }
-}
-
-const minRPL_NOWAWAY = 0
-func _RPL_NOWAWAY(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "306",
- params: []string{
- connection.user.username,
- "You have been marked as away",
- },
- }
-}
-
-const minRPL_WHOISUSER = 1
-func _RPL_WHOISUSER(connection *connectionT, msg messageT) replyT {
- user := msg.params[0]
- return replyT{
- command: "311",
- params: []string{
- connection.user.username,
- user,
- user,
- "samehost",
- "*",
- "my real name is: " + user,
- },
- }
-}
-
-const minRPL_WHOISSERVER = 1
-func _RPL_WHOISSERVER(connection *connectionT, msg messageT) replyT {
- user := msg.params[0]
- return replyT{
- command: "312",
- params: []string{
- connection.user.username,
- user,
- "stillsamehost",
- "some server info",
- },
- }
-}
-
-const minRPL_ENDOFWHOIS = 1
-func _RPL_ENDOFWHOIS(connection *connectionT, msg messageT) replyT {
- user := msg.params[0]
- return replyT{
- command: "318",
- params: []string{
- connection.user.username,
- user,
- "End of WHOIS list",
- },
- }
-}
-
-const minRPL_WHOISCHANNELS = 1
-func _RPL_WHOISCHANNELS(connection *connectionT, msg messageT) replyT {
- user := msg.params[0]
- return replyT{
- command: "319",
- params: []string{
- connection.user.username,
- user,
- "#default",
- },
- }
-}
-
-const minRPL_CHANNELMODEIS = 1
-func _RPL_CHANNELMODEIS(connection *connectionT, msg messageT) replyT {
- channel := msg.params[0]
- return replyT{
- command: "324",
- params: []string{
- connection.user.username,
- channel,
- "+Cnst",
- },
- }
-}
-
-const minRPL_NOTOPIC = 1
-func _RPL_NOTOPIC(connection *connectionT, msg messageT) replyT {
- channel := msg.params[0]
- return replyT{
- command: "331",
- params: []string{
- connection.user.username,
- channel,
- "No topic is set",
- },
- }
-}
-
-const minRPL_NAMREPLY = 1
-func _RPL_NAMREPLY(connection *connectionT, msg messageT) replyT {
- channel := msg.params[0]
- return replyT{
- command: "353",
- params: []string{
- connection.user.username,
- "=",
- channel,
- connection.user.username + " virtualuser",
- },
- }
-}
-
-const minRPL_ENDOFNAMES = 1
-func _RPL_ENDOFNAMES(connection *connectionT, msg messageT) replyT {
- channel := msg.params[0]
- return replyT{
- command: "366",
- params: []string{
- connection.user.username,
- channel,
- "End of NAMES list",
- },
- }
-}
-
-const minERR_UNKNOWNCOMMAND = 0
-func _ERR_UNKNOWNCOMMAND(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "421",
- params: []string{
- connection.user.username,
- "Unknown command",
- },
- }
-}
-
-const minERR_FILEERROR = 0
-func _ERR_FILEERROR(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "424",
- params: []string{
- "File error doing query on database",
- },
- }
-}
-
-const minERR_NOTREGISTERED = 0
-func _ERR_NOTREGISTERED(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "451",
- params: []string{
- "You have not registered",
- },
- }
-}
-
-const minERR_NEEDMOREPARAMS = 0
-func _ERR_NEEDMOREPARAMS(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "461",
- params: []string{
- msg.command,
- "Not enough parameters",
- },
- }
-}
-
-
-func _CAP(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "CAP",
- params: []string {
- "*",
- "LS",
- },
- }
-}
-
-const minPONG = 0
-func _PONG(connection *connectionT, msg messageT) replyT {
- return replyT{
- command: "PONG",
- params: msg.params,
- }
-}
-
-
-const minUSER = 4
-func handleUSER(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- return []replyT{
- _RPL_WELCOME (connection, msg),
- _RPL_YOURHOST(connection, msg),
- _RPL_CREATED (connection, msg),
- _RPL_MYINFO (connection, msg),
- }, false, nil
-}
-
-const minNICK = 1
-func handleNICK(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- connection.user.username = msg.params[0]
- return []replyT{}, false, nil
-}
-
-const minPRIVMSG = 2
-func handlePRIVMSG(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- // FIXME: check if user is member of channel, and is authorized to post
- // FIXME: adapt to handle multiple targets
-
- go broadcastMessage(
- msg,
- msg.params[0],
- papod.state.Deref(),
- )
- return []replyT{}, false, nil
-}
-
-const minTOPIC = 2
-func handleTOPIC(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- return []replyT{
- _RPL_NOTOPIC(connection, msg),
- }, false, nil
-}
-
-const minJOIN = 1
-func handleJOIN(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- // FIXME: add to database
- // channels := strings.FieldsFunc(msg.params[0], splitCommas)
- // papod.stateMutable.subscribe(connection.user.username, channels)
-
- return []replyT{
- _RPL_NOTOPIC (connection, msg),
- _RPL_NAMREPLY (connection, msg),
- _RPL_ENDOFNAMES(connection, msg),
- }, false, nil
-}
-
-const minMODE = 1
-func handleMODE(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- return []replyT{
- _RPL_CHANNELMODEIS(connection, msg),
- }, false, nil
-}
-
-const minWHOIS = 1
-func handleWHOIS(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- return []replyT{
- _RPL_WHOISUSER (connection, msg),
- _RPL_WHOISSERVER (connection, msg),
- _RPL_WHOISCHANNELS(connection, msg),
- _RPL_ENDOFWHOIS (connection, msg),
- }, false, nil
-}
-
-const minAWAY = 0
-func handleAWAY(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- replyFn := _RPL_NOWAWAY
- if len(msg.params) == 0 {
- replyFn = _RPL_UNAWAY
- }
-
- return []replyT{
- replyFn(connection, msg),
- }, false, nil
-}
-
-const minPING = 0
-func handlePING(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- return []replyT{
- _PONG(connection, msg),
- }, false, nil
-}
-
-const minQUIT = 0
-func handleQUIT(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- return []replyT{}, true, nil
-}
-
-const minCAP = 1
-func handleCAP(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- if msg.params[0] == "END" {
- return nil, false, nil
- }
-
- return []replyT{
- _CAP(connection, msg),
- }, false, nil
-}
-
-
-func authRequired(
- fn func(
- papodT,
- *connectionT,
- messageT,
- ) ([]replyT, bool, error),
-) func(papodT, *connectionT, messageT) ([]replyT, bool, error) {
- return func(
- papod papodT,
- connection *connectionT,
- msg messageT,
- ) ([]replyT, bool, error) {
- if connection.user == nil {
- return []replyT{
- _ERR_NOTREGISTERED(connection, msg),
- }, false, nil
- }
-
- return fn(papod, connection, msg)
- }
-}
-
-func minArgs(
- count int,
- fn func(
- papodT,
- *connectionT,
- messageT,
- ) ([]replyT, bool, error),
-) func(papodT, *connectionT, messageT) ([]replyT, bool, error) {
- return func(
- papod papodT,
- connection *connectionT,
- msg messageT,
- ) ([]replyT, bool, error) {
- if len(msg.params) < count {
- return []replyT{
- _ERR_NEEDMOREPARAMS(connection, msg),
- }, false, nil
- }
-
- return fn(papod, connection, msg)
- }
-}
-
-var commands = map[string]func(
- papodT,
- *connectionT,
- messageT,
-) ([]replyT, bool, error) {
- "USER": minArgs(minUSER, handleUSER),
- "NICK": minArgs(minNICK, handleNICK),
- "QUIT": minArgs(minQUIT, handleQUIT),
- "CAP": minArgs(minCAP, handleCAP),
- "AWAY": authRequired(minArgs(minAWAY, handleAWAY)),
- "PRIVMSG": authRequired(minArgs(minPRIVMSG, handlePRIVMSG)),
- "PING": authRequired(minArgs(minPING, handlePING)),
- "JOIN": authRequired(minArgs(minJOIN, handleJOIN)),
- "MODE": authRequired(minArgs(minMODE, handleMODE)),
- "TOPIC": authRequired(minArgs(minTOPIC, handleTOPIC)),
- "WHOIS": authRequired(minArgs(minWHOIS, handleWHOIS)),
-}
-
-func handleUnknown(
- papod papodT,
- connection *connectionT,
- msg messageT,
-) ([]replyT, bool, error) {
- // FIXME: user doesn't exist when unauthenticated
- /*
- err := papod.queries.logMessage(userT{ }, msg)
- if err != nil {
- g.Warning(
- "Failed to log message", fmt.Sprintf("%#v", msg),
- "group-as", "db-write",
- "handler-action", "log-and-ignore",
- "connection", connection.uuid.String(),
- "err", err,
- )
- }
- */
-
- return []replyT{
- _ERR_UNKNOWNCOMMAND(connection, msg),
- }, false, nil
-}
-
-func actionFnFor(
- command string,
-) func(papodT, *connectionT, messageT) ([]replyT, bool, error) {
- fn := commands[command]
- if fn != nil {
- return fn
- }
-
- return handleUnknown
-}
-
-func addTrailingSeparator(strs []string) {
- if len(strs) == 0 {
- return
- }
-
- last := strs[len(strs) - 1]
- if strings.Contains(last, " ") && last[0] != ':' {
- strs[len(strs) - 1] = ":" + last
- }
-}
-
-func (r replyT) String() string {
- addTrailingSeparator(r.params)
- return fmt.Sprintf(
- "%s %s\r\n",
- r.command,
- strings.Join(r.params, " "),
- )
-}
-
-func (m messageT) logAttributes() slog.Attr {
- return slog.Group(
- "message",
- "prefix", m.prefix,
- "raw", m.raw,
- "params", m.params,
- )
-}
-
-func (r replyT) logAttributes() slog.Attr {
- return slog.Group(
- "reply",
- "command", r.command,
- "params", r.params,
- )
-}
-
-func processMessage(
- papod papodT,
- connection *connectionT,
- rawMessage string,
-) {
- msg, err := parseMessage(rawMessage)
- if err != nil {
- g.Info(
- "Error parsing message", "parse-message-error",
- slog.Group(
- "message",
- "text", rawMessage,
- ),
- "err", err,
- )
- return
- }
- papod.metrics.receivedMessage(msg.logAttributes())
-
- var replyErrors []error
- replies, shouldClose, actionErr := actionFnFor(msg.command)(
- papod,
- connection,
- msg,
- )
- for _, reply := range replies {
- _, err = io.WriteString(connection.conn, reply.String())
- if err != nil {
- replyErrors = append(replyErrors, err)
- }
-
- papod.metrics.sentReply(
- msg.logAttributes(),
- reply.logAttributes(),
- )
- }
-
- if actionErr != nil || len(replyErrors) != 0 {
- if actionErr != nil {
- g.Info(
- "Handler returned error", "handler-error",
- "from", "daemon",
- "err", err,
- )
- }
-
- if len(replyErrors) != 0 {
- g.Info(
- "Failed to send reply", "send-reply-error",
- "from", "daemon",
- "err", replyErrors,
- )
- }
-
- stm.Swap(papod.state, func(state stateT) stateT {
- return removeConnection(state, connection)
- })
- err := connection.conn.Close()
- if err != nil {
- g.Warning(
- "Failed to close the connection",
- "close-error",
- "from", "daemon",
- "err", err,
- )
- }
-
- return
- }
-
- if shouldClose {
- // FIXME
- // papod.stateMutable.disconnect(connection)
- }
-}
-
-func processTasks() // FIXME
-
-func handleConnection(papod papodT, conn net.Conn) {
- connection := connectionT{
- uuid: uuid.New(),
- user: &userT{}, // TODO: SASL shenanigan probably goes here
- conn: conn,
- }
- scanner := bufio.NewScanner(conn)
- scanner.Split(splitOnRawMessage)
- for scanner.Scan() {
- processMessage(papod, &connection, scanner.Text())
- }
-}
-
-func daemonLoop(papod papodT) {
- for {
- conn, err := papod.listeners.daemon.Accept()
- if err != nil {
- if errors.Is(err, net.ErrClosed) {
- break
- }
-
- g.Warning(
- "Error accepting daemon connection",
- "accept-connection",
- "from", "daemon",
- "err", err,
- )
- continue
- }
- go handleConnection(papod, conn)
- }
-}
-
-func commanderLoop(papod papodT) {
- for {
- conn, err := papod.listeners.commander.Accept()
- if err != nil {
- if errors.Is(err, net.ErrClosed) {
- break
- }
-
- g.Warning(
- "Error accepting commander connection",
- "accept-connection",
- "from", "commander",
- "err", err,
- )
- continue
- }
- go handleConnection(papod, conn)
- }
-}
-
-func mkbgrun() (func(func()), func()) {
- var wg sync.WaitGroup
- bgrun := func(f func()) {
- wg.Add(1)
- go func() {
- f()
- wg.Done()
- }()
- }
- return bgrun, wg.Wait
-}
-
-func (papod papodT) Start() error {
- g.Info("Starting service", "lifecycle-event",
- "event", "starting-server",
- slog.Group(
- "versions",
- "cracha", cracha.Version,
- "uuid", uuid.Version,
- "pds", pds.Version,
- "stm", stm.Version,
- "papod", Version,
- "gobang", g.Version,
- "gotext", gt.Version,
- ),
- )
-
- run, wait := mkbgrun()
- run(func() { daemonLoop(papod) })
- run(func() { commanderLoop(papod) })
- wait()
-
- return nil
-}
-
-func (papod papodT) Close() error {
- // FIXME: does this wait for current handlers to wait? Well, it should.
- /*
- return g.WrapErrors(
- papod.listeners.close(),
- // papod.connCloser.closeAll(),
- papod.auth.Close(),
- papod.queue.Close(),
- papod.queries.close(),
- )
- */
- return nil
-}
-
-func usage(argv0 string, w io.Writer) {
- fmt.Fprintf(
- w,
- gt.Gettext("Usage: %s [-t TAG] [BASEDIR]"),
- argv0,
- )
-}
-
-func getopt(allArgs []string, w io.Writer) (argsT, int) {
- argv0 := allArgs[0]
- argv := allArgs[1:]
- fs := flag.NewFlagSet("", flag.ContinueOnError)
- fs.Usage = func() {}
- fs.SetOutput(w)
-
- tag := fs.String(
- "t",
- "",
- "The specific instance tag for inclusion in the log",
- )
- if fs.Parse(argv) != nil {
- usage(argv0, w)
- return argsT{}, 2
- }
-
- subArgs := fs.Args()
- baseDir := "."
- if len(subArgs) != 0 {
- baseDir = subArgs[0]
- }
-
- return argsT{
- allArgs: allArgs,
- subArgs: subArgs,
- baseDir: baseDir,
- tag: *tag,
- }, 0
-}
-
-func run(env envT) int {
- papod, err := newPapod(env.args.baseDir, env.args.tag)
- if err != nil {
- fmt.Fprintln(env.err, err)
- return 1
- }
-
- err = papod.Start()
- if err != nil {
- fmt.Fprintln(env.err, err)
- return 1
- }
-
- return 0
-}
-
-
-
-func Main() {
- g.Init()
- gt.Init(Name, LOCALEDIR)
- args, rc := getopt(os.Args, os.Stderr)
- g.ExitIf(rc)
- os.Exit(run(envT{
- args: args,
- in: os.Stdin,
- out: os.Stdout,
- err: os.Stderr,
- }))
-}