Skip to content

Commit

Permalink
Windows: fix service termination (elastic#18916)
Browse files Browse the repository at this point in the history
Update the Windows service handling logic so that the service doesn't
transition to the STOPPED state until the beater is terminated.

Before this patch, a Beats service would report to be STOPPED as soon
as it received the stop request. This causes some problems during service
restarts, as the new service would start while the old one was still cleaning
up.

Fixes elastic#18914

(cherry picked from commit f3ab7c7)
  • Loading branch information
adriansr committed Jun 5, 2020
1 parent 818fe20 commit fc4a2a3
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- [Autodiscover] Check if runner is already running before starting again. {pull}18564[18564]
- Fix `keystore add` hanging under Windows. {issue}18649[18649] {pull}18654[18654]
- Fix regression in `add_kubernetes_metadata`, so configured `indexers` and `matchers` are used if defaults are not disabled. {issue}18481[18481] {pull}18818[18818]
- Fixed a service restart failure under Windows. {issue}18914[18914] {pull}18916[18916]

*Auditbeat*

Expand Down
6 changes: 6 additions & 0 deletions libbeat/cmd/instance/beat.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,12 @@ func (b *Beat) launch(settings Settings, bt beat.Creator) error {
return err
}

// Windows: Mark service as stopped.
// After this is run, a Beat service is considered by the OS to be stopped
// and another instance of the process can be started.
// This must be the first deferred cleanup task (last to execute).
defer svc.NotifyTermination()

// Try to acquire exclusive lock on data path to prevent another beat instance
// sharing same data path.
bl := newLocker(b)
Expand Down
5 changes: 5 additions & 0 deletions libbeat/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ func HandleSignals(stopFunction func(), cancel context.CancelFunc) {
})
}

// NotifyTermination tells the OS that the service is stopped.
func NotifyTermination() {
notifyWindowsServiceStopped()
}

// cmdline flags
var memprofile, cpuprofile, httpprof *string
var cpuOut *os.File
Expand Down
3 changes: 3 additions & 0 deletions libbeat/service/service_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ package service
// ProcessWindowsControlEvents is not used on non-windows platforms.
func ProcessWindowsControlEvents(stopCallback func()) {
}

func notifyWindowsServiceStopped() {
}
27 changes: 24 additions & 3 deletions libbeat/service/service_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ import (
"github.com/elastic/beats/v7/libbeat/logp"
)

type beatService struct{}
type beatService struct {
stopCallback func()
done chan struct{}
}

var serviceInstance = &beatService{
stopCallback: nil,
done: make(chan struct{}, 0),
}

// Execute runs the beat service with the arguments and manages changes that
// occur in the environment or runtime that may affect the beat.
Expand All @@ -52,9 +60,22 @@ loop:
}
}
changes <- svc.Status{State: svc.StopPending}
m.stopCallback()
// Block until notifyWindowsServiceStopped below is called. This is required
// as the windows/svc package will transition the service to STOPPED state
// once this function returns.
<-m.done
return
}

func (m *beatService) stop() {
close(m.done)
}

func notifyWindowsServiceStopped() {
serviceInstance.stop()
}

// couldNotConnect is the errno for ERROR_FAILED_SERVICE_CONTROLLER_CONNECT.
const couldNotConnect syscall.Errno = 1063

Expand All @@ -76,10 +97,10 @@ func ProcessWindowsControlEvents(stopCallback func()) {
run = debug.Run
}

err = run(os.Args[0], &beatService{})
serviceInstance.stopCallback = stopCallback
err = run(os.Args[0], serviceInstance)

if err == nil {
stopCallback()
return
}

Expand Down

0 comments on commit fc4a2a3

Please sign in to comment.