diff --git a/main.go b/main.go index 97f9143..904902b 100644 --- a/main.go +++ b/main.go @@ -17,9 +17,14 @@ import ( "fmt" "io" "log" + "net" "net/http" + "os" + "os/exec" "path" + "path/filepath" "strings" + "syscall" "9fans.net/go/plan9" "9fans.net/go/plan9/client" @@ -35,8 +40,100 @@ var ( addr = flag.String("addr", "", "9P server address; empty = use namespace service") ) +const pidFile = "ollie-httpgw.pid" + func main() { + if len(os.Args) >= 2 { + subcmd := os.Args[1] + switch subcmd { + case "start", "fgstart", "stop", "status": + flag.CommandLine.Parse(os.Args[2:]) //nolint:errcheck + ns := client.Namespace() + if ns == "" { + fmt.Fprintln(os.Stderr, "no namespace") + os.Exit(1) + } + pidPath := filepath.Join(ns, pidFile) + switch subcmd { + case "start": + if gwRunning() { + fmt.Println("ollie-httpgw already running") + os.Exit(0) + } + daemonize() + case "fgstart": + if gwRunning() { + fmt.Println("ollie-httpgw already running") + os.Exit(0) + } + runGateway(pidPath) + case "stop": + stopGateway(pidPath) + case "status": + if gwRunning() { + fmt.Println("ollie-httpgw running") + } else { + fmt.Println("ollie-httpgw not running") + os.Exit(1) + } + } + return + } + } + // Legacy: no subcommand — run in foreground directly (no pid file). flag.Parse() + serve() +} + +func gwRunning() bool { + conn, err := net.Dial("tcp", *listen) + if err == nil { + conn.Close() + return true + } + return false +} + +func daemonize() { + exe, _ := os.Executable() + args := []string{"fgstart", "-listen", *listen, "-service", *service} + if *network != "" { + args = append(args, "-net", *network) + } + if *addr != "" { + args = append(args, "-addr", *addr) + } + cmd := exec.Command(exe, args...) + cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + if err := cmd.Start(); err != nil { + fmt.Fprintf(os.Stderr, "failed to start: %v\n", err) + os.Exit(1) + } + fmt.Printf("ollie-httpgw started (pid %d)\n", cmd.Process.Pid) +} + +func stopGateway(pidPath string) { + data, err := os.ReadFile(pidPath) + if err != nil { + fmt.Println("ollie-httpgw not running") + return + } + var pid int + fmt.Sscanf(string(data), "%d", &pid) + if pid > 0 { + syscall.Kill(pid, syscall.SIGTERM) //nolint:errcheck + } + os.Remove(pidPath) //nolint:errcheck + fmt.Println("ollie-httpgw stopped") +} + +func runGateway(pidPath string) { + os.WriteFile(pidPath, fmt.Appendf(nil, "%d", os.Getpid()), 0644) //nolint:errcheck + serve() + os.Remove(pidPath) //nolint:errcheck +} + +func serve() { http.HandleFunc("/openapi.json", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write(openapiSpec)