gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
  4. //go:build windows
  5. package graceful
  6. import (
  7. "os"
  8. "runtime/pprof"
  9. "strconv"
  10. "time"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/setting"
  13. "golang.org/x/sys/windows/svc"
  14. "golang.org/x/sys/windows/svc/debug"
  15. )
  16. // WindowsServiceName is the name of the Windows service
  17. var WindowsServiceName = "gitea"
  18. const (
  19. hammerCode = 128
  20. hammerCmd = svc.Cmd(hammerCode)
  21. acceptHammerCode = svc.Accepted(hammerCode)
  22. )
  23. func (g *Manager) start() {
  24. // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager
  25. pprof.SetGoroutineLabels(g.managerCtx)
  26. defer pprof.SetGoroutineLabels(g.ctx)
  27. if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
  28. log.Trace("Skipping SVC check as SKIP_MINWINSVC is set")
  29. return
  30. }
  31. // Make SVC process
  32. run := svc.Run
  33. isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck // must use IsAnInteractiveSession because IsWindowsService has a different permissions profile
  34. if err != nil {
  35. log.Error("Unable to ascertain if running as an Windows Service: %v", err)
  36. return
  37. }
  38. if isAnInteractiveSession {
  39. log.Trace("Not running a service ... using the debug SVC manager")
  40. run = debug.Run
  41. }
  42. go func() {
  43. _ = run(WindowsServiceName, g)
  44. }()
  45. }
  46. // Execute makes Manager implement svc.Handler
  47. func (g *Manager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
  48. if setting.StartupTimeout > 0 {
  49. status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)}
  50. } else {
  51. status <- svc.Status{State: svc.StartPending}
  52. }
  53. log.Trace("Awaiting server start-up")
  54. // Now need to wait for everything to start...
  55. if !g.awaitServer(setting.StartupTimeout) {
  56. log.Trace("... start-up failed ... Stopped")
  57. return false, 1
  58. }
  59. log.Trace("Sending Running state to SVC")
  60. // We need to implement some way of svc.AcceptParamChange/svc.ParamChange
  61. status <- svc.Status{
  62. State: svc.Running,
  63. Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
  64. }
  65. log.Trace("Started")
  66. waitTime := 30 * time.Second
  67. loop:
  68. for {
  69. select {
  70. case <-g.ctx.Done():
  71. log.Trace("Shutting down")
  72. g.DoGracefulShutdown()
  73. waitTime += setting.GracefulHammerTime
  74. break loop
  75. case <-g.shutdownRequested:
  76. log.Trace("Shutting down")
  77. waitTime += setting.GracefulHammerTime
  78. break loop
  79. case change := <-changes:
  80. switch change.Cmd {
  81. case svc.Interrogate:
  82. log.Trace("SVC sent interrogate")
  83. status <- change.CurrentStatus
  84. case svc.Stop, svc.Shutdown:
  85. log.Trace("SVC requested shutdown - shutting down")
  86. g.DoGracefulShutdown()
  87. waitTime += setting.GracefulHammerTime
  88. break loop
  89. case hammerCode:
  90. log.Trace("SVC requested hammer - shutting down and hammering immediately")
  91. g.DoGracefulShutdown()
  92. g.DoImmediateHammer()
  93. break loop
  94. default:
  95. log.Debug("Unexpected control request: %v", change.Cmd)
  96. }
  97. }
  98. }
  99. log.Trace("Sending StopPending state to SVC")
  100. status <- svc.Status{
  101. State: svc.StopPending,
  102. WaitHint: uint32(waitTime / time.Millisecond),
  103. }
  104. hammerLoop:
  105. for {
  106. select {
  107. case change := <-changes:
  108. switch change.Cmd {
  109. case svc.Interrogate:
  110. log.Trace("SVC sent interrogate")
  111. status <- change.CurrentStatus
  112. case svc.Stop, svc.Shutdown, hammerCmd:
  113. log.Trace("SVC requested hammer - hammering immediately")
  114. g.DoImmediateHammer()
  115. break hammerLoop
  116. default:
  117. log.Debug("Unexpected control request: %v", change.Cmd)
  118. }
  119. case <-g.hammerCtx.Done():
  120. break hammerLoop
  121. }
  122. }
  123. log.Trace("Stopped")
  124. return false, 0
  125. }
  126. func (g *Manager) awaitServer(limit time.Duration) bool {
  127. c := make(chan struct{})
  128. go func() {
  129. g.createServerCond.L.Lock()
  130. for {
  131. if g.createdServer >= numberOfServersToCreate {
  132. g.createServerCond.L.Unlock()
  133. close(c)
  134. return
  135. }
  136. select {
  137. case <-g.IsShutdown():
  138. g.createServerCond.L.Unlock()
  139. return
  140. default:
  141. }
  142. g.createServerCond.Wait()
  143. }
  144. }()
  145. var tc <-chan time.Time
  146. if limit > 0 {
  147. tc = time.After(limit)
  148. }
  149. select {
  150. case <-c:
  151. return true // completed normally
  152. case <-tc:
  153. return false // timed out
  154. case <-g.IsShutdown():
  155. g.createServerCond.Signal()
  156. return false
  157. }
  158. }
  159. func (g *Manager) notify(msg systemdNotifyMsg) {
  160. // Windows doesn't use systemd to notify
  161. }
  162. func KillParent() {
  163. // Windows doesn't need to "kill parent" because there is no graceful restart
  164. }