From 20b8448eb9dc6b9012af3a2615a09e2cf1ba33a0 Mon Sep 17 00:00:00 2001 From: Charlie Voiselle <464492+angrycub@users.noreply.github.com> Date: Fri, 2 Aug 2019 08:54:42 -0400 Subject: [PATCH 1/9] Added service wrapper code --- command/agent/command.go | 3 +++ service_os/service.go | 7 ++++++ service_os/service_windows.go | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 service_os/service.go create mode 100644 service_os/service_windows.go diff --git a/command/agent/command.go b/command/agent/command.go index da3e40e909c0..3af6452d50dc 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -30,6 +30,7 @@ import ( gatedwriter "github.com/hashicorp/nomad/helper/gated-writer" "github.com/hashicorp/nomad/helper/logging" "github.com/hashicorp/nomad/nomad/structs/config" + "github.com/hashicorp/nomad/service_os" "github.com/hashicorp/nomad/version" "github.com/mitchellh/cli" "github.com/posener/complete" @@ -777,6 +778,8 @@ WAIT: select { case s := <-signalCh: sig = s + case <-service_os.Shutdown_Channel(): + sig = os.Interrupt case <-c.ShutdownCh: sig = os.Interrupt case <-c.retryJoinErrCh: diff --git a/service_os/service.go b/service_os/service.go new file mode 100644 index 000000000000..432baaf2ad8b --- /dev/null +++ b/service_os/service.go @@ -0,0 +1,7 @@ +package service_os + +var chanGraceExit = make(chan int) + +func Shutdown_Channel() <-chan int { + return chanGraceExit +} diff --git a/service_os/service_windows.go b/service_os/service_windows.go new file mode 100644 index 000000000000..d7fc245f52bc --- /dev/null +++ b/service_os/service_windows.go @@ -0,0 +1,42 @@ +//+build windows + +package service_os + +import ( + wsvc "golang.org/x/sys/windows/svc" +) + +type serviceWindows struct{} + +func init() { + interactive, err := wsvc.IsAnInteractiveSession() + if err != nil { + panic(err) + } + if interactive { + return + } + go func() { + _ = wsvc.Run("", serviceWindows{}) + }() +} + +func (serviceWindows) Execute(args []string, r <-chan wsvc.ChangeRequest, s chan<- wsvc.Status) (svcSpecificEC bool, exitCode uint32) { + const accCommands = wsvc.AcceptStop | wsvc.AcceptShutdown + s <- wsvc.Status{State: wsvc.StartPending} + + s <- wsvc.Status{State: wsvc.Running, Accepts: accCommands} + for { + c := <-r + switch c.Cmd { + case wsvc.Interrogate: + s <- c.CurrentStatus + case wsvc.Stop, wsvc.Shutdown: + chanGraceExit <- 1 + s <- wsvc.Status{State: wsvc.StopPending} + return false, 0 + } + } + + return false, 0 +} From c1af8f6f4d0dd257d792911d6147209b983b2347 Mon Sep 17 00:00:00 2001 From: Charlie Voiselle <464492+angrycub@users.noreply.github.com> Date: Wed, 23 Oct 2019 16:59:16 -0400 Subject: [PATCH 2/9] Vendored golang.org/x/sys/windows/svc --- vendor/golang.org/x/sys/windows/svc/event.go | 48 +++ vendor/golang.org/x/sys/windows/svc/go12.c | 24 ++ vendor/golang.org/x/sys/windows/svc/go12.go | 11 + vendor/golang.org/x/sys/windows/svc/go13.go | 31 ++ .../golang.org/x/sys/windows/svc/security.go | 62 +++ .../golang.org/x/sys/windows/svc/service.go | 364 ++++++++++++++++++ vendor/golang.org/x/sys/windows/svc/sys_386.s | 69 ++++ .../golang.org/x/sys/windows/svc/sys_amd64.s | 44 +++ vendor/golang.org/x/sys/windows/svc/sys_arm.s | 38 ++ vendor/vendor.json | 1 + 10 files changed, 692 insertions(+) create mode 100644 vendor/golang.org/x/sys/windows/svc/event.go create mode 100644 vendor/golang.org/x/sys/windows/svc/go12.c create mode 100644 vendor/golang.org/x/sys/windows/svc/go12.go create mode 100644 vendor/golang.org/x/sys/windows/svc/go13.go create mode 100644 vendor/golang.org/x/sys/windows/svc/security.go create mode 100644 vendor/golang.org/x/sys/windows/svc/service.go create mode 100644 vendor/golang.org/x/sys/windows/svc/sys_386.s create mode 100644 vendor/golang.org/x/sys/windows/svc/sys_amd64.s create mode 100644 vendor/golang.org/x/sys/windows/svc/sys_arm.s diff --git a/vendor/golang.org/x/sys/windows/svc/event.go b/vendor/golang.org/x/sys/windows/svc/event.go new file mode 100644 index 000000000000..0508e2288184 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/event.go @@ -0,0 +1,48 @@ +// Copyright 2012 The Go 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 svc + +import ( + "errors" + + "golang.org/x/sys/windows" +) + +// event represents auto-reset, initially non-signaled Windows event. +// It is used to communicate between go and asm parts of this package. +type event struct { + h windows.Handle +} + +func newEvent() (*event, error) { + h, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return nil, err + } + return &event{h: h}, nil +} + +func (e *event) Close() error { + return windows.CloseHandle(e.h) +} + +func (e *event) Set() error { + return windows.SetEvent(e.h) +} + +func (e *event) Wait() error { + s, err := windows.WaitForSingleObject(e.h, windows.INFINITE) + switch s { + case windows.WAIT_OBJECT_0: + break + case windows.WAIT_FAILED: + return err + default: + return errors.New("unexpected result from WaitForSingleObject") + } + return nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/go12.c b/vendor/golang.org/x/sys/windows/svc/go12.c new file mode 100644 index 000000000000..6f1be1fa3bcb --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go12.c @@ -0,0 +1,24 @@ +// Copyright 2012 The Go 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 +// +build !go1.3 + +// copied from pkg/runtime +typedef unsigned int uint32; +typedef unsigned long long int uint64; +#ifdef _64BIT +typedef uint64 uintptr; +#else +typedef uint32 uintptr; +#endif + +// from sys_386.s or sys_amd64.s +void ·servicemain(void); + +void +·getServiceMain(uintptr *r) +{ + *r = (uintptr)·servicemain; +} diff --git a/vendor/golang.org/x/sys/windows/svc/go12.go b/vendor/golang.org/x/sys/windows/svc/go12.go new file mode 100644 index 000000000000..cd8b913c99d4 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go12.go @@ -0,0 +1,11 @@ +// Copyright 2014 The Go 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 +// +build !go1.3 + +package svc + +// from go12.c +func getServiceMain(r *uintptr) diff --git a/vendor/golang.org/x/sys/windows/svc/go13.go b/vendor/golang.org/x/sys/windows/svc/go13.go new file mode 100644 index 000000000000..9d7f3cec54cd --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go13.go @@ -0,0 +1,31 @@ +// Copyright 2014 The Go 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 +// +build go1.3 + +package svc + +import "unsafe" + +const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const + +// Should be a built-in for unsafe.Pointer? +func add(p unsafe.Pointer, x uintptr) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// funcPC returns the entry PC of the function f. +// It assumes that f is a func value. Otherwise the behavior is undefined. +func funcPC(f interface{}) uintptr { + return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize)) +} + +// from sys_386.s and sys_amd64.s +func servicectlhandler(ctl uint32) uintptr +func servicemain(argc uint32, argv **uint16) + +func getServiceMain(r *uintptr) { + *r = funcPC(servicemain) +} diff --git a/vendor/golang.org/x/sys/windows/svc/security.go b/vendor/golang.org/x/sys/windows/svc/security.go new file mode 100644 index 000000000000..6fbc9236ed53 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/security.go @@ -0,0 +1,62 @@ +// Copyright 2012 The Go 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 svc + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +func allocSid(subAuth0 uint32) (*windows.SID, error) { + var sid *windows.SID + err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, + 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid) + if err != nil { + return nil, err + } + return sid, nil +} + +// IsAnInteractiveSession determines if calling process is running interactively. +// It queries the process token for membership in the Interactive group. +// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s +func IsAnInteractiveSession() (bool, error) { + interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID) + if err != nil { + return false, err + } + defer windows.FreeSid(interSid) + + serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID) + if err != nil { + return false, err + } + defer windows.FreeSid(serviceSid) + + t, err := windows.OpenCurrentProcessToken() + if err != nil { + return false, err + } + defer t.Close() + + gs, err := t.GetTokenGroups() + if err != nil { + return false, err + } + p := unsafe.Pointer(&gs.Groups[0]) + groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount] + for _, g := range groups { + if windows.EqualSid(g.Sid, interSid) { + return true, nil + } + if windows.EqualSid(g.Sid, serviceSid) { + return false, nil + } + } + return false, nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/service.go b/vendor/golang.org/x/sys/windows/svc/service.go new file mode 100644 index 000000000000..ee3d6965a180 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/service.go @@ -0,0 +1,364 @@ +// Copyright 2012 The Go 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 svc provides everything required to build Windows service. +// +package svc + +import ( + "errors" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +// State describes service execution state (Stopped, Running and so on). +type State uint32 + +const ( + Stopped = State(windows.SERVICE_STOPPED) + StartPending = State(windows.SERVICE_START_PENDING) + StopPending = State(windows.SERVICE_STOP_PENDING) + Running = State(windows.SERVICE_RUNNING) + ContinuePending = State(windows.SERVICE_CONTINUE_PENDING) + PausePending = State(windows.SERVICE_PAUSE_PENDING) + Paused = State(windows.SERVICE_PAUSED) +) + +// Cmd represents service state change request. It is sent to a service +// by the service manager, and should be actioned upon by the service. +type Cmd uint32 + +const ( + Stop = Cmd(windows.SERVICE_CONTROL_STOP) + Pause = Cmd(windows.SERVICE_CONTROL_PAUSE) + Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE) + Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE) + Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN) + ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE) + NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD) + NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE) + NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE) + NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE) + DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT) + HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE) + PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT) + SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE) +) + +// Accepted is used to describe commands accepted by the service. +// Note that Interrogate is always accepted. +type Accepted uint32 + +const ( + AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP) + AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN) + AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE) + AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE) + AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE) + AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE) + AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT) + AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE) +) + +// Status combines State and Accepted commands to fully describe running service. +type Status struct { + State State + Accepts Accepted + CheckPoint uint32 // used to report progress during a lengthy operation + WaitHint uint32 // estimated time required for a pending operation, in milliseconds + ProcessId uint32 // if the service is running, the process identifier of it, and otherwise zero +} + +// ChangeRequest is sent to the service Handler to request service status change. +type ChangeRequest struct { + Cmd Cmd + EventType uint32 + EventData uintptr + CurrentStatus Status + Context uintptr +} + +// Handler is the interface that must be implemented to build Windows service. +type Handler interface { + + // Execute will be called by the package code at the start of + // the service, and the service will exit once Execute completes. + // Inside Execute you must read service change requests from r and + // act accordingly. You must keep service control manager up to date + // about state of your service by writing into s as required. + // args contains service name followed by argument strings passed + // to the service. + // You can provide service exit code in exitCode return parameter, + // with 0 being "no error". You can also indicate if exit code, + // if any, is service specific or not by using svcSpecificEC + // parameter. + Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32) +} + +var ( + // These are used by asm code. + goWaitsH uintptr + cWaitsH uintptr + ssHandle uintptr + sName *uint16 + sArgc uintptr + sArgv **uint16 + ctlHandlerExProc uintptr + cSetEvent uintptr + cWaitForSingleObject uintptr + cRegisterServiceCtrlHandlerExW uintptr +) + +func init() { + k := windows.NewLazySystemDLL("kernel32.dll") + cSetEvent = k.NewProc("SetEvent").Addr() + cWaitForSingleObject = k.NewProc("WaitForSingleObject").Addr() + a := windows.NewLazySystemDLL("advapi32.dll") + cRegisterServiceCtrlHandlerExW = a.NewProc("RegisterServiceCtrlHandlerExW").Addr() +} + +type ctlEvent struct { + cmd Cmd + eventType uint32 + eventData uintptr + context uintptr + errno uint32 +} + +// service provides access to windows service api. +type service struct { + name string + h windows.Handle + cWaits *event + goWaits *event + c chan ctlEvent + handler Handler +} + +func newService(name string, handler Handler) (*service, error) { + var s service + var err error + s.name = name + s.c = make(chan ctlEvent) + s.handler = handler + s.cWaits, err = newEvent() + if err != nil { + return nil, err + } + s.goWaits, err = newEvent() + if err != nil { + s.cWaits.Close() + return nil, err + } + return &s, nil +} + +func (s *service) close() error { + s.cWaits.Close() + s.goWaits.Close() + return nil +} + +type exitCode struct { + isSvcSpecific bool + errno uint32 +} + +func (s *service) updateStatus(status *Status, ec *exitCode) error { + if s.h == 0 { + return errors.New("updateStatus with no service status handle") + } + var t windows.SERVICE_STATUS + t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS + t.CurrentState = uint32(status.State) + if status.Accepts&AcceptStop != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP + } + if status.Accepts&AcceptShutdown != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN + } + if status.Accepts&AcceptPauseAndContinue != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE + } + if status.Accepts&AcceptParamChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE + } + if status.Accepts&AcceptNetBindChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE + } + if status.Accepts&AcceptHardwareProfileChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE + } + if status.Accepts&AcceptPowerEvent != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT + } + if status.Accepts&AcceptSessionChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE + } + if ec.errno == 0 { + t.Win32ExitCode = windows.NO_ERROR + t.ServiceSpecificExitCode = windows.NO_ERROR + } else if ec.isSvcSpecific { + t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR) + t.ServiceSpecificExitCode = ec.errno + } else { + t.Win32ExitCode = ec.errno + t.ServiceSpecificExitCode = windows.NO_ERROR + } + t.CheckPoint = status.CheckPoint + t.WaitHint = status.WaitHint + return windows.SetServiceStatus(s.h, &t) +} + +const ( + sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota + sysErrNewThreadInCallback +) + +func (s *service) run() { + s.goWaits.Wait() + s.h = windows.Handle(ssHandle) + argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc] + args := make([]string, len(argv)) + for i, a := range argv { + args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:]) + } + + cmdsToHandler := make(chan ChangeRequest) + changesFromHandler := make(chan Status) + exitFromHandler := make(chan exitCode) + + go func() { + ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler) + exitFromHandler <- exitCode{ss, errno} + }() + + ec := exitCode{isSvcSpecific: true, errno: 0} + outcr := ChangeRequest{ + CurrentStatus: Status{State: Stopped}, + } + var outch chan ChangeRequest + inch := s.c +loop: + for { + select { + case r := <-inch: + if r.errno != 0 { + ec.errno = r.errno + break loop + } + inch = nil + outch = cmdsToHandler + outcr.Cmd = r.cmd + outcr.EventType = r.eventType + outcr.EventData = r.eventData + outcr.Context = r.context + case outch <- outcr: + inch = s.c + outch = nil + case c := <-changesFromHandler: + err := s.updateStatus(&c, &ec) + if err != nil { + // best suitable error number + ec.errno = sysErrSetServiceStatusFailed + if err2, ok := err.(syscall.Errno); ok { + ec.errno = uint32(err2) + } + break loop + } + outcr.CurrentStatus = c + case ec = <-exitFromHandler: + break loop + } + } + + s.updateStatus(&Status{State: Stopped}, &ec) + s.cWaits.Set() +} + +func newCallback(fn interface{}) (cb uintptr, err error) { + defer func() { + r := recover() + if r == nil { + return + } + cb = 0 + switch v := r.(type) { + case string: + err = errors.New(v) + case error: + err = v + default: + err = errors.New("unexpected panic in syscall.NewCallback") + } + }() + return syscall.NewCallback(fn), nil +} + +// BUG(brainman): There is no mechanism to run multiple services +// inside one single executable. Perhaps, it can be overcome by +// using RegisterServiceCtrlHandlerEx Windows api. + +// Run executes service name by calling appropriate handler function. +func Run(name string, handler Handler) error { + runtime.LockOSThread() + + tid := windows.GetCurrentThreadId() + + s, err := newService(name, handler) + if err != nil { + return err + } + + ctlHandler := func(ctl, evtype, evdata, context uintptr) uintptr { + e := ctlEvent{cmd: Cmd(ctl), eventType: uint32(evtype), eventData: evdata, context: context} + // We assume that this callback function is running on + // the same thread as Run. Nowhere in MS documentation + // I could find statement to guarantee that. So putting + // check here to verify, otherwise things will go bad + // quickly, if ignored. + i := windows.GetCurrentThreadId() + if i != tid { + e.errno = sysErrNewThreadInCallback + } + s.c <- e + // Always return NO_ERROR (0) for now. + return windows.NO_ERROR + } + + var svcmain uintptr + getServiceMain(&svcmain) + t := []windows.SERVICE_TABLE_ENTRY{ + {ServiceName: syscall.StringToUTF16Ptr(s.name), ServiceProc: svcmain}, + {ServiceName: nil, ServiceProc: 0}, + } + + goWaitsH = uintptr(s.goWaits.h) + cWaitsH = uintptr(s.cWaits.h) + sName = t[0].ServiceName + ctlHandlerExProc, err = newCallback(ctlHandler) + if err != nil { + return err + } + + go s.run() + + err = windows.StartServiceCtrlDispatcher(&t[0]) + if err != nil { + return err + } + return nil +} + +// StatusHandle returns service status handle. It is safe to call this function +// from inside the Handler.Execute because then it is guaranteed to be set. +// This code will have to change once multiple services are possible per process. +func StatusHandle() windows.Handle { + return windows.Handle(ssHandle) +} diff --git a/vendor/golang.org/x/sys/windows/svc/sys_386.s b/vendor/golang.org/x/sys/windows/svc/sys_386.s new file mode 100644 index 000000000000..c8a583d7375d --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_386.s @@ -0,0 +1,69 @@ +// Copyright 2012 The Go 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 + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),7,$0 + MOVL argc+0(FP), AX + MOVL AX, ·sArgc(SB) + MOVL argv+4(FP), AX + MOVL AX, ·sArgv(SB) + + PUSHL BP + PUSHL BX + PUSHL SI + PUSHL DI + + SUBL $12, SP + + MOVL ·sName(SB), AX + MOVL AX, (SP) + MOVL $·servicectlhandler(SB), AX + MOVL AX, 4(SP) + // Set context to 123456 to test issue #25660. + MOVL $123456, 8(SP) + MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + CMPL AX, $0 + JE exit + MOVL AX, ·ssHandle(SB) + + MOVL ·goWaitsH(SB), AX + MOVL AX, (SP) + MOVL ·cSetEvent(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + + MOVL ·cWaitsH(SB), AX + MOVL AX, (SP) + MOVL $-1, AX + MOVL AX, 4(SP) + MOVL ·cWaitForSingleObject(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + +exit: + ADDL $12, SP + + POPL DI + POPL SI + POPL BX + POPL BP + + MOVL 0(SP), CX + ADDL $12, SP + JMP CX + +// I do not know why, but this seems to be the only way to call +// ctlHandlerProc on Windows 7. + +// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { +TEXT ·servicectlhandler(SB),7,$0 + MOVL ·ctlHandlerExProc(SB), CX + JMP CX diff --git a/vendor/golang.org/x/sys/windows/svc/sys_amd64.s b/vendor/golang.org/x/sys/windows/svc/sys_amd64.s new file mode 100644 index 000000000000..2f7609c5b646 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_amd64.s @@ -0,0 +1,44 @@ +// Copyright 2012 The Go 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 + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),7,$0 + MOVL CX, ·sArgc(SB) + MOVQ DX, ·sArgv(SB) + + SUBQ $32, SP // stack for the first 4 syscall params + + MOVQ ·sName(SB), CX + MOVQ $·servicectlhandler(SB), DX + // BUG(pastarmovj): Figure out a way to pass in context in R8. + // Set context to 123456 to test issue #25660. + MOVQ $123456, R8 + MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX + CALL AX + CMPQ AX, $0 + JE exit + MOVQ AX, ·ssHandle(SB) + + MOVQ ·goWaitsH(SB), CX + MOVQ ·cSetEvent(SB), AX + CALL AX + + MOVQ ·cWaitsH(SB), CX + MOVQ $4294967295, DX + MOVQ ·cWaitForSingleObject(SB), AX + CALL AX + +exit: + ADDQ $32, SP + RET + +// I do not know why, but this seems to be the only way to call +// ctlHandlerProc on Windows 7. + +// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { +TEXT ·servicectlhandler(SB),7,$0 + MOVQ ·ctlHandlerExProc(SB), AX + JMP AX diff --git a/vendor/golang.org/x/sys/windows/svc/sys_arm.s b/vendor/golang.org/x/sys/windows/svc/sys_arm.s new file mode 100644 index 000000000000..33c692a8dea4 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_arm.s @@ -0,0 +1,38 @@ +// Copyright 2018 The Go 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 + +#include "textflag.h" + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),NOSPLIT|NOFRAME,$0 + MOVM.DB.W [R4, R14], (R13) // push {r4, lr} + MOVW R13, R4 + BIC $0x7, R13 // alignment for ABI + + MOVW R0, ·sArgc(SB) + MOVW R1, ·sArgv(SB) + + MOVW ·sName(SB), R0 + MOVW ·ctlHandlerExProc(SB), R1 + MOVW $0, R2 + MOVW ·cRegisterServiceCtrlHandlerExW(SB), R3 + BL (R3) + CMP $0, R0 + BEQ exit + MOVW R0, ·ssHandle(SB) + + MOVW ·goWaitsH(SB), R0 + MOVW ·cSetEvent(SB), R1 + BL (R1) + + MOVW ·cWaitsH(SB), R0 + MOVW $-1, R1 + MOVW ·cWaitForSingleObject(SB), R2 + BL (R2) + +exit: + MOVW R4, R13 // free extra stack space + MOVM.IA.W (R13), [R4, R15] // pop {r4, pc} diff --git a/vendor/vendor.json b/vendor/vendor.json index 4f46cd6566eb..9ac26780c807 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -445,6 +445,7 @@ {"path":"golang.org/x/sys/cpu","checksumSHA1":"REkmyB368pIiip76LiqMLspgCRk=","revision":"1b2967e3c290b7c545b3db0deeda16e9be4f98a2","revisionTime":"2018-07-06T09:56:39Z"}, {"path":"golang.org/x/sys/unix","checksumSHA1":"su2QDjUzrUO0JnOH9m0cNg0QqsM=","revision":"ac767d655b305d4e9612f5f6e33120b9176c4ad4","revisionTime":"2018-07-08T03:57:06Z"}, {"path":"golang.org/x/sys/windows","checksumSHA1":"l1jIhK9Y33obLipDvmjVPCdYtJI=","revision":"1b2967e3c290b7c545b3db0deeda16e9be4f98a2","revisionTime":"2018-07-06T09:56:39Z"}, + {"path":"golang.org/x/sys/windows/svc","checksumSHA1":"aeOJe5xLqyC3gN07YRyEC4pI2oo=","revision":"f89234f9a2c237c4d4cf7b32e3d9fe2f7c4eacd7","revisionTime":"2019-10-23T11:32:03Z"}, {"path":"golang.org/x/text/encoding","checksumSHA1":"Mr4ur60bgQJnQFfJY0dGtwWwMPE=","revision":"e113a52b01bdd1744681b6ce70c2e3d26b58d389","revisionTime":"2017-08-30T18:54:29Z"}, {"path":"golang.org/x/text/encoding/charmap","checksumSHA1":"HgcUFTOQF5jOYtTIj5obR3GVN9A=","revision":"e113a52b01bdd1744681b6ce70c2e3d26b58d389","revisionTime":"2017-08-30T18:54:29Z"}, {"path":"golang.org/x/text/encoding/htmlindex","checksumSHA1":"yBhX1V6U7stq3GqS2x5yzF0lV+I=","revision":"e113a52b01bdd1744681b6ce70c2e3d26b58d389","revisionTime":"2017-08-30T18:54:29Z"}, From 42c9b1daab9d8d9ea44d4e96a6ebfb995e1becdb Mon Sep 17 00:00:00 2001 From: Charlie Voiselle <464492+angrycub@users.noreply.github.com> Date: Tue, 29 Oct 2019 15:49:15 -0400 Subject: [PATCH 3/9] Added Docs * guide for installing as a windows service. * configuration for logging to file from PR #6429 --- .../source/docs/configuration/index.html.md | 47 +++++++++--- .../guides/install/windows-service.html.md | 75 +++++++++++++++++++ website/source/layouts/guides.erb | 4 + 3 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 website/source/guides/install/windows-service.html.md diff --git a/website/source/docs/configuration/index.html.md b/website/source/docs/configuration/index.html.md index 6952af1d9e1f..ecc47fd154cd 100644 --- a/website/source/docs/configuration/index.html.md +++ b/website/source/docs/configuration/index.html.md @@ -79,7 +79,7 @@ testing. ## General Parameters -- `acl` ([ACL][acl]: nil) - Specifies configuration which is specific to ACLs. +- `acl` `([ACL][acl]: nil)` - Specifies configuration which is specific to ACLs. - `addresses` `(Addresses: see below)` - Specifies the bind address for individual network services. Any values configured in this stanza take @@ -130,9 +130,10 @@ testing. Dev mode (`-dev`) defaults to localhost. The value supports [go-sockaddr/template format][go-sockaddr/template]. -- `client` ([Client][client]: nil) - Specifies configuration which is specific to the Nomad client. +- `client` `([Client][client]: nil)` - Specifies configuration which is specific + to the Nomad client. -- `consul` ([Consul][consul]: nil) - Specifies configuration for +- `consul` `([Consul][consul]: nil)` - Specifies configuration for connecting to Consul. - `datacenter` `(string: "dc1")` - Specifies the data center of the local agent. @@ -143,13 +144,16 @@ testing. allocation data as well as cluster information. Server nodes use this directory to store cluster state, including the replicated log and snapshot data. This must be specified as an absolute path. - - ~> **WARNING**: This directory **must not** be set to a directory that is [included in the chroot](/docs/drivers/exec.html#chroot) if you use the [`exec`](/docs/drivers/exec.html) driver. + + ~> **WARNING**: This directory **must not** be set to a directory that is + [included in the chroot](/docs/drivers/exec.html#chroot) if you use the + [`exec`](/docs/drivers/exec.html) driver. - `disable_anonymous_signature` `(bool: false)` - Specifies if Nomad should provide an anonymous signature for de-duplication with the update check. -- `disable_update_check` `(bool: false)` - Specifies if Nomad should not check for updates and security bulletins. +- `disable_update_check` `(bool: false)` - Specifies if Nomad should not check + for updates and security bulletins. - `enable_debug` `(bool: false)` - Specifies if the debugging HTTP endpoints should be enabled. These endpoints can be used with profiling tools to dump @@ -179,6 +183,22 @@ testing. - `log_json` `(bool: false)` - Output logs in a JSON format. +- `log_file` `(string: "")` - Specifies the path for logging. If the path + does not includes a filename, the filename defaults to "nomad-{timestamp}.log". + This setting can be combined with `log_rotate_bytes` and `log_rotate_duration` + for a fine-grained log rotation control. + +- `log_rotate_bytes` `(int: 0)` - Specifies the number of bytes that should be + written to a log before it needs to be rotated. Unless specified, there is no + limit to the number of bytes that can be written to a log file. + +- `log_rotate_duration` `(duration: "24h")` - Specifies the maximum duration a + log should be written to before it needs to be rotated. Must be a duration + value such as 30s. + +- `log_rotate_max_files` `(int: 0)` - Specifies the maximum number of older log + file archives to keep. If 0 no files are ever deleted. + - `name` `(string: [hostname])` - Specifies the name of the local node. This value is used to identify individual agents. When specified on a server, the name must be unique within the region. @@ -188,7 +208,7 @@ testing. [data_dir](#data_dir) suffixed with "plugins", like `"/opt/nomad/plugins"`. This must be an absolute path. -- `plugin` ([Plugin][plugin]: nil) - Specifies configuration for a +- `plugin` `([Plugin][plugin]: nil)` - Specifies configuration for a specific plugin. The plugin stanza may be repeated, once for each plugin being configured. The key of the stanza is the plugin's executable name relative to the [plugin_dir](#plugin_dir). @@ -221,15 +241,18 @@ testing. with potentially multiple zones, which map to [datacenters](#datacenter) such as `us-west` and `us-east`. -- `sentinel` ([Sentinel][sentinel]: nil) - Specifies configuration for Sentinel policies. +- `sentinel` `([Sentinel][sentinel]: nil)` - Specifies configuration for Sentinel + policies. -- `server` ([Server][server]: nil) - Specifies configuration which is specific to the Nomad server. +- `server` `([Server][server]: nil)` - Specifies configuration which is specific + to the Nomad server. -- `syslog_facility` `(string: "LOCAL0")` - Specifies the syslog facility to write to. This has no effect unless `enable_syslog` is true. +- `syslog_facility` `(string: "LOCAL0")` - Specifies the syslog facility to + write to. This has no effect unless `enable_syslog` is true. -- `tls` ([TLS][tls]: nil) - Specifies configuration for TLS. +- `tls` `([TLS][tls]: nil)` - Specifies configuration for TLS. -- `vault` ([Vault][vault]: nil) - Specifies configuration for +- `vault` `([Vault][vault]: nil)` - Specifies configuration for connecting to Vault. ## Examples diff --git a/website/source/guides/install/windows-service.html.md b/website/source/guides/install/windows-service.html.md new file mode 100644 index 000000000000..f59b60a365a1 --- /dev/null +++ b/website/source/guides/install/windows-service.html.md @@ -0,0 +1,75 @@ +--- +layout: "docs" +page_title: "Nomad as a Windows Service" +sidebar_current: "guides-install-windows-service" +description: |- + Discusses how to register and run Nomad as a native Windows service. +--- + +# Installing Nomad as a Windows service + +Nomad can be run as a native Windows service. In order to do this, you will need +to register the Nomad application with the Windows Service Control Manager using +[`sc.exe`], configure Nomad to log to a file, and then start the Nomad service. + +~> **Note:** These steps should be run in a PowerShell session with Administrator + capabilities. + +## Register Nomad with Windows + +Download the Nomad binary for your architecture. + +Use the [`sc.exe`] command to create a Service named "Nomad". The binPath +argument should include the fully qualified path to the Nomad executable and any +arguments to the nomad command: agent, -config, etc. + +```plaintext +sc.exe create "Nomad" binPath="«full path to nomad.exe» agent -config=«path to config file or directory»" start= auto +[SC] CreateService SUCCESS +``` + +If you receive a success message, your service is registered with the service +manager. + +If you get an error, please verify the path to the binary and check the +arguments, by running the contents of `binPath` directly in a PowerShell session +and observing the results. + +## Configure Nomad to log to file + +Because Windows services run non-interactively and Nomad does not log to the +Windows Event Viewer, you will need to configure file-based logging in Nomad. + +To do this, set the [`log_file`][logging] argument in your Nomad configuration +file or in the binPath argument of the [`sc.exe`] command used to register the +service. + +## Start the Nomad service + +You have two ways to start the service. + +- Go to the Windows Service Manager, and look for **Nomad** in the service name + column. Click the _Start_ button to start the service. + +- Using the [`sc.exe`] command: + + ```plaintext + sc.exe start "Nomad" + + SERVICE_NAME: Nomad + TYPE : 10 WIN32_OWN_PROCESS + STATE : 4 RUNNING + (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN) + WIN32_EXIT_CODE : 0 (0x0) + SERVICE_EXIT_CODE : 0 (0x0) + CHECKPOINT : 0x0 + WAIT_HINT : 0x0 + PID : 8008 + FLAGS : + ``` + +The service automatically starts up during/after boot, so you don't need to +launch Nomad from the command-line again. + +[`sc.exe`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682107(v=vs.85).aspx +[logging]: /docs/configuration/index.html#log_file diff --git a/website/source/layouts/guides.erb b/website/source/layouts/guides.erb index ed5985a4dfea..6233a61f43a4 100644 --- a/website/source/layouts/guides.erb +++ b/website/source/layouts/guides.erb @@ -32,6 +32,10 @@ + + > + As a Windows Service + From 698d5386adda4c7ed3771fd40eff97ab478e5ca7 Mon Sep 17 00:00:00 2001 From: Charlie Voiselle <464492+angrycub@users.noreply.github.com> Date: Tue, 5 Nov 2019 11:18:08 -0500 Subject: [PATCH 4/9] Incorporate review feedback from @schmichael * fix layout on guide for sidebar * link downloads * correct md formatting for links inside of monospace * shorten title --- .../source/docs/configuration/index.html.md | 34 +++++++++---------- .../guides/install/windows-service.html.md | 5 +-- website/source/layouts/guides.erb | 2 +- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/website/source/docs/configuration/index.html.md b/website/source/docs/configuration/index.html.md index ecc47fd154cd..51dca38bbbf5 100644 --- a/website/source/docs/configuration/index.html.md +++ b/website/source/docs/configuration/index.html.md @@ -79,7 +79,7 @@ testing. ## General Parameters -- `acl` `([ACL][acl]: nil)` - Specifies configuration which is specific to ACLs. +- `acl` `(`[`ACL`]`: nil)` - Specifies configuration which is specific to ACLs. - `addresses` `(Addresses: see below)` - Specifies the bind address for individual network services. Any values configured in this stanza take @@ -130,10 +130,10 @@ testing. Dev mode (`-dev`) defaults to localhost. The value supports [go-sockaddr/template format][go-sockaddr/template]. -- `client` `([Client][client]: nil)` - Specifies configuration which is specific +- `client` `(`[`Client`]`: nil)` - Specifies configuration which is specific to the Nomad client. -- `consul` `([Consul][consul]: nil)` - Specifies configuration for +- `consul` `(`[`Consul`]`: nil)` - Specifies configuration for connecting to Consul. - `datacenter` `(string: "dc1")` - Specifies the data center of the local agent. @@ -208,7 +208,7 @@ testing. [data_dir](#data_dir) suffixed with "plugins", like `"/opt/nomad/plugins"`. This must be an absolute path. -- `plugin` `([Plugin][plugin]: nil)` - Specifies configuration for a +- `plugin` `(`[`Plugin`]`: nil)` - Specifies configuration for a specific plugin. The plugin stanza may be repeated, once for each plugin being configured. The key of the stanza is the plugin's executable name relative to the [plugin_dir](#plugin_dir). @@ -241,18 +241,18 @@ testing. with potentially multiple zones, which map to [datacenters](#datacenter) such as `us-west` and `us-east`. -- `sentinel` `([Sentinel][sentinel]: nil)` - Specifies configuration for Sentinel +- `sentinel` `(`[`Sentinel`]`: nil)` - Specifies configuration for Sentinel policies. -- `server` `([Server][server]: nil)` - Specifies configuration which is specific +- `server` `(`[`Server`]`: nil)` - Specifies configuration which is specific to the Nomad server. - `syslog_facility` `(string: "LOCAL0")` - Specifies the syslog facility to write to. This has no effect unless `enable_syslog` is true. -- `tls` `([TLS][tls]: nil)` - Specifies configuration for TLS. +- `tls` `(`[`TLS`]`: nil)` - Specifies configuration for TLS. -- `vault` `([Vault][vault]: nil)` - Specifies configuration for +- `vault` `(`[`Vault`]`: nil)` - Specifies configuration for connecting to Vault. ## Examples @@ -277,13 +277,13 @@ http_api_response_headers { } ``` -[hcl]: https://github.com/hashicorp/hcl "HashiCorp Configuration Language" +[`ACL`]: /docs/configuration/acl.html "Nomad Agent ACL Configuration" +[`Client`]: /docs/configuration/client.html "Nomad Agent client Configuration" +[`Consul`]: /docs/configuration/consul.html "Nomad Agent consul Configuration" +[`Plugin`]: /docs/configuration/plugin.html "Nomad Agent Plugin Configuration" +[`Sentinel`]: /docs/configuration/sentinel.html "Nomad Agent sentinel Configuration" +[`Server`]: /docs/configuration/server.html "Nomad Agent server Configuration" +[`TLS`]: /docs/configuration/tls.html "Nomad Agent tls Configuration" +[`Vault`]: /docs/configuration/vault.html "Nomad Agent vault Configuration" [go-sockaddr/template]: https://godoc.org/github.com/hashicorp/go-sockaddr/template -[consul]: /docs/configuration/consul.html "Nomad Agent consul Configuration" -[vault]: /docs/configuration/vault.html "Nomad Agent vault Configuration" -[tls]: /docs/configuration/tls.html "Nomad Agent tls Configuration" -[client]: /docs/configuration/client.html "Nomad Agent client Configuration" -[sentinel]: /docs/configuration/sentinel.html "Nomad Agent sentinel Configuration" -[server]: /docs/configuration/server.html "Nomad Agent server Configuration" -[acl]: /docs/configuration/acl.html "Nomad Agent ACL Configuration" -[plugin]: /docs/configuration/plugin.html "Nomad Agent Plugin Configuration" +[hcl]: https://github.com/hashicorp/hcl "HashiCorp Configuration Language" diff --git a/website/source/guides/install/windows-service.html.md b/website/source/guides/install/windows-service.html.md index f59b60a365a1..ca0c3e9d3143 100644 --- a/website/source/guides/install/windows-service.html.md +++ b/website/source/guides/install/windows-service.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "guides" page_title: "Nomad as a Windows Service" sidebar_current: "guides-install-windows-service" description: |- @@ -17,7 +17,7 @@ to register the Nomad application with the Windows Service Control Manager using ## Register Nomad with Windows -Download the Nomad binary for your architecture. +[Download] the Nomad binary for your architecture. Use the [`sc.exe`] command to create a Service named "Nomad". The binPath argument should include the fully qualified path to the Nomad executable and any @@ -72,4 +72,5 @@ The service automatically starts up during/after boot, so you don't need to launch Nomad from the command-line again. [`sc.exe`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682107(v=vs.85).aspx +[Download]: /downloads.html [logging]: /docs/configuration/index.html#log_file diff --git a/website/source/layouts/guides.erb b/website/source/layouts/guides.erb index 6233a61f43a4..70ed6a336b70 100644 --- a/website/source/layouts/guides.erb +++ b/website/source/layouts/guides.erb @@ -34,7 +34,7 @@ > - As a Windows Service + Windows Service From 0bb81e47a9fbb38e5d4734767c3310c5f53a385a Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Wed, 6 Nov 2019 15:01:27 -0500 Subject: [PATCH 5/9] rename package --- command/agent/command.go | 4 ++-- {service_os => helper/winsvc}/service.go | 2 +- {service_os => helper/winsvc}/service_windows.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename {service_os => helper/winsvc}/service.go (83%) rename {service_os => helper/winsvc}/service_windows.go (97%) diff --git a/command/agent/command.go b/command/agent/command.go index 3af6452d50dc..d257876e3831 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -29,8 +29,8 @@ import ( flaghelper "github.com/hashicorp/nomad/helper/flag-helpers" gatedwriter "github.com/hashicorp/nomad/helper/gated-writer" "github.com/hashicorp/nomad/helper/logging" + "github.com/hashicorp/nomad/helper/winsvc" "github.com/hashicorp/nomad/nomad/structs/config" - "github.com/hashicorp/nomad/service_os" "github.com/hashicorp/nomad/version" "github.com/mitchellh/cli" "github.com/posener/complete" @@ -778,7 +778,7 @@ WAIT: select { case s := <-signalCh: sig = s - case <-service_os.Shutdown_Channel(): + case <-winsvc.Shutdown_Channel(): sig = os.Interrupt case <-c.ShutdownCh: sig = os.Interrupt diff --git a/service_os/service.go b/helper/winsvc/service.go similarity index 83% rename from service_os/service.go rename to helper/winsvc/service.go index 432baaf2ad8b..243004741fb1 100644 --- a/service_os/service.go +++ b/helper/winsvc/service.go @@ -1,4 +1,4 @@ -package service_os +package winsvc var chanGraceExit = make(chan int) diff --git a/service_os/service_windows.go b/helper/winsvc/service_windows.go similarity index 97% rename from service_os/service_windows.go rename to helper/winsvc/service_windows.go index d7fc245f52bc..306de08cb1be 100644 --- a/service_os/service_windows.go +++ b/helper/winsvc/service_windows.go @@ -1,6 +1,6 @@ //+build windows -package service_os +package winsvc import ( wsvc "golang.org/x/sys/windows/svc" From 54e2f605893ce21121807ede07c08504b8da9b50 Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Wed, 6 Nov 2019 15:18:10 -0500 Subject: [PATCH 6/9] send StopPending to windows before sending graceful exit --- helper/winsvc/service_windows.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helper/winsvc/service_windows.go b/helper/winsvc/service_windows.go index 306de08cb1be..0f71e18bbe7d 100644 --- a/helper/winsvc/service_windows.go +++ b/helper/winsvc/service_windows.go @@ -13,6 +13,7 @@ func init() { if err != nil { panic(err) } + // Cannot run as a service when running interactively if interactive { return } @@ -32,8 +33,8 @@ func (serviceWindows) Execute(args []string, r <-chan wsvc.ChangeRequest, s chan case wsvc.Interrogate: s <- c.CurrentStatus case wsvc.Stop, wsvc.Shutdown: - chanGraceExit <- 1 s <- wsvc.Status{State: wsvc.StopPending} + chanGraceExit <- 1 return false, 0 } } From a7b487439e24a97c13376970e79ef1aed43c9c46 Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Wed, 6 Nov 2019 15:50:33 -0500 Subject: [PATCH 7/9] removed extra start pending signal --- helper/winsvc/service_windows.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/helper/winsvc/service_windows.go b/helper/winsvc/service_windows.go index 0f71e18bbe7d..c46ab8a6f4d3 100644 --- a/helper/winsvc/service_windows.go +++ b/helper/winsvc/service_windows.go @@ -24,8 +24,6 @@ func init() { func (serviceWindows) Execute(args []string, r <-chan wsvc.ChangeRequest, s chan<- wsvc.Status) (svcSpecificEC bool, exitCode uint32) { const accCommands = wsvc.AcceptStop | wsvc.AcceptShutdown - s <- wsvc.Status{State: wsvc.StartPending} - s <- wsvc.Status{State: wsvc.Running, Accepts: accCommands} for { c := <-r From f2a4c41854de37c7caac27fdb03f46a209339f6b Mon Sep 17 00:00:00 2001 From: Charlie Voiselle <464492+angrycub@users.noreply.github.com> Date: Mon, 11 Nov 2019 09:34:57 -0500 Subject: [PATCH 8/9] Fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4bc594381fb..882da6a37a77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ BUG FIXES: * scheduler: Changes to devices in resource stanza should cause rescheduling [[GH-6644](https://github.com/hashicorp/nomad/issues/6644)] * api: Decompress web socket response body if gzipped on error responses [[GH-6650](https://github.com/hashicorp/nomad/issues/6650)] * api: Return a 404 if endpoint not found instead of redirecting to /ui/ [[GH-6658](https://github.com/hashicorp/nomad/issues/6658)] - + ## 0.10.1 (November 4, 2019) BUG FIXES: From 608f4da72d918688f3adccfb6dcbf0645e035ed5 Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Mon, 11 Nov 2019 14:04:51 -0500 Subject: [PATCH 9/9] fixup naming --- command/agent/command.go | 2 +- helper/winsvc/service.go | 4 +++- helper/winsvc/service_windows.go | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/command/agent/command.go b/command/agent/command.go index d257876e3831..d90429211ae1 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -778,7 +778,7 @@ WAIT: select { case s := <-signalCh: sig = s - case <-winsvc.Shutdown_Channel(): + case <-winsvc.ShutdownChannel(): sig = os.Interrupt case <-c.ShutdownCh: sig = os.Interrupt diff --git a/helper/winsvc/service.go b/helper/winsvc/service.go index 243004741fb1..c8d21f5c7d0a 100644 --- a/helper/winsvc/service.go +++ b/helper/winsvc/service.go @@ -2,6 +2,8 @@ package winsvc var chanGraceExit = make(chan int) -func Shutdown_Channel() <-chan int { +// ShutdownChannel returns a channel that sends a message that a shutdown +// signal has been received for the service. +func ShutdownChannel() <-chan int { return chanGraceExit } diff --git a/helper/winsvc/service_windows.go b/helper/winsvc/service_windows.go index c46ab8a6f4d3..eeecf1b82dad 100644 --- a/helper/winsvc/service_windows.go +++ b/helper/winsvc/service_windows.go @@ -17,11 +17,12 @@ func init() { if interactive { return } - go func() { - _ = wsvc.Run("", serviceWindows{}) - }() + go wsvc.Run("", serviceWindows{}) } +// Execute implements the Windows service Handler type. It will be +// called at the start of the service, and the service will exit +// once Execute completes. func (serviceWindows) Execute(args []string, r <-chan wsvc.ChangeRequest, s chan<- wsvc.Status) (svcSpecificEC bool, exitCode uint32) { const accCommands = wsvc.AcceptStop | wsvc.AcceptShutdown s <- wsvc.Status{State: wsvc.Running, Accepts: accCommands}