Windows Service Failure Actions

288 views
Skip to first unread message

s...@infitialis.com

unread,
Feb 2, 2016, 1:17:02 PM2/2/16
to golang-nuts
Hello,

I'm writing a Windows Service in GoLang using the "golang.org/x/sys/windows/svc" package.

So far, it's all working great and so easy to get started with, I love it.

I've written in some auto update functionality and I'm wanting the service to restart itself when it has completed an update.
I've tried spawning a process that will restart the service using the SCM, but it logs an error message of "The service process could not connect to the service controller", which seems to be to do with trying to control the service while running as Local System.
A better/easier way seems to be to "os.Exit(1)" and have the service "Failure Actions" set to "Restart on Failure", which works brilliantly!

The only trouble is, there doesn't seem to be the functionality to configure those options programatically from GoLang :(.

I've done some digging and it looks like they are configured by passing a struct to ChangeServiceConfig2 in advapi32.dll - http://stackoverflow.com/questions/3242505/how-to-create-service-which-restarts-on-crash

In golang/sys/blob/master/windows/svc/mgr/config.go - func updateDescription(handle windows.Handle, desc string) error
The code already calls windows.ChangeServiceConfig2 which is a link to the DLL call.
And the Microsoft docs for the SERVICE_FAILURE_ACTIONS struct are here: https://msdn.microsoft.com/en-us/library/ms685939(v=vs.85).aspx

I'm having trouble figuring out how to build and pass that struct in GoLang - does anyone have any insights?

Regards,
Samuel Melrose.

Daniel Theophanes

unread,
Feb 2, 2016, 1:52:52 PM2/2/16
to golang-nuts, s...@infitialis.com
You define a struct in Go the same way you define a struct in C. You just need to translate the windows C macros into equivalent Go types.

So in C we have:
typedef struct _SERVICE_DESCRIPTION {
  LPTSTR lpDescription;
} SERVICE_DESCRIPTION, *LPSERVICE_DESCRIPTION;

Which translates to Go as:
type SERVICE_DESCRIPTION struct {
Description *uint16
}

Se we do the same with the next struct:
typedef struct _SERVICE_FAILURE_ACTIONS {
  DWORD     dwResetPeriod;
  LPTSTR    lpRebootMsg;
  LPTSTR    lpCommand;
  DWORD     cActions;
  SC_ACTION *lpsaActions;
} SERVICE_FAILURE_ACTIONS, *LPSERVICE_FAILURE_ACTIONS;


type SC_ACTION struct {
ActionType uint32 // I think it is an int32 type... values: https://msdn.microsoft.com/en-us/library/ms685126(v=vs.85).aspx
Delay uint32
}
type SERVICE_FAILURE_ACTIONS struct {
ResetPeriod uint32
RebootMsg *uint16
Command *uint16
ActionCount uint32 // length of action slice.
Actions *SC_ACTION // grab a pointer from the first element in slice.
}

I could be off some of these, but I think they should work.

You could also install the service as a higher privileged system user, rather then the default one. Although then you might need two services.

-Daniel

s...@infitialis.com

unread,
Feb 25, 2016, 8:47:44 AM2/25/16
to golang-nuts, s...@infitialis.com
Thanks Daniel,

I had a play and figured it out - most of what you'd said was spot on, just had to tweak the pointer to array of actions bit a little.


:).
Reply all
Reply to author
Forward
0 new messages