ready to test: proxy

This commit is contained in:
Suyono 2023-11-03 12:12:11 +11:00
parent 45d1c50501
commit e6e9bf291b
3 changed files with 180 additions and 1 deletions

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -137,7 +137,7 @@ WaitPidLoop:
func main() {
if len(os.Args) < 3 {
_, _ = fmt.Fprintln(os.Stderr, "invalid parameter")
_, _ = fmt.Fprintln(os.Stderr, "usage: pidproxy /path/to/pid/file program arguments ...")
_, _ = fmt.Fprintln(os.Stderr, "usage: pid1 /path/to/pid/file program arguments ...")
os.Exit(2)
}

173
proxy/proxy.go Normal file
View File

@ -0,0 +1,173 @@
package main
import (
"errors"
"fmt"
"golang.org/x/sys/unix"
"io/fs"
"log"
"os"
"os/exec"
"os/signal"
"strconv"
"syscall"
"time"
)
type pidProxy struct {
pidPath string
targetArgs []string
incomingSignal chan os.Signal
}
var (
b = make([]byte, 64)
)
func (p *pidProxy) RunStarter() {
var (
c *exec.Cmd
)
notRunning := true
starterLoop:
for notRunning {
if len(p.targetArgs) == 1 {
c = exec.Command(p.targetArgs[0])
} else {
c = exec.Command(p.targetArgs[0], p.targetArgs[1:]...)
}
err := c.Run()
if err != nil {
log.Println("fail to run starter:", err)
continue starterLoop
}
notRunning = false
}
}
func (p *pidProxy) ClearPidFile() {
f, err := os.OpenFile(p.pidPath, unix.O_WRONLY|unix.O_TRUNC, 0644)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return
}
log.Println("cannot clear pid file:", err)
}
defer func() {
_ = f.Close()
}()
}
func (p *pidProxy) SignalTargetProcess(sig syscall.Signal) {
var (
pidStr string
f *os.File
err error
n int
pid int
)
pidStr = ""
func() {
f, err = os.Open(p.pidPath)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
log.Println("failed to open pid file:", p.pidPath, ":", err)
}
return
}
defer func() {
_ = f.Close()
}()
n, err = f.Read(b)
if err != nil {
log.Println("failed to read pid file:", p.pidPath, ":", err)
return
}
if n > 0 {
pidStr = string(b[:n])
}
}()
if len(pidStr) > 0 {
pid, err = strconv.Atoi(pidStr)
if err != nil {
log.Println("failed to parse pid", pidStr, ":", err)
return
}
err = unix.Kill(pid, sig)
if err != nil {
if errors.Is(err, unix.ESRCH) && sig == syscall.Signal(0) {
// target not found, start a new one
p.ClearPidFile()
go p.RunStarter()
} else if errors.Is(err, unix.EPERM) {
log.Println("I have no permission to check the target process")
} else {
log.Println("unexpected error:", err)
}
return
}
}
}
func (p *pidProxy) Watcher() {
var (
tr *time.Timer
is os.Signal
us unix.Signal
ok bool
)
tr = time.NewTimer(time.Second)
watchLoop:
for {
select {
case <-tr.C:
p.SignalTargetProcess(syscall.Signal(0))
tr = time.NewTimer(time.Second)
case is = <-p.incomingSignal:
us, ok = is.(unix.Signal)
if !ok {
log.Println("FATAL: implementation problem, cannot assert to unix.Signal")
continue watchLoop
}
switch us {
case unix.SIGTERM, unix.SIGINT:
p.SignalTargetProcess(us)
break watchLoop
case unix.SIGCHLD:
}
}
}
if !tr.Stop() {
<-tr.C
}
}
func main() {
if len(os.Args) < 3 {
_, _ = fmt.Fprintln(os.Stderr, "invalid parameter")
_, _ = fmt.Fprintln(os.Stderr, "usage: pidproxy /path/to/pid/file program arguments ...")
os.Exit(2)
}
p := &pidProxy{
pidPath: os.Args[1],
targetArgs: os.Args[2:],
incomingSignal: make(chan os.Signal, 1),
}
p.ClearPidFile()
go p.RunStarter()
signal.Notify(p.incomingSignal, unix.SIGINT, unix.SIGTERM, unix.SIGCHLD)
p.Watcher()
}