runsvinit/main.go

162 lines
2.9 KiB
Go
Raw Normal View History

2015-09-25 10:30:24 +00:00
package main
import (
2015-09-28 20:30:44 +00:00
"flag"
2015-09-25 10:30:24 +00:00
"log"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"syscall"
)
const etcService = "/etc/service"
2015-09-29 12:19:20 +00:00
var (
debugf = log.Printf
info = log.Print
infof = log.Printf
fatal = log.Fatal
fatalf = log.Fatalf
)
2015-09-25 10:30:24 +00:00
func main() {
2015-09-29 12:19:20 +00:00
var (
reap = flag.Bool("reap", true, "reap orphan children")
debug = flag.Bool("debug", false, "log debug information")
)
2015-09-28 20:30:44 +00:00
flag.Parse()
2015-09-25 10:30:24 +00:00
log.SetFlags(0)
2015-09-29 12:19:20 +00:00
if !*debug {
debugf = func(string, ...interface{}) {}
}
2015-09-25 10:30:24 +00:00
runsvdir, err := exec.LookPath("runsvdir")
if err != nil {
2015-09-29 12:19:20 +00:00
fatal(err)
2015-09-25 10:30:24 +00:00
}
sv, err := exec.LookPath("sv")
if err != nil {
2015-09-29 12:19:20 +00:00
fatal(err)
2015-09-25 10:30:24 +00:00
}
if fi, err := os.Stat(etcService); err != nil {
2015-09-29 12:19:20 +00:00
fatal(err)
2015-09-25 10:30:24 +00:00
} else if !fi.IsDir() {
2015-09-29 12:19:20 +00:00
fatalf("%s is not a directory", etcService)
2015-09-25 10:30:24 +00:00
}
if pid := os.Getpid(); pid != 1 {
2015-09-29 12:19:20 +00:00
debugf("warning: I'm not PID 1, I'm PID %d", pid)
2015-09-25 10:30:24 +00:00
}
2015-09-28 20:30:44 +00:00
if *reap {
go reapLoop()
} else {
2015-09-29 12:19:20 +00:00
infof("warning: NOT reaping zombies")
2015-09-28 20:30:44 +00:00
}
2015-09-25 10:30:24 +00:00
supervisor := cmd(runsvdir, etcService)
if err := supervisor.Start(); err != nil {
2015-09-29 12:19:20 +00:00
fatal(err)
2015-09-25 10:30:24 +00:00
}
2015-09-29 12:19:20 +00:00
debugf("%s started", runsvdir)
2015-09-25 10:30:24 +00:00
2015-09-25 14:14:09 +00:00
go shutdown(sv, supervisor.Process)
2015-09-25 10:30:24 +00:00
if err := supervisor.Wait(); err != nil {
2015-09-29 12:19:20 +00:00
infof("%s exited with error: %v", runsvdir, err)
2015-09-25 10:30:24 +00:00
} else {
2015-09-29 12:19:20 +00:00
debugf("%s exited cleanly", runsvdir)
2015-09-25 10:30:24 +00:00
}
}
2015-09-28 20:30:44 +00:00
// From https://github.com/ramr/go-reaper/blob/master/reaper.go
func reapLoop() {
2015-09-25 10:30:24 +00:00
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGCHLD)
for range c {
2015-09-28 20:30:44 +00:00
reapChildren()
2015-09-25 10:30:24 +00:00
}
}
2015-09-28 20:30:44 +00:00
func reapChildren() {
2015-09-25 10:30:24 +00:00
for {
2015-09-28 20:30:44 +00:00
var (
ws syscall.WaitStatus
pid int
err error
)
for {
pid, err = syscall.Wait4(-1, &ws, 0, nil)
if err != syscall.EINTR {
break
}
2015-09-25 10:30:24 +00:00
}
2015-09-28 20:30:44 +00:00
if err == syscall.ECHILD {
return // done
}
2015-09-29 12:19:20 +00:00
infof("reaped child process %d (%+v)", pid, ws)
2015-09-25 10:30:24 +00:00
}
}
type signaler interface {
Signal(os.Signal) error
}
2015-09-25 14:14:09 +00:00
func shutdown(sv string, s signaler) {
2015-09-25 10:30:24 +00:00
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
2015-09-25 14:14:09 +00:00
sig := <-c
2015-09-29 12:19:20 +00:00
debugf("received %s", sig)
2015-09-25 10:30:24 +00:00
matches, err := filepath.Glob(filepath.Join(etcService, "*"))
if err != nil {
2015-09-29 12:19:20 +00:00
infof("when shutting down services: %v", err)
2015-09-25 10:30:24 +00:00
return
}
var stopped []string
for _, match := range matches {
fi, err := os.Stat(match)
if err != nil {
2015-09-29 12:19:20 +00:00
infof("%s: %v", match, err)
2015-09-25 10:30:24 +00:00
continue
}
if !fi.IsDir() {
2015-09-29 12:19:20 +00:00
infof("%s: not a directory", match)
2015-09-25 10:30:24 +00:00
continue
}
service := filepath.Base(match)
stop := cmd(sv, "stop", service)
if err := stop.Run(); err != nil {
2015-09-29 12:19:20 +00:00
infof("%s: %v", strings.Join(stop.Args, " "), err)
2015-09-25 10:30:24 +00:00
continue
}
stopped = append(stopped, service)
}
2015-09-29 12:19:20 +00:00
debugf("stopped %d: %s", len(stopped), strings.Join(stopped, ", "))
debugf("stopping supervisor with signal %s...", sig)
2015-09-25 14:14:09 +00:00
if err := s.Signal(sig); err != nil {
2015-09-29 12:19:20 +00:00
info(err)
2015-09-25 10:30:24 +00:00
}
2015-09-29 12:19:20 +00:00
debugf("shutdown handler exiting")
2015-09-25 10:30:24 +00:00
}
func cmd(path string, args ...string) *exec.Cmd {
return &exec.Cmd{
Path: path,
Args: append([]string{path}, args...),
Env: os.Environ(),
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
}