123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- package respawn
- import (
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "os/signal"
- "runtime"
- "strings"
- "sync"
- "syscall"
- "time"
- "github.com/rancher/os/config"
- "github.com/rancher/os/pkg/log"
- "github.com/codegangsta/cli"
- )
- var (
- running = true
- processes = map[int]*os.Process{}
- processLock = sync.Mutex{}
- )
- func Main() {
- log.InitLogger()
- runtime.GOMAXPROCS(1)
- runtime.LockOSThread()
- app := cli.NewApp()
- app.Name = os.Args[0]
- app.Usage = fmt.Sprintf("%s RancherOS\nbuilt: %s", app.Name, config.BuildDate)
- app.Version = config.Version
- app.Author = "Rancher Labs, Inc."
- app.Flags = []cli.Flag{
- cli.StringFlag{
- Name: "file, f",
- Usage: "Optional config file to load",
- },
- }
- app.Action = run
- log.Infof("%s, %s", app.Usage, app.Version)
- fmt.Printf("%s, %s", app.Usage, app.Version)
- 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)
- }
- lines := strings.Split(string(input), "\n")
- doneChannel := make(chan string, len(lines))
- for _, line := range lines {
- if strings.TrimSpace(line) == "" || strings.Index(strings.TrimSpace(line), "#") == 0 {
- continue
- }
- go execute(line, doneChannel)
- }
- for i := 0; i < len(lines); i++ {
- line := <-doneChannel
- log.Infof("FINISHED: %s", line)
- fmt.Printf("FINISHED: %s", line)
- }
- 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 {
- log.Infof("sending SIGTERM to %d", process.Pid)
- process.Signal(syscall.SIGTERM)
- }
- }
- func execute(line string, doneChannel chan string) {
- defer func() { doneChannel <- line }()
- start := time.Now()
- count := 0
- args := strings.Split(line, " ")
- for {
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Setsid: true,
- }
- if err := cmd.Start(); err == nil {
- addProcess(cmd.Process)
- if err = cmd.Wait(); err != nil {
- log.Errorf("Wait cmd to exit: %s, err: %v", line, err)
- }
- removeProcess(cmd.Process)
- } else {
- log.Errorf("Start cmd: %s, err: %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()
- }
- }
- }
|