Files
2026-05-20 14:29:39 -04:00

206 lines
4.8 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"runtime"
"runtime/pprof"
"strconv"
"strings"
"syscall"
"time"
"git.stormux.org/storm/skald/diskwriter"
"git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/ice"
"git.stormux.org/storm/skald/limit"
"git.stormux.org/storm/skald/token"
"git.stormux.org/storm/skald/turnserver"
"git.stormux.org/storm/skald/webserver"
)
func main() {
var cpuprofile, memprofile, mutexprofile, httpAddr string
var udpRange string
flag.StringVar(&httpAddr, "http", ":8443", "web server `address`")
flag.StringVar(&webserver.StaticRoot, "static", "./static/",
"web server root `directory`")
flag.BoolVar(&webserver.Insecure, "insecure", false,
"act as an HTTP server rather than HTTPS")
flag.StringVar(&hall.DataDirectory, "data", "./data/",
"data `directory`")
flag.StringVar(&hall.Directory, "halls", "./halls/",
"hall description `directory`")
flag.StringVar(&diskwriter.Directory, "recordings", "./recordings/",
"recordings `directory`")
flag.StringVar(&cpuprofile, "cpuprofile", "",
"store CPU profile in `file`")
flag.StringVar(&memprofile, "memprofile", "",
"store memory profile in `file`")
flag.StringVar(&mutexprofile, "mutexprofile", "",
"store mutex profile in `file`")
flag.StringVar(&udpRange, "udp-range", "",
"UDP `port` (multiplexing) or port1-port2 (range)")
flag.BoolVar(&hall.UseMDNS, "mdns", false, "gather mDNS addresses")
flag.BoolVar(&ice.ICERelayOnly, "relay-only", false,
"require use of TURN relays for all media traffic")
flag.StringVar(&turnserver.Address, "turn", "auto",
"built-in TURN server `address` (\"\" to disable)")
flag.Parse()
if udpRange != "" {
if strings.ContainsRune(udpRange, '-') {
var min, max uint16
n, err := fmt.Sscanf(udpRange, "%v-%v", &min, &max)
if err != nil || n != 2 {
log.Fatalf("UDP range: %v", err)
}
if n != 2 || min <= 0 || max <= 0 || min > max {
log.Fatalf("UDP range: bad range")
}
hall.UDPMin = min
hall.UDPMax = max
} else {
port, err := strconv.Atoi(udpRange)
if err != nil {
log.Fatalf("UDP: %v", err)
}
err = hall.SetUDPMux(port)
if err != nil {
log.Fatalf("UDP: %v", err)
}
}
}
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
if err != nil {
log.Printf("Create(cpuprofile): %v", err)
return
}
pprof.StartCPUProfile(f)
defer func() {
pprof.StopCPUProfile()
f.Close()
}()
}
if memprofile != "" {
defer func() {
f, err := os.Create(memprofile)
if err != nil {
log.Printf("Create(memprofile): %v", err)
return
}
pprof.WriteHeapProfile(f)
f.Close()
}()
}
if mutexprofile != "" {
runtime.SetMutexProfileFraction(1)
defer func() {
f, err := os.Create(mutexprofile)
if err != nil {
log.Printf("Create(mutexprofile): %v", err)
return
}
pprof.Lookup("mutex").WriteTo(f, 0)
f.Close()
}()
}
n, err := limit.Nofile()
if err != nil {
log.Printf("Couldn't get file descriptor limit: %v", err)
} else if n < 0xFFFF {
log.Printf("File descriptor limit is %v, please increase it!", n)
}
ice.ICEFilename = filepath.Join(hall.DataDirectory, "ice-servers.json")
token.SetStatefulFilename(
filepath.Join(
filepath.Join(hall.DataDirectory, "var"),
"tokens.jsonl",
),
)
// make sure the list of public halls is updated early
go hall.Update()
// causes the built-in server to start if required
ice.Update()
defer turnserver.Stop()
err = webserver.Serve(httpAddr, hall.DataDirectory)
if err != nil {
log.Fatalf("Server: %v", err)
}
terminate := make(chan os.Signal, 1)
signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)
go relayTest()
ticker := time.NewTicker(15 * time.Minute)
defer ticker.Stop()
slowTicker := time.NewTicker(12 * time.Hour)
defer slowTicker.Stop()
for {
select {
case <-ticker.C:
go func() {
hall.Update()
token.Expire()
cleanupRecordings()
}()
case <-slowTicker.C:
go relayTest()
case <-terminate:
webserver.Shutdown()
return
}
}
}
func cleanupRecordings() {
conf, err := hall.GetConfiguration()
if err != nil {
log.Printf("Recording cleanup: %v", err)
return
}
if conf.RecordingRetention == "" {
return
}
retention, err := time.ParseDuration(conf.RecordingRetention)
if err != nil {
log.Printf("Recording cleanup: bad recordingRetention %q: %v",
conf.RecordingRetention, err)
return
}
if retention <= 0 {
return
}
if err := diskwriter.CleanupOldRecordings(retention); err != nil {
log.Printf("Recording cleanup: %v", err)
}
}
func relayTest() {
now := time.Now()
d, err := ice.RelayTest(20 * time.Second)
if err != nil {
log.Printf("Relay test failed: %v", err)
log.Printf("Perhaps you didn't configure a TURN server?")
return
}
log.Printf("Relay test successful in %v, RTT = %v", time.Since(now), d)
}