123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- package respawn
- import (
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "os/signal"
- "runtime"
- "strings"
- "sync"
- "syscall"
- "time"
- "github.com/codegangsta/cli"
- "github.com/rancher/os/log"
- )
- var (
- running = true
- processes = map[int]*os.Process{}
- processLock = sync.Mutex{}
- )
- func Main() {
- log.InitLogger()
- runtime.GOMAXPROCS(1)
- runtime.LockOSThread()
- app := cli.NewApp()
- app.Flags = []cli.Flag{
- cli.StringFlag{
- Name: "file, f",
- Usage: "Optional config file to load",
- },
- }
- app.Action = run
- app.Run(os.Args)
- }
- func setupSigterm() {
- sigtermChan := make(chan os.Signal)
- signal.Notify(sigtermChan, syscall.SIGTERM)
- go func() {
- for range sigtermChan {
- termPids()
- }
- }()
- }
- func run(c *cli.Context) error {
- setupSigterm()
- var stream io.Reader = os.Stdin
- var err error
- inputFileName := c.String("file")
- if inputFileName != "" {
- stream, err = os.Open(inputFileName)
- if err != nil {
- log.Fatal(err)
- }
- }
- input, err := ioutil.ReadAll(stream)
- if err != nil {
- panic(err)
- }
- var wg sync.WaitGroup
- for _, line := range strings.Split(string(input), "\n") {
- if strings.TrimSpace(line) == "" || strings.Index(strings.TrimSpace(line), "#") == 0 {
- continue
- }
- wg.Add(1)
- go execute(line, &wg)
- }
- wg.Wait()
- return nil
- }
- func addProcess(process *os.Process) {
- processLock.Lock()
- defer processLock.Unlock()
- processes[process.Pid] = process
- }
- func removeProcess(process *os.Process) {
- processLock.Lock()
- defer processLock.Unlock()
- delete(processes, process.Pid)
- }
- func termPids() {
- running = false
- processLock.Lock()
- defer processLock.Unlock()
- for _, process := range processes {
- process.Signal(syscall.SIGTERM)
- }
- }
- func execute(line string, wg *sync.WaitGroup) {
- defer wg.Done()
- start := time.Now()
- count := 0
- for {
- args := strings.Split(line, " ")
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Setsid: true,
- }
- err := cmd.Start()
- if err != nil {
- log.Errorf("%s : %v", line, err)
- }
- if err == nil {
- addProcess(cmd.Process)
- err = cmd.Wait()
- removeProcess(cmd.Process)
- }
- if err != nil {
- log.Errorf("%s : %v", line, err)
- }
- if !running {
- log.Infof("%s : not restarting, exiting", line)
- break
- }
- count++
- if count > 10 {
- if time.Now().Sub(start) <= (1 * time.Second) {
- log.Errorf("%s : restarted too fast, not executing", line)
- break
- }
- count = 0
- start = time.Now()
- }
- }
- }
|