monitor.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at http://mozilla.org/MPL/2.0/.
  6. package main
  7. import (
  8. "bufio"
  9. "io"
  10. "os"
  11. "os/exec"
  12. "os/signal"
  13. "runtime"
  14. "strings"
  15. "syscall"
  16. "time"
  17. "github.com/syncthing/syncthing/internal/osutil"
  18. "github.com/syncthing/syncthing/internal/sync"
  19. )
  20. var (
  21. stdoutFirstLines []string // The first 10 lines of stdout
  22. stdoutLastLines []string // The last 50 lines of stdout
  23. stdoutMut = sync.NewMutex()
  24. )
  25. const (
  26. countRestarts = 4
  27. loopThreshold = 60 * time.Second
  28. )
  29. func monitorMain() {
  30. os.Setenv("STNORESTART", "yes")
  31. os.Setenv("STMONITORED", "yes")
  32. l.SetPrefix("[monitor] ")
  33. var err error
  34. var dst io.Writer = os.Stdout
  35. if logFile != "" {
  36. var fileDst io.Writer
  37. fileDst, err = os.Create(logFile)
  38. if err != nil {
  39. l.Fatalln("log file:", err)
  40. }
  41. if runtime.GOOS == "windows" {
  42. // Translate line breaks to Windows standard
  43. fileDst = osutil.ReplacingWriter{
  44. Writer: fileDst,
  45. From: '\n',
  46. To: []byte{'\r', '\n'},
  47. }
  48. }
  49. // Log to both stdout and file.
  50. dst = io.MultiWriter(dst, fileDst)
  51. l.Infof(`Log output saved to file "%s"`, logFile)
  52. }
  53. args := os.Args
  54. var restarts [countRestarts]time.Time
  55. sign := make(chan os.Signal, 1)
  56. sigTerm := syscall.Signal(0xf)
  57. signal.Notify(sign, os.Interrupt, sigTerm, os.Kill)
  58. for {
  59. if t := time.Since(restarts[0]); t < loopThreshold {
  60. l.Warnf("%d restarts in %v; not retrying further", countRestarts, t)
  61. os.Exit(exitError)
  62. }
  63. copy(restarts[0:], restarts[1:])
  64. restarts[len(restarts)-1] = time.Now()
  65. cmd := exec.Command(args[0], args[1:]...)
  66. stderr, err := cmd.StderrPipe()
  67. if err != nil {
  68. l.Fatalln("stderr:", err)
  69. }
  70. stdout, err := cmd.StdoutPipe()
  71. if err != nil {
  72. l.Fatalln("stdout:", err)
  73. }
  74. l.Infoln("Starting syncthing")
  75. err = cmd.Start()
  76. if err != nil {
  77. l.Fatalln(err)
  78. }
  79. // Let the next child process know that this is not the first time
  80. // it's starting up.
  81. os.Setenv("STRESTART", "yes")
  82. stdoutMut.Lock()
  83. stdoutFirstLines = make([]string, 0, 10)
  84. stdoutLastLines = make([]string, 0, 50)
  85. stdoutMut.Unlock()
  86. wg := sync.NewWaitGroup()
  87. wg.Add(1)
  88. go func() {
  89. copyStderr(stderr, dst)
  90. wg.Done()
  91. }()
  92. wg.Add(1)
  93. go func() {
  94. copyStdout(stdout, dst)
  95. wg.Done()
  96. }()
  97. exit := make(chan error)
  98. go func() {
  99. wg.Wait()
  100. exit <- cmd.Wait()
  101. }()
  102. select {
  103. case s := <-sign:
  104. l.Infof("Signal %d received; exiting", s)
  105. cmd.Process.Kill()
  106. <-exit
  107. return
  108. case err = <-exit:
  109. if err == nil {
  110. // Successful exit indicates an intentional shutdown
  111. return
  112. } else if exiterr, ok := err.(*exec.ExitError); ok {
  113. if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
  114. switch status.ExitStatus() {
  115. case exitUpgrading:
  116. // Restart the monitor process to release the .old
  117. // binary as part of the upgrade process.
  118. l.Infoln("Restarting monitor...")
  119. os.Setenv("STNORESTART", "")
  120. err := exec.Command(args[0], args[1:]...).Start()
  121. if err != nil {
  122. l.Warnln("restart:", err)
  123. }
  124. return
  125. }
  126. }
  127. }
  128. }
  129. l.Infoln("Syncthing exited:", err)
  130. time.Sleep(1 * time.Second)
  131. }
  132. }
  133. func copyStderr(stderr io.Reader, dst io.Writer) {
  134. br := bufio.NewReader(stderr)
  135. var panicFd *os.File
  136. for {
  137. line, err := br.ReadString('\n')
  138. if err != nil {
  139. return
  140. }
  141. if panicFd == nil {
  142. dst.Write([]byte(line))
  143. if strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:") {
  144. panicFd, err = os.Create(timestampedLoc(locPanicLog))
  145. if err != nil {
  146. l.Warnln("Create panic log:", err)
  147. continue
  148. }
  149. l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name())
  150. l.Warnln("Please create an issue at https://github.com/syncthing/syncthing/issues/ with the panic log attached")
  151. stdoutMut.Lock()
  152. for _, line := range stdoutFirstLines {
  153. panicFd.WriteString(line)
  154. }
  155. panicFd.WriteString("...\n")
  156. for _, line := range stdoutLastLines {
  157. panicFd.WriteString(line)
  158. }
  159. stdoutMut.Unlock()
  160. }
  161. panicFd.WriteString("Panic at " + time.Now().Format(time.RFC3339) + "\n")
  162. }
  163. if panicFd != nil {
  164. panicFd.WriteString(line)
  165. }
  166. }
  167. }
  168. func copyStdout(stdout io.Reader, dst io.Writer) {
  169. br := bufio.NewReader(stdout)
  170. for {
  171. line, err := br.ReadString('\n')
  172. if err != nil {
  173. return
  174. }
  175. stdoutMut.Lock()
  176. if len(stdoutFirstLines) < cap(stdoutFirstLines) {
  177. stdoutFirstLines = append(stdoutFirstLines, line)
  178. } else {
  179. if l := len(stdoutLastLines); l == cap(stdoutLastLines) {
  180. stdoutLastLines = stdoutLastLines[:l-1]
  181. }
  182. stdoutLastLines = append(stdoutLastLines, line)
  183. }
  184. stdoutMut.Unlock()
  185. dst.Write([]byte(line))
  186. }
  187. }