Commit abc74081 authored by Nemanja Zbiljić's avatar Nemanja Zbiljić Committed by GitHub

Support start as Windows service (#1060)

parent 9c5bf9c2
......@@ -29,10 +29,15 @@ import (
"github.com/ethersphere/bee/pkg/node"
"github.com/ethersphere/bee/pkg/resolver/multiresolver"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/kardianos/service"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
const (
serviceName = "SwarmBeeSvc"
)
func (c *command) initStartCmd() (err error) {
cmd := &cobra.Command{
......@@ -61,6 +66,19 @@ func (c *command) initStartCmd() (err error) {
return fmt.Errorf("unknown verbosity level %q", v)
}
isWindowsService, err := isWindowsService()
if err != nil {
return fmt.Errorf("failed to determine if we are running in service: %w", err)
}
if isWindowsService {
var err error
logger, err = createWindowsEventLogger(serviceName, logger)
if err != nil {
return fmt.Errorf("failed to create windows logger %w", err)
}
}
// If the resolver is specified, resolve all connection strings
// and fail on any errors.
var resolverCfgs []multiresolver.ConnectionConfig
......@@ -136,31 +154,55 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, syscall.SIGINT, syscall.SIGTERM)
// Block main goroutine until it is interrupted
sig := <-interruptChannel
logger.Debugf("received signal: %v", sig)
logger.Info("shutting down")
// Shutdown
done := make(chan struct{})
go func() {
defer close(done)
p := &program{
start: func() {
// Block main goroutine until it is interrupted
sig := <-interruptChannel
logger.Debugf("received signal: %v", sig)
logger.Info("shutting down")
},
stop: func() {
// Shutdown
done := make(chan struct{})
go func() {
defer close(done)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := b.Shutdown(ctx); err != nil {
logger.Errorf("shutdown: %v", err)
}
}()
// If shutdown function is blocking too long,
// allow process termination by receiving another signal.
select {
case sig := <-interruptChannel:
logger.Debugf("received signal: %v", sig)
case <-done:
}
},
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if isWindowsService {
s, err := service.New(p, &service.Config{
Name: serviceName,
DisplayName: "Bee",
Description: "Bee, Swarm client.",
})
if err != nil {
return err
}
if err := b.Shutdown(ctx); err != nil {
logger.Errorf("shutdown: %v", err)
if err = s.Run(); err != nil {
return err
}
}()
// If shutdown function is blocking too long,
// allow process termination by receiving another signal.
select {
case sig := <-interruptChannel:
logger.Debugf("received signal: %v", sig)
case <-done:
} else {
// start blocks until some interrupt is received
p.start()
p.stop()
}
return nil
......@@ -175,6 +217,22 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
return nil
}
type program struct {
start func()
stop func()
}
func (p *program) Start(s service.Service) error {
// Start should not block. Do the actual work async.
go p.start()
return nil
}
func (p *program) Stop(s service.Service) error {
p.stop()
return nil
}
type signerConfig struct {
signer crypto.Signer
address swarm.Address
......
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !windows
package cmd
import (
"errors"
"github.com/ethersphere/bee/pkg/logging"
)
func isWindowsService() (bool, error) {
return false, nil
}
func createWindowsEventLogger(svcName string, logger logging.Logger) (logging.Logger, error) {
return nil, errors.New("cannot create Windows event logger")
}
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package cmd
import (
"fmt"
"io"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/debug"
"golang.org/x/sys/windows/svc/eventlog"
"github.com/ethersphere/bee/pkg/logging"
"github.com/sirupsen/logrus"
)
func isWindowsService() (bool, error) {
return svc.IsWindowsService()
}
func createWindowsEventLogger(svcName string, logger logging.Logger) (logging.Logger, error) {
el, err := eventlog.Open(svcName)
if err != nil {
return nil, err
}
winlog := &windowsEventLogger{
logger: logger,
winlog: el,
}
return winlog, nil
}
type windowsEventLogger struct {
logger logging.Logger
winlog debug.Log
}
func (l *windowsEventLogger) Tracef(format string, args ...interface{}) {
// ignore
}
func (l *windowsEventLogger) Trace(args ...interface{}) {
// ignore
}
func (l *windowsEventLogger) Debugf(format string, args ...interface{}) {
// ignore
}
func (l *windowsEventLogger) Debug(args ...interface{}) {
// ignore
}
func (l *windowsEventLogger) Infof(format string, args ...interface{}) {
l.winlog.Info(1633, fmt.Sprintf(format, args...))
}
func (l *windowsEventLogger) Info(args ...interface{}) {
l.winlog.Info(1633, fmt.Sprint(args...))
}
func (l *windowsEventLogger) Warningf(format string, args ...interface{}) {
l.winlog.Warning(1633, fmt.Sprintf(format, args...))
}
func (l *windowsEventLogger) Warning(args ...interface{}) {
l.winlog.Warning(1633, fmt.Sprint(args...))
}
func (l *windowsEventLogger) Errorf(format string, args ...interface{}) {
l.winlog.Error(1633, fmt.Sprintf(format, args...))
}
func (l *windowsEventLogger) Error(args ...interface{}) {
l.winlog.Error(1633, fmt.Sprint(args...))
}
func (l *windowsEventLogger) WithField(key string, value interface{}) *logrus.Entry {
return l.logger.WithField(key, value)
}
func (l *windowsEventLogger) WithFields(fields logrus.Fields) *logrus.Entry {
return l.logger.WithFields(fields)
}
func (l *windowsEventLogger) WriterLevel(level logrus.Level) *io.PipeWriter {
return l.NewEntry().WriterLevel(level)
}
func (l *windowsEventLogger) NewEntry() *logrus.Entry {
return l.logger.NewEntry()
}
......@@ -19,6 +19,7 @@ require (
github.com/gorilla/mux v1.7.4
github.com/gorilla/websocket v1.4.2
github.com/ipfs/go-log/v2 v2.1.1 // indirect
github.com/kardianos/service v1.2.0
github.com/kr/text v0.2.0 // indirect
github.com/libp2p/go-libp2p v0.10.0
github.com/libp2p/go-libp2p-autonat v0.3.0 // indirect
......@@ -62,7 +63,7 @@ require (
golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20200707034311-ab3426394381
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211
golang.org/x/text v0.3.3 // indirect
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f // indirect
google.golang.org/protobuf v1.25.0 // indirect
......
......@@ -409,6 +409,8 @@ github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM5
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 h1:ZHuwnjpP8LsVsUYqTqeVAI+GfDfJ6UNPrExZF+vX/DQ=
github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g=
github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
......@@ -1194,8 +1196,8 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment