respawn.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package respawn
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "os/exec"
  8. "os/signal"
  9. "runtime"
  10. "strings"
  11. "sync"
  12. "syscall"
  13. "time"
  14. "github.com/codegangsta/cli"
  15. "github.com/rancher/os/config"
  16. "github.com/rancher/os/log"
  17. )
  18. var (
  19. running = true
  20. processes = map[int]*os.Process{}
  21. processLock = sync.Mutex{}
  22. )
  23. func Main() {
  24. log.InitLogger()
  25. runtime.GOMAXPROCS(1)
  26. runtime.LockOSThread()
  27. app := cli.NewApp()
  28. app.Name = os.Args[0]
  29. app.Usage = fmt.Sprintf("%s RancherOS\nbuilt: %s", app.Name, config.BuildDate)
  30. app.Version = config.Version
  31. app.Author = "Rancher Labs, Inc."
  32. app.Flags = []cli.Flag{
  33. cli.StringFlag{
  34. Name: "file, f",
  35. Usage: "Optional config file to load",
  36. },
  37. }
  38. app.Action = run
  39. log.Infof("%s, %s", app.Usage, app.Version)
  40. fmt.Printf("%s, %s", app.Usage, app.Version)
  41. app.Run(os.Args)
  42. }
  43. func setupSigterm() {
  44. sigtermChan := make(chan os.Signal)
  45. signal.Notify(sigtermChan, syscall.SIGTERM)
  46. go func() {
  47. for range sigtermChan {
  48. termPids()
  49. }
  50. }()
  51. }
  52. func run(c *cli.Context) error {
  53. setupSigterm()
  54. var stream io.Reader = os.Stdin
  55. var err error
  56. inputFileName := c.String("file")
  57. if inputFileName != "" {
  58. stream, err = os.Open(inputFileName)
  59. if err != nil {
  60. log.Fatal(err)
  61. }
  62. }
  63. input, err := ioutil.ReadAll(stream)
  64. if err != nil {
  65. panic(err)
  66. }
  67. lines := strings.Split(string(input), "\n")
  68. doneChannel := make(chan string, len(lines))
  69. for _, line := range lines {
  70. if strings.TrimSpace(line) == "" || strings.Index(strings.TrimSpace(line), "#") == 0 {
  71. continue
  72. }
  73. go execute(line, doneChannel)
  74. }
  75. for i := 0; i < len(lines); i++ {
  76. line := <-doneChannel
  77. log.Infof("FINISHED: %s", line)
  78. fmt.Printf("FINISHED: %s", line)
  79. }
  80. return nil
  81. }
  82. func addProcess(process *os.Process) {
  83. processLock.Lock()
  84. defer processLock.Unlock()
  85. processes[process.Pid] = process
  86. }
  87. func removeProcess(process *os.Process) {
  88. processLock.Lock()
  89. defer processLock.Unlock()
  90. delete(processes, process.Pid)
  91. }
  92. func termPids() {
  93. running = false
  94. processLock.Lock()
  95. defer processLock.Unlock()
  96. for _, process := range processes {
  97. log.Infof("sending SIGTERM to %d", process.Pid)
  98. process.Signal(syscall.SIGTERM)
  99. }
  100. }
  101. func execute(line string, doneChannel chan string) {
  102. defer func() { doneChannel <- line }()
  103. start := time.Now()
  104. count := 0
  105. args := strings.Split(line, " ")
  106. for {
  107. cmd := exec.Command(args[0], args[1:]...)
  108. cmd.Stdout = os.Stdout
  109. cmd.Stderr = os.Stderr
  110. cmd.SysProcAttr = &syscall.SysProcAttr{
  111. Setsid: true,
  112. }
  113. err := cmd.Start()
  114. if err != nil {
  115. log.Errorf("%s : %v", line, err)
  116. }
  117. if err == nil {
  118. addProcess(cmd.Process)
  119. err = cmd.Wait()
  120. removeProcess(cmd.Process)
  121. }
  122. if err != nil {
  123. log.Errorf("%s : %v", line, err)
  124. }
  125. if !running {
  126. log.Infof("%s : not restarting, exiting", line)
  127. break
  128. }
  129. count++
  130. if count > 10 {
  131. if time.Now().Sub(start) <= (1 * time.Second) {
  132. log.Errorf("%s : restarted too fast, not executing", line)
  133. break
  134. }
  135. count = 0
  136. start = time.Now()
  137. }
  138. }
  139. }