broadcast/cmd/broadcast/main.go

209 lines
3.8 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/go-chi/chi/v5"
"git.abbiamoundominio.org/blallo/broadcast"
"git.sr.ht/~blallo/logz/interface"
"git.sr.ht/~blallo/logz/zlog"
)
var (
debug = flag.Bool("debug", false, "Enable debug logging")
addr = flag.String("addr", ":8080", "Addres to bind to, in the 'ipaddress:port' format")
)
func main() {
logger := zlog.NewConsoleLogger()
flag.Parse()
if flag.NArg() < 1 {
logger.Err(map[string]any{
"msg": "Wrong number of arguments",
})
os.Exit(1)
}
if *debug {
logger.SetLevel(logz.LogDebug)
}
prog := flag.Arg(0)
cmdLine := flag.Args()[1:]
radio, err := broadcast.NewRadio(logger, prog, cmdLine...)
if err != nil {
logger.Err(map[string]any{
"msg": "Failed to start",
"err": err,
})
os.Exit(2)
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
start := make(chan os.Signal)
stop := make(chan os.Signal)
status := make(chan os.Signal)
signal.Notify(start, syscall.SIGUSR1)
signal.Notify(stop, syscall.SIGTSTP)
signal.Notify(status, syscall.SIGUSR2)
go func() {
for {
select {
case <-ctx.Done():
return
case <-start:
resp := <-radio.Start()
if resp != nil {
logger.Warn(map[string]any{
"msg": "Failed to start",
"context": "os",
"err": resp.(error).Error(),
})
} else {
logger.Info(map[string]any{
"msg": "Started",
"context": "os",
})
}
}
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
case <-stop:
resp := <-radio.Stop()
if resp != nil {
logger.Warn(map[string]any{
"msg": "Failed to stop",
"context": "os",
"err": resp.(error).Error(),
})
} else {
logger.Info(map[string]any{
"msg": "Stopped",
"context": "os",
})
}
}
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
case <-status:
resp := <-radio.Status()
for i, line := range resp.([]string) {
logger.Info(map[string]any{
"msg": line,
"context": "os",
"lineNum": i,
})
}
}
}
}()
go func() {
resp, err := withTimeout(ctx, radio.Start())
if err != nil {
logger.Err(map[string]any{
"msg": "Cannot start",
"context": "os",
"err": err.Error(),
})
} else {
if resp != nil {
logger.Info(map[string]any{
"msg": "Started",
"context": "os",
"resp": resp.(error).Error(),
})
}
}
}()
go setupHandler(radio, logger, *addr)
if err := radio.Run(ctx); err != nil {
logger.Err(map[string]any{
"msg": "Execution failed",
"err": err.Error(),
})
os.Exit(2)
}
}
func withTimeout[T any](ctx context.Context, respCh <-chan T) (zero T, err error) {
shortCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
select {
case <-shortCtx.Done():
return zero, shortCtx.Err()
case resp := <-respCh:
return resp, nil
}
}
func setupHandler(radio *broadcast.Radio, logger logz.Logger, addr string) error {
handler := &radioHandler{
radio: radio,
logger: logger,
}
wsHandler := &livenessHandler{
radio: radio,
logger: logger,
}
testUI := &testUIHandler{
baseAddr: getBaseAddr(addr),
logger: logger,
}
router := chi.NewRouter()
router.Post("/start", handler.Start)
router.Post("/stop", handler.Stop)
router.Get("/status", handler.Status)
router.Handle("/liveness", wsHandler)
router.Get("/test_ui", testUI.TestUI)
return http.ListenAndServe(addr, router)
}
func getBaseAddr(addr string) string {
parts := strings.Split(addr, ":")
if parts[0] == "" {
parts[0] = "localhost"
}
if len(parts) == 2 {
return fmt.Sprintf("%s:%s", parts[0], parts[1])
}
return parts[0]
}