如何配置使用Go编写的Windows服务的故障操作?

huangapple go评论69阅读模式
英文:

How do I configure failure actions of a Windows service written in Go?

问题

我正在使用golang.org/x/sys/windows/svc包在Go中编写一个Windows服务。

到目前为止,一切都很顺利,使用起来非常简单,我很喜欢。

我已经编写了一些自动更新功能,并希望服务在完成更新后重新启动自己。

我尝试使用SCM生成一个重新启动服务的进程,但它记录了一个错误消息,似乎是因为在以Local System身份运行时尝试控制服务。

服务进程无法连接到服务控制器。

一个更好/更简单的方法似乎是使用os.Exit(1),并将服务的Failure Actions设置为Restart on Failure,这个方法非常好用!

唯一的问题是,似乎没有使用Go来以编程方式配置这些选项的功能。

我进行了一些调查,看起来它们是通过将一个结构体传递给advapi32.dll中的ChangeServiceConfig2来配置的- https://stackoverflow.com/questions/3242505/how-to-create-service-which-restarts-on-crash

golang/sys/blob/master/windows/svc/mgr/config.go中的func updateDescription(handle windows.Handle, desc string) error函数中,代码已经调用了windows.ChangeServiceConfig2,它是一个指向DLL调用的链接。

SERVICE_FAILURE_ACTIONS结构的Microsoft文档在这里

我在弄清楚如何使用Go构建和传递该结构时遇到了一些问题- 有人有什么见解吗?

英文:

I'm writing a Windows Service in Go 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 which seems to be to do with trying to control the service while running as Local System.

The service process could not connect to the service controller. 

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 using Go.

I've done some digging and it looks like they are configured by passing a struct to ChangeServiceConfig2 in advapi32.dll - https://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.

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

答案1

得分: 2

根据这里的指导以及阅读现有的Go Windows服务接口的源代码,我得出了自己的答案,我将尝试在下面进行记录。

在使用Windows DLL时,可以参考MSDN文档这里

我的代码如下:

import (
    "unsafe"
    "golang.org/x/sys/windows"
)

const (
    SC_ACTION_NONE                      = 0
    SC_ACTION_RESTART                   = 1
    SC_ACTION_REBOOT                    = 2
    SC_ACTION_RUN_COMMAND               = 3

    SERVICE_CONFIG_FAILURE_ACTIONS      = 2
)

type SERVICE_FAILURE_ACTIONS struct {
    ResetPeriod     uint32
    RebootMsg       *uint16
    Command         *uint16
    ActionsCount    uint32
    Actions         uintptr
}

type SC_ACTION struct {
    Type            uint32
    Delay           uint32
}

func setServiceFailureActions(handle windows.Handle) error {
    t := []SC_ACTION{
        { Type: SC_ACTION_RESTART, Delay: uint32(1000) },
        { Type: SC_ACTION_RESTART, Delay: uint32(10000) },
        { Type: SC_ACTION_RESTART, Delay: uint32(60000) },
    }
    m := SERVICE_FAILURE_ACTIONS{ ResetPeriod: uint32(60), ActionsCount: uint32(3), Actions: uintptr(unsafe.Pointer(&t[0])) }

    return windows.ChangeServiceConfig2(handle, SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&m)))
}

在我的基本示例中,你需要传递一个服务句柄,然后它将将故障操作设置为硬编码的默认值:

  1. 第一次故障后1秒钟重新启动。
  2. 第二次故障后10秒钟重新启动。
  3. 第三次故障和以后的故障后60秒钟重新启动。
  4. 在60秒后重置故障计数器。

我刚刚测试了一下,似乎工作正常。

英文:

After some guidance from here, plus reading through the source code for the existing Go Windows Service interface, I came up with my own answer, which I'll try to document below.

For type reference when working with the Windows DLLs, the MSDN docs are here.

My code looks like this:

import (
    "unsafe"
    "golang.org/x/sys/windows"
)

const (
    SC_ACTION_NONE                      = 0
    SC_ACTION_RESTART                   = 1
    SC_ACTION_REBOOT                    = 2
    SC_ACTION_RUN_COMMAND               = 3

    SERVICE_CONFIG_FAILURE_ACTIONS      = 2
)

type SERVICE_FAILURE_ACTIONS struct {
    ResetPeriod     uint32
    RebootMsg       *uint16
    Command         *uint16
    ActionsCount    uint32
    Actions         uintptr
}

type SC_ACTION struct {
    Type            uint32
    Delay           uint32
}

func setServiceFailureActions(handle windows.Handle) error {
    t := []SC_ACTION{
        { Type: SC_ACTION_RESTART, Delay: uint32(1000) },
        { Type: SC_ACTION_RESTART, Delay: uint32(10000) },
        { Type: SC_ACTION_RESTART, Delay: uint32(60000) },
    }
    m := SERVICE_FAILURE_ACTIONS{ ResetPeriod: uint32(60), ActionsCount: uint32(3), Actions: uintptr(unsafe.Pointer(&t[0])) }

    return windows.ChangeServiceConfig2(handle, SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&m)))
}

In my basic example, you need to pass a Service Handle, then it'll set the failure actions to a hard coded default of:

  1. Restart the first time after 1 second.
  2. Restart the second time after 10 seconds.
  3. Restart the third time and any subsequent times after 60 seconds.
  4. Reset the failure counter after 60 seconds.

I've just tested and it seems to work ok.

huangapple
  • 本文由 发表于 2016年2月2日 18:35:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/35151052.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定