aboutsummaryrefslogtreecommitdiff
path: root/forged/internal
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--forged/internal/config/config.go34
-rw-r--r--forged/internal/database/database.go4
-rw-r--r--forged/internal/hooki/hooki.go45
-rw-r--r--forged/internal/lmtp/config.go53
-rw-r--r--forged/internal/misc/usock.go23
-rw-r--r--forged/internal/server/server.go48
6 files changed, 187 insertions, 20 deletions
diff --git a/forged/internal/config/config.go b/forged/internal/config/config.go
index d3eb8e1..0166390 100644
--- a/forged/internal/config/config.go
+++ b/forged/internal/config/config.go
@@ -1,8 +1,14 @@
package config
import (
+ "bufio"
+ "log/slog"
+ "os"
+
"go.lindenii.runxiyu.org/forge/forged/internal/database"
+ "go.lindenii.runxiyu.org/forge/forged/internal/hooki"
"go.lindenii.runxiyu.org/forge/forged/internal/irc"
+ "go.lindenii.runxiyu.org/forge/forged/internal/scfg"
)
type Config struct {
@@ -16,11 +22,8 @@ type Config struct {
IdleTimeout uint32 `scfg:"idle_timeout"`
ReverseProxy bool `scfg:"reverse_proxy"`
} `scfg:"http"`
- Hooks struct {
- Socket string `scfg:"socket"`
- Execs string `scfg:"execs"`
- } `scfg:"hooks"`
- LMTP struct {
+ Hooks hooki.Config `scfg:"hooks"`
+ LMTP struct {
Socket string `scfg:"socket"`
Domain string `scfg:"domain"`
MaxSize int64 `scfg:"max_size"`
@@ -42,9 +45,28 @@ type Config struct {
General struct {
Title string `scfg:"title"`
} `scfg:"general"`
- DB database.Config `scfg:"db"`
+ DB database.Config `scfg:"db"`
Pprof struct {
Net string `scfg:"net"`
Addr string `scfg:"addr"`
} `scfg:"pprof"`
}
+
+func Open(path string) (config Config, err error) {
+ var configFile *os.File
+
+ if configFile, err = os.Open(path); err != nil {
+ return config, err
+ }
+ defer configFile.Close()
+
+ decoder := scfg.NewDecoder(bufio.NewReader(configFile))
+ if err = decoder.Decode(&config); err != nil {
+ return config, err
+ }
+ for _, u := range decoder.UnknownDirectives() {
+ slog.Warn("unknown configuration directive", "directive", u)
+ }
+
+ return config, err
+}
diff --git a/forged/internal/database/database.go b/forged/internal/database/database.go
index 6674fe6..f353fe8 100644
--- a/forged/internal/database/database.go
+++ b/forged/internal/database/database.go
@@ -19,8 +19,8 @@ type Database struct {
// Open opens a new database connection pool using the provided connection
// string. It returns a Database instance and an error if any occurs.
// It is run indefinitely in the background.
-func Open(connString string) (Database, error) {
- db, err := pgxpool.New(context.Background(), connString)
+func Open(ctx context.Context, config Config) (Database, error) {
+ db, err := pgxpool.New(ctx, config.Conn)
return Database{db}, err
}
diff --git a/forged/internal/hooki/hooki.go b/forged/internal/hooki/hooki.go
index ae26846..8e75bae 100644
--- a/forged/internal/hooki/hooki.go
+++ b/forged/internal/hooki/hooki.go
@@ -1,13 +1,26 @@
package hooki
import (
- "go.lindenii.runxiyu.org/forge/forged/internal/cmap"
+ "fmt"
+ "net"
+
"github.com/gliderlabs/ssh"
+ "go.lindenii.runxiyu.org/forge/forged/internal/cmap"
+ "go.lindenii.runxiyu.org/forge/forged/internal/misc"
)
-type Pool cmap.Map[string, hookinfo]
+type Pool struct {
+ hookMap cmap.Map[string, hookInfo]
+ socketPath string
+ executablesPath string
+}
-type hookinfo struct {
+type Config struct {
+ Socket string `scfg:"socket"`
+ Execs string `scfg:"execs"`
+}
+
+type hookInfo struct {
session ssh.Session
pubkey string
directAccess bool
@@ -19,3 +32,29 @@ type hookinfo struct {
repoName string
contribReq string
}
+
+func New(config Config) (pool Pool) {
+ pool.socketPath = config.Socket
+ pool.executablesPath = config.Execs
+ return
+}
+
+func (pool *Pool) Run() error {
+ listener, _, err := misc.ListenUnixSocket(pool.socketPath)
+ if err != nil {
+ return fmt.Errorf("listen unix socket for hooks: %w", err)
+ }
+
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ return fmt.Errorf("accept conn: %w", err)
+ }
+
+ go pool.handleConn(conn)
+ }
+}
+
+func (pool *Pool) handleConn(conn net.Conn) {
+ panic("TODO: handle hook connection")
+}
diff --git a/forged/internal/lmtp/config.go b/forged/internal/lmtp/config.go
new file mode 100644
index 0000000..ce22740
--- /dev/null
+++ b/forged/internal/lmtp/config.go
@@ -0,0 +1,53 @@
+package lmtp
+
+import (
+ "fmt"
+ "net"
+
+ "go.lindenii.runxiyu.org/forge/forged/internal/misc"
+)
+
+type Pool struct {
+ socket string
+ domain string
+ maxSize int64
+ writeTimeout uint32
+ readTimeout uint32
+}
+
+type Config struct {
+ Socket string `scfg:"socket"`
+ Domain string `scfg:"domain"`
+ MaxSize int64 `scfg:"max_size"`
+ WriteTimeout uint32 `scfg:"write_timeout"`
+ ReadTimeout uint32 `scfg:"read_timeout"`
+}
+
+func New(config Config) (pool Pool) {
+ pool.socket = config.Socket
+ pool.domain = config.Domain
+ pool.maxSize = config.MaxSize
+ pool.writeTimeout = config.WriteTimeout
+ pool.readTimeout = config.ReadTimeout
+ return pool
+}
+
+func (pool *Pool) Run() error {
+ listener, _, err := misc.ListenUnixSocket(pool.socket)
+ if err != nil {
+ return fmt.Errorf("listen unix socket for LMTP: %w", err)
+ }
+
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ return fmt.Errorf("accept conn: %w", err)
+ }
+
+ go pool.handleConn(conn)
+ }
+}
+
+func (pool *Pool) handleConn(conn net.Conn) {
+ panic("TODO: handle LMTP connection")
+}
diff --git a/forged/internal/misc/usock.go b/forged/internal/misc/usock.go
new file mode 100644
index 0000000..357fa43
--- /dev/null
+++ b/forged/internal/misc/usock.go
@@ -0,0 +1,23 @@
+package misc
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "syscall"
+)
+
+func ListenUnixSocket(path string) (listener net.Listener, replaced bool, err error) {
+ listener, err = net.Listen("unix", path)
+ if errors.Is(err, syscall.EADDRINUSE) {
+ replaced = true
+ if unlinkErr := syscall.Unlink(path); unlinkErr != nil {
+ return listener, false, fmt.Errorf("remove existing socket %q: %w", path, unlinkErr)
+ }
+ listener, err = net.Listen("unix", path)
+ }
+ if err != nil {
+ return listener, replaced, fmt.Errorf("listen on unix socket %q: %w", path, err)
+ }
+ return listener, replaced, nil
+}
diff --git a/forged/internal/server/server.go b/forged/internal/server/server.go
index ec875f1..03570c3 100644
--- a/forged/internal/server/server.go
+++ b/forged/internal/server/server.go
@@ -4,29 +4,59 @@ import (
"context"
"fmt"
"html/template"
+ "log"
"go.lindenii.runxiyu.org/forge/forged/internal/config"
"go.lindenii.runxiyu.org/forge/forged/internal/database"
"go.lindenii.runxiyu.org/forge/forged/internal/hooki"
+ "go.lindenii.runxiyu.org/forge/forged/internal/lmtp"
"go.lindenii.runxiyu.org/forge/forged/internal/store"
)
type Server struct {
config config.Config
- database database.Database
- stores *store.Set
- hookis *hooki.Pool
+ database database.Database
+ stores *store.Set
+ hookPool hooki.Pool
+ lmtpPool lmtp.Pool
templates *template.Template
}
-func New(ctx context.Context, config config.Config) (*Server, error) {
- database, err := database.Open(ctx, config.DB)
+func New(ctx context.Context, configPath string) (server *Server, err error) {
+ server = &Server{}
+
+ server.config, err = config.Open(configPath)
if err != nil {
- return nil, fmt.Errorf("open database: %w", err)
+ return server, fmt.Errorf("open config: %w", err)
}
- return &Server{
- database: database,
- }, nil
+ // TODO: Should this belong here, or in Run()?
+ server.database, err = database.Open(ctx, server.config.DB)
+ if err != nil {
+ return server, fmt.Errorf("open database: %w", err)
+ }
+
+ return server, nil
+}
+
+func (s *Server) Run() error {
+ // TODO: Not running git2d because it should be run separately.
+ // This needs to be documented somewhere, hence a TODO here for now.
+
+ go func() {
+ s.hookPool = hooki.New(s.config.Hooks)
+ if err := s.hookPool.Run(); err != nil {
+ log.Fatalf("run hook pool: %v", err)
+ }
+ }()
+
+ go func() {
+ s.lmtpPool = lmtp.New(s.config.LMTP)
+ if err := s.lmtpPool.Run(); err != nil {
+ log.Fatalf("run LMTP pool: %v", err)
+ }
+ }()
+
+ return nil
}