You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manager_windows.go 4.6KB

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. //lint:ignore SA1019 We use IsAnInteractiveSession because IsWindowsService has a different permissions profile
  34. isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck
  35. if err != nil {
  36. log.Error("Unable to ascertain if running as an Windows Service: %v", err)
  37. return
  38. }
  39. if isAnInteractiveSession {
  40. log.Trace("Not running a service ... using the debug SVC manager")
  41. run = debug.Run
  42. }
  43. go func() {
  44. _ = run(WindowsServiceName, g)
  45. }()
  46. }
  47. // Execute makes Manager implement svc.Handler
  48. func (g *Manager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
  49. if setting.StartupTimeout > 0 {
  50. status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)}
  51. } else {
  52. status <- svc.Status{State: svc.StartPending}
  53. }
  54. log.Trace("Awaiting server start-up")
  55. // Now need to wait for everything to start...
  56. if !g.awaitServer(setting.StartupTimeout) {
  57. log.Trace("... start-up failed ... Stopped")
  58. return false, 1
  59. }
  60. log.Trace("Sending Running state to SVC")
  61. // We need to implement some way of svc.AcceptParamChange/svc.ParamChange
  62. status <- svc.Status{
  63. State: svc.Running,
  64. Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
  65. }
  66. log.Trace("Started")
  67. waitTime := 30 * time.Second
  68. loop:
  69. for {
  70. select {
  71. case <-g.ctx.Done():
  72. log.Trace("Shutting down")
  73. g.DoGracefulShutdown()
  74. waitTime += setting.GracefulHammerTime
  75. break loop
  76. case <-g.shutdownRequested:
  77. log.Trace("Shutting down")
  78. waitTime += setting.GracefulHammerTime
  79. break loop
  80. case change := <-changes:
  81. switch change.Cmd {
  82. case svc.Interrogate:
  83. log.Trace("SVC sent interrogate")
  84. status <- change.CurrentStatus
  85. case svc.Stop, svc.Shutdown:
  86. log.Trace("SVC requested shutdown - shutting down")
  87. g.DoGracefulShutdown()
  88. waitTime += setting.GracefulHammerTime
  89. break loop
  90. case hammerCode:
  91. log.Trace("SVC requested hammer - shutting down and hammering immediately")
  92. g.DoGracefulShutdown()
  93. g.DoImmediateHammer()
  94. break loop
  95. default:
  96. log.Debug("Unexpected control request: %v", change.Cmd)
  97. }
  98. }
  99. }
  100. log.Trace("Sending StopPending state to SVC")
  101. status <- svc.Status{
  102. State: svc.StopPending,
  103. WaitHint: uint32(waitTime / time.Millisecond),
  104. }
  105. hammerLoop:
  106. for {
  107. select {
  108. case change := <-changes:
  109. switch change.Cmd {
  110. case svc.Interrogate:
  111. log.Trace("SVC sent interrogate")
  112. status <- change.CurrentStatus
  113. case svc.Stop, svc.Shutdown, hammerCmd:
  114. log.Trace("SVC requested hammer - hammering immediately")
  115. g.DoImmediateHammer()
  116. break hammerLoop
  117. default:
  118. log.Debug("Unexpected control request: %v", change.Cmd)
  119. }
  120. case <-g.hammerCtx.Done():
  121. break hammerLoop
  122. }
  123. }
  124. log.Trace("Stopped")
  125. return false, 0
  126. }
  127. func (g *Manager) awaitServer(limit time.Duration) bool {
  128. c := make(chan struct{})
  129. go func() {
  130. g.createServerCond.L.Lock()
  131. for {
  132. if g.createdServer >= numberOfServersToCreate {
  133. g.createServerCond.L.Unlock()
  134. close(c)
  135. return
  136. }
  137. select {
  138. case <-g.IsShutdown():
  139. g.createServerCond.L.Unlock()
  140. return
  141. default:
  142. }
  143. g.createServerCond.Wait()
  144. }
  145. }()
  146. var tc <-chan time.Time
  147. if limit > 0 {
  148. tc = time.After(limit)
  149. }
  150. select {
  151. case <-c:
  152. return true // completed normally
  153. case <-tc:
  154. return false // timed out
  155. case <-g.IsShutdown():
  156. g.createServerCond.Signal()
  157. return false
  158. }
  159. }
  160. func (g *Manager) notify(msg systemdNotifyMsg) {
  161. // Windows doesn't use systemd to notify
  162. }
  163. func KillParent() {
  164. // Windows doesn't need to "kill parent" because there is no graceful restart
  165. }