| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- // Copyright (C) 2016 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package svcutil
- import (
- "context"
- "errors"
- "fmt"
- "time"
- "github.com/syncthing/syncthing/lib/logger"
- "github.com/syncthing/syncthing/lib/sync"
- "github.com/thejerf/suture/v4"
- )
- const ServiceTimeout = 10 * time.Second
- type FatalErr struct {
- Err error
- Status ExitStatus
- }
- // AsFatalErr wraps the given error creating a FatalErr. If the given error
- // already is of type FatalErr, it is not wrapped again.
- func AsFatalErr(err error, status ExitStatus) *FatalErr {
- var ferr *FatalErr
- if errors.As(err, &ferr) {
- return ferr
- }
- return &FatalErr{
- Err: err,
- Status: status,
- }
- }
- func (e *FatalErr) Error() string {
- return e.Err.Error()
- }
- func (e *FatalErr) Unwrap() error {
- return e.Err
- }
- func (e *FatalErr) Is(target error) bool {
- return target == suture.ErrTerminateSupervisorTree
- }
- // NoRestartErr wraps the given error err (which may be nil) to make sure that
- // `errors.Is(err, suture.ErrDoNotRestart) == true`.
- func NoRestartErr(err error) error {
- if err == nil {
- return suture.ErrDoNotRestart
- }
- return &noRestartErr{err}
- }
- type noRestartErr struct {
- err error
- }
- func (e *noRestartErr) Error() string {
- return e.err.Error()
- }
- func (e *noRestartErr) Unwrap() error {
- return e.err
- }
- func (e *noRestartErr) Is(target error) bool {
- return target == suture.ErrDoNotRestart
- }
- type ExitStatus int
- const (
- ExitSuccess ExitStatus = 0
- ExitError ExitStatus = 1
- ExitNoUpgradeAvailable ExitStatus = 2
- ExitRestart ExitStatus = 3
- ExitUpgrade ExitStatus = 4
- )
- func (s ExitStatus) AsInt() int {
- return int(s)
- }
- type ServiceWithError interface {
- suture.Service
- fmt.Stringer
- Error() error
- }
- // AsService wraps the given function to implement suture.Service. In addition
- // it keeps track of the returned error and allows querying that error.
- func AsService(fn func(ctx context.Context) error, creator string) ServiceWithError {
- return &service{
- creator: creator,
- serve: fn,
- mut: sync.NewMutex(),
- }
- }
- type service struct {
- creator string
- serve func(ctx context.Context) error
- err error
- mut sync.Mutex
- }
- func (s *service) Serve(ctx context.Context) error {
- s.mut.Lock()
- s.err = nil
- s.mut.Unlock()
- err := s.serve(ctx)
- s.mut.Lock()
- s.err = err
- s.mut.Unlock()
- return err
- }
- func (s *service) Error() error {
- s.mut.Lock()
- defer s.mut.Unlock()
- return s.err
- }
- func (s *service) String() string {
- return fmt.Sprintf("Service@%p created by %v", s, s.creator)
- }
- type doneService func()
- func (fn doneService) Serve(ctx context.Context) error {
- <-ctx.Done()
- fn()
- return nil
- }
- // OnSupervisorDone calls fn when sup is done.
- func OnSupervisorDone(sup *suture.Supervisor, fn func()) {
- sup.Add(doneService(fn))
- }
- func SpecWithDebugLogger(l logger.Logger) suture.Spec {
- return spec(func(e suture.Event) { l.Debugln(e) })
- }
- func SpecWithInfoLogger(l logger.Logger) suture.Spec {
- return spec(func(e suture.Event) { l.Infoln(e) })
- }
- func spec(eventHook suture.EventHook) suture.Spec {
- return suture.Spec{
- EventHook: eventHook,
- Timeout: ServiceTimeout,
- PassThroughPanics: true,
- DontPropagateTermination: false,
- }
- }
|