replace zxq.co/ripple/hanayo
This commit is contained in:
284
vendor/github.com/rcrowley/goagain/goagain.go
generated
vendored
Normal file
284
vendor/github.com/rcrowley/goagain/goagain.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// Zero-downtime restarts in Go.
|
||||
package goagain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type strategy int
|
||||
|
||||
const (
|
||||
// The Single-exec strategy: parent forks child to exec with an inherited
|
||||
// net.Listener; child kills parent and becomes a child of init(8).
|
||||
Single strategy = iota
|
||||
|
||||
// The Double-exec strategy: parent forks child to exec (first) with an
|
||||
// inherited net.Listener; child signals parent to exec (second); parent
|
||||
// kills child.
|
||||
Double
|
||||
)
|
||||
|
||||
// Don't make the caller import syscall.
|
||||
const (
|
||||
SIGINT = syscall.SIGINT
|
||||
SIGQUIT = syscall.SIGQUIT
|
||||
SIGTERM = syscall.SIGTERM
|
||||
SIGUSR2 = syscall.SIGUSR2
|
||||
)
|
||||
|
||||
var (
|
||||
// OnSIGHUP is the function called when the server receives a SIGHUP
|
||||
// signal. The normal use case for SIGHUP is to reload the
|
||||
// configuration.
|
||||
OnSIGHUP func(l net.Listener) error
|
||||
|
||||
// OnSIGUSR1 is the function called when the server receives a
|
||||
// SIGUSR1 signal. The normal use case for SIGUSR1 is to repon the
|
||||
// log files.
|
||||
OnSIGUSR1 func(l net.Listener) error
|
||||
|
||||
// The strategy to use; Single by default.
|
||||
Strategy strategy = Single
|
||||
)
|
||||
|
||||
// Re-exec this same image without dropping the net.Listener.
|
||||
func Exec(l net.Listener) error {
|
||||
var pid int
|
||||
fmt.Sscan(os.Getenv("GOAGAIN_PID"), &pid)
|
||||
if syscall.Getppid() == pid {
|
||||
return fmt.Errorf("goagain.Exec called by a child process")
|
||||
}
|
||||
argv0, err := lookPath()
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
if _, err := setEnvs(l); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv(
|
||||
"GOAGAIN_SIGNAL",
|
||||
fmt.Sprintf("%d", syscall.SIGQUIT),
|
||||
); nil != err {
|
||||
return err
|
||||
}
|
||||
log.Println("re-executing", argv0)
|
||||
return syscall.Exec(argv0, os.Args, os.Environ())
|
||||
}
|
||||
|
||||
// Fork and exec this same image without dropping the net.Listener.
|
||||
func ForkExec(l net.Listener) error {
|
||||
argv0, err := lookPath()
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
fd, err := setEnvs(l)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("GOAGAIN_PID", ""); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv(
|
||||
"GOAGAIN_PPID",
|
||||
fmt.Sprint(syscall.Getpid()),
|
||||
); nil != err {
|
||||
return err
|
||||
}
|
||||
var sig syscall.Signal
|
||||
if Double == Strategy {
|
||||
sig = syscall.SIGUSR2
|
||||
} else {
|
||||
sig = syscall.SIGQUIT
|
||||
}
|
||||
if err := os.Setenv("GOAGAIN_SIGNAL", fmt.Sprintf("%d", sig)); nil != err {
|
||||
return err
|
||||
}
|
||||
files := make([]*os.File, fd+1)
|
||||
files[syscall.Stdin] = os.Stdin
|
||||
files[syscall.Stdout] = os.Stdout
|
||||
files[syscall.Stderr] = os.Stderr
|
||||
addr := l.Addr()
|
||||
files[fd] = os.NewFile(
|
||||
fd,
|
||||
fmt.Sprintf("%s:%s->", addr.Network(), addr.String()),
|
||||
)
|
||||
p, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
|
||||
Dir: wd,
|
||||
Env: os.Environ(),
|
||||
Files: files,
|
||||
Sys: &syscall.SysProcAttr{},
|
||||
})
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
log.Println("spawned child", p.Pid)
|
||||
if err = os.Setenv("GOAGAIN_PID", fmt.Sprint(p.Pid)); nil != err {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test whether an error is equivalent to net.errClosing as returned by
|
||||
// Accept during a graceful exit.
|
||||
func IsErrClosing(err error) bool {
|
||||
if opErr, ok := err.(*net.OpError); ok {
|
||||
err = opErr.Err
|
||||
}
|
||||
return "use of closed network connection" == err.Error()
|
||||
}
|
||||
|
||||
// Kill process specified in the environment with the signal specified in the
|
||||
// environment; default to SIGQUIT.
|
||||
func Kill() error {
|
||||
var (
|
||||
pid int
|
||||
sig syscall.Signal
|
||||
)
|
||||
_, err := fmt.Sscan(os.Getenv("GOAGAIN_PID"), &pid)
|
||||
if io.EOF == err {
|
||||
_, err = fmt.Sscan(os.Getenv("GOAGAIN_PPID"), &pid)
|
||||
}
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Sscan(os.Getenv("GOAGAIN_SIGNAL"), &sig); nil != err {
|
||||
sig = syscall.SIGQUIT
|
||||
}
|
||||
if syscall.SIGQUIT == sig && Double == Strategy {
|
||||
go syscall.Wait4(pid, nil, 0, nil)
|
||||
}
|
||||
log.Println("sending signal", sig, "to process", pid)
|
||||
return syscall.Kill(pid, sig)
|
||||
}
|
||||
|
||||
// Reconstruct a net.Listener from a file descriptior and name specified in the
|
||||
// environment. Deal with Go's insistence on dup(2)ing file descriptors.
|
||||
func Listener() (l net.Listener, err error) {
|
||||
var fd uintptr
|
||||
if _, err = fmt.Sscan(os.Getenv("GOAGAIN_FD"), &fd); nil != err {
|
||||
return
|
||||
}
|
||||
l, err = net.FileListener(os.NewFile(fd, os.Getenv("GOAGAIN_NAME")))
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
switch l.(type) {
|
||||
case *net.TCPListener, *net.UnixListener:
|
||||
default:
|
||||
err = fmt.Errorf(
|
||||
"file descriptor is %T not *net.TCPListener or *net.UnixListener",
|
||||
l,
|
||||
)
|
||||
return
|
||||
}
|
||||
if err = syscall.Close(int(fd)); nil != err {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Block this goroutine awaiting signals. Signals are handled as they
|
||||
// are by Nginx and Unicorn: <http://unicorn.bogomips.org/SIGNALS.html>.
|
||||
func Wait(l net.Listener) (syscall.Signal, error) {
|
||||
ch := make(chan os.Signal, 2)
|
||||
signal.Notify(
|
||||
ch,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGUSR1,
|
||||
syscall.SIGUSR2,
|
||||
)
|
||||
forked := false
|
||||
for {
|
||||
sig := <-ch
|
||||
log.Println(sig.String())
|
||||
switch sig {
|
||||
|
||||
// SIGHUP should reload configuration.
|
||||
case syscall.SIGHUP:
|
||||
if nil != OnSIGHUP {
|
||||
if err := OnSIGHUP(l); nil != err {
|
||||
log.Println("OnSIGHUP:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SIGINT should exit.
|
||||
case syscall.SIGINT:
|
||||
return syscall.SIGINT, nil
|
||||
|
||||
// SIGQUIT should exit gracefully.
|
||||
case syscall.SIGQUIT:
|
||||
return syscall.SIGQUIT, nil
|
||||
|
||||
// SIGTERM should exit.
|
||||
case syscall.SIGTERM:
|
||||
return syscall.SIGTERM, nil
|
||||
|
||||
// SIGUSR1 should reopen logs.
|
||||
case syscall.SIGUSR1:
|
||||
if nil != OnSIGUSR1 {
|
||||
if err := OnSIGUSR1(l); nil != err {
|
||||
log.Println("OnSIGUSR1:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SIGUSR2 forks and re-execs the first time it is received and execs
|
||||
// without forking from then on.
|
||||
case syscall.SIGUSR2:
|
||||
if forked {
|
||||
return syscall.SIGUSR2, nil
|
||||
}
|
||||
forked = true
|
||||
if err := ForkExec(l); nil != err {
|
||||
return syscall.SIGUSR2, err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lookPath() (argv0 string, err error) {
|
||||
argv0, err = exec.LookPath(os.Args[0])
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
if _, err = os.Stat(argv0); nil != err {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setEnvs(l net.Listener) (fd uintptr, err error) {
|
||||
v := reflect.ValueOf(l).Elem().FieldByName("fd").Elem()
|
||||
fd = uintptr(v.FieldByName("sysfd").Int())
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0)
|
||||
if 0 != e1 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
if err = os.Setenv("GOAGAIN_FD", fmt.Sprint(fd)); nil != err {
|
||||
return
|
||||
}
|
||||
addr := l.Addr()
|
||||
if err = os.Setenv(
|
||||
"GOAGAIN_NAME",
|
||||
fmt.Sprintf("%s:%s->", addr.Network(), addr.String()),
|
||||
); nil != err {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user