svcutil.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/.
  6. package svcutil
  7. import (
  8. "context"
  9. "errors"
  10. "fmt"
  11. "time"
  12. "github.com/syncthing/syncthing/lib/logger"
  13. "github.com/syncthing/syncthing/lib/sync"
  14. "github.com/thejerf/suture/v4"
  15. )
  16. const ServiceTimeout = 10 * time.Second
  17. type FatalErr struct {
  18. Err error
  19. Status ExitStatus
  20. }
  21. // AsFatalErr wraps the given error creating a FatalErr. If the given error
  22. // already is of type FatalErr, it is not wrapped again.
  23. func AsFatalErr(err error, status ExitStatus) *FatalErr {
  24. var ferr *FatalErr
  25. if errors.As(err, &ferr) {
  26. return ferr
  27. }
  28. return &FatalErr{
  29. Err: err,
  30. Status: status,
  31. }
  32. }
  33. func (e *FatalErr) Error() string {
  34. return e.Err.Error()
  35. }
  36. func (e *FatalErr) Unwrap() error {
  37. return e.Err
  38. }
  39. func (e *FatalErr) Is(target error) bool {
  40. return target == suture.ErrTerminateSupervisorTree
  41. }
  42. // NoRestartErr wraps the given error err (which may be nil) to make sure that
  43. // `errors.Is(err, suture.ErrDoNotRestart) == true`.
  44. func NoRestartErr(err error) error {
  45. if err == nil {
  46. return suture.ErrDoNotRestart
  47. }
  48. return &noRestartErr{err}
  49. }
  50. type noRestartErr struct {
  51. err error
  52. }
  53. func (e *noRestartErr) Error() string {
  54. return e.err.Error()
  55. }
  56. func (e *noRestartErr) Unwrap() error {
  57. return e.err
  58. }
  59. func (e *noRestartErr) Is(target error) bool {
  60. return target == suture.ErrDoNotRestart
  61. }
  62. type ExitStatus int
  63. const (
  64. ExitSuccess ExitStatus = 0
  65. ExitError ExitStatus = 1
  66. ExitNoUpgradeAvailable ExitStatus = 2
  67. ExitRestart ExitStatus = 3
  68. ExitUpgrade ExitStatus = 4
  69. )
  70. func (s ExitStatus) AsInt() int {
  71. return int(s)
  72. }
  73. type ServiceWithError interface {
  74. suture.Service
  75. fmt.Stringer
  76. Error() error
  77. SetError(error)
  78. }
  79. // AsService wraps the given function to implement suture.Service. In addition
  80. // it keeps track of the returned error and allows querying and setting that error.
  81. func AsService(fn func(ctx context.Context) error, creator string) ServiceWithError {
  82. return &service{
  83. creator: creator,
  84. serve: fn,
  85. mut: sync.NewMutex(),
  86. }
  87. }
  88. type service struct {
  89. creator string
  90. serve func(ctx context.Context) error
  91. err error
  92. mut sync.Mutex
  93. }
  94. func (s *service) Serve(ctx context.Context) error {
  95. s.mut.Lock()
  96. s.err = nil
  97. s.mut.Unlock()
  98. err := s.serve(ctx)
  99. s.mut.Lock()
  100. s.err = err
  101. s.mut.Unlock()
  102. return err
  103. }
  104. func (s *service) Error() error {
  105. s.mut.Lock()
  106. defer s.mut.Unlock()
  107. return s.err
  108. }
  109. func (s *service) SetError(err error) {
  110. s.mut.Lock()
  111. s.err = err
  112. s.mut.Unlock()
  113. }
  114. func (s *service) String() string {
  115. return fmt.Sprintf("Service@%p created by %v", s, s.creator)
  116. }
  117. type doneService struct {
  118. fn func()
  119. }
  120. func (s *doneService) Serve(ctx context.Context) error {
  121. <-ctx.Done()
  122. s.fn()
  123. return nil
  124. }
  125. // OnSupervisorDone calls fn when sup is done.
  126. func OnSupervisorDone(sup *suture.Supervisor, fn func()) {
  127. sup.Add(&doneService{fn})
  128. }
  129. func SpecWithDebugLogger(l logger.Logger) suture.Spec {
  130. return spec(func(e suture.Event) { l.Debugln(e) })
  131. }
  132. func SpecWithInfoLogger(l logger.Logger) suture.Spec {
  133. return spec(func(e suture.Event) { l.Infoln(e) })
  134. }
  135. func spec(eventHook suture.EventHook) suture.Spec {
  136. return suture.Spec{
  137. EventHook: eventHook,
  138. Timeout: ServiceTimeout,
  139. PassThroughPanics: true,
  140. DontPropagateTermination: false,
  141. }
  142. }