Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

the ability to set the Recovery options #49

Closed
pyed opened this issue Dec 2, 2015 · 13 comments
Closed

the ability to set the Recovery options #49

pyed opened this issue Dec 2, 2015 · 13 comments

Comments

@pyed
Copy link

pyed commented Dec 2, 2015

I believe the current service package doesn't not offer the ability to set the recovery options for windows, such as on first failure, second failure, subsequent failure, to restart the service, or restart the computer.

s, err = m.CreateService(ws.Name, exepath, mgr.Config{
    DisplayName:      ws.DisplayName,
    Description:      ws.Description,
    StartType:        mgr.StartAutomatic,
    ServiceStartName: ws.UserName,
    Password:         ws.Option.string("Password", ""),
    Dependencies:     ws.Dependencies,
}, ws.Arguments...)

mgr.Config type

type Config struct {
    ServiceType      uint32
    StartType        uint32
    ErrorControl     uint32
    BinaryPathName   string // fully qualified path to the service binary file, can also include arguments for an auto-start service
    LoadOrderGroup   string
    TagId            uint32
    Dependencies     []string
    ServiceStartName string // name of the account under which the service should run
    DisplayName      string
    Password         string
    Description      string
}

what's the best way to set my service to restart on failure ?

@kardianos
Copy link
Owner

Right now, manually.

I would be open to a patch that used the Config.Option map to set a windows specific recovery mode.

@pyed
Copy link
Author

pyed commented Dec 2, 2015

there's ErrorControl in the Config, but I couldn't figure out how to deal with it, maybe @alexbrainman can enlighten us

@alexbrainman
Copy link

I never had the need for that, but @bogdanteleaga implemented something similar here https://github.com/juju/juju/blob/master/service/windows/service_windows.go#L396

Alex

@pyed
Copy link
Author

pyed commented Dec 6, 2015

thanks @alexbrainman for pointing me at this.

I had the time to try and figure out and copy over some code from juju/juju over to our package here.
but I kept hitting an error at the end.

so what I did is that I extracted the code into one individual Go main package to ease the process of debugging it

package main

import (
    "fmt"
    "unsafe"

    "golang.org/x/sys/windows"
    "golang.org/x/sys/windows/svc/mgr"
)

// this service should get its recovery options set to restart
var SERVICE_NAME = "insert service name here"

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988(v=vs.85).aspx
const (
    SERVICE_CONFIG_FAILURE_ACTIONS      = 2
    SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 4
)

const (
    SC_ACTION_NONE = iota
    SC_ACTION_RESTART
    SC_ACTION_REBOOT
    SC_ACTION_RUN_COMMAND
)

type serviceAction struct {
    actionType uint16
    delay      uint32
}

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms685939(v=vs.85).aspx
type serviceFailureActions struct {
    dwResetPeriod uint32
    lpRebootMsg   *uint16
    lpCommand     *uint16
    cActions      uint32
    scAction      *serviceAction
}

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms685937(v=vs.85).aspx
type serviceFailureActionsFlag struct {
    failureActionsOnNonCrashFailures bool
}

func main() {
    m, err := mgr.Connect()
    if err != nil {
        panic(err)
    }
    defer m.Disconnect()

    s, err := m.OpenService(SERVICE_NAME)
    if err != nil {
        panic(err)
    }
    defer s.Close()

    action := serviceAction{
        actionType: SC_ACTION_RESTART,
        delay:      5000,
    }
    failActions := serviceFailureActions{
        dwResetPeriod: 5,
        lpRebootMsg:   nil,
        lpCommand:     nil,
        cActions:      1,
        scAction:      &action,
    }

    err = windows.ChangeServiceConfig2(s.Handle, SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&failActions)))
    if err != nil {
        panic(err)
    }

    flag := serviceFailureActionsFlag{
        failureActionsOnNonCrashFailures: true,
    }

    err = windows.ChangeServiceConfig2(s.Handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, (*byte)(unsafe.Pointer(&flag)))
    if err != nil {  // it will hit this error: The parameter is incorrect
        panic(err)
    }
}

SERVICE_NAME should get changed to any service name, and when this program gets executed, that service should has its recovery options set to restart on failure, just like juju/juju, but sadly it kept hitting that error, the first call to ChangeServiceConfig2 passed, but not the second.

perhaps @gabriel-samfira can help us with this.

@bogdanteleaga
Copy link

So juju is still using go1.2.1. I tried it out and it seems that using 1.2.1 the code works, but you have to use gabriel's forks that actually compile on 1.2.1. The 2nd call does seem to panic on 1.5, which is peculiar and problematic at the same time since juju wants to migrate to 1.5. I will have to try a new go environment though, the one I'm using now is all sorts of messed up and might give odd results, but it does confirm what you found so far.

EDIT: I can confirm this doesn't work with neither a development go version of 1.5 I had installed nor go 1.5.2

@gabriel-samfira
Copy link

switch from bool to int in serviceFailureActionsFlag and give that a shot.

@bogdanteleaga
Copy link

Yup converting it to int works in go 1.5

@pyed
Copy link
Author

pyed commented Dec 6, 2015

@bogdanteleaga @gabriel-samfira your help is much appreciated, it works for me now with 1.5.2 and golang.org/x/sys/windows

@gabriel-samfira
Copy link

that is great! Glad we could help :).

@pyed pyed closed this as completed Dec 6, 2015
@alexbrainman
Copy link

Windows BOOL type occupies 4 bytes. But Go int is int64 on windows/amd64, isn't it? Better use uint32 (or int32) where Windows BOOL is used.

Alex

@pyed
Copy link
Author

pyed commented Dec 7, 2015

@alexbrainman good point. they both works after testing.

@kardianos how do you propose migrating such code into service ? it can't go as a method of service.Service because that would break the interface, should we make the default behavior and let Install() call it ? or should we just keep it out of the package and leave this issue as a reference to anyone who wants to play with the recovery options

@clns
Copy link
Contributor

clns commented Dec 7, 2015

Check this out #33 (comment) for some ideas on how to implement this.

jujubot added a commit to juju/juju that referenced this issue Dec 15, 2015
…yscall-for-go15

service/windows: comply syscall structure with go1.5

As seen [here](kardianos/service#49 (comment)), it seems like using booleans in go1.5 causes a problem that can be easily solved by using int32.

(Review request: http://reviews.vapour.ws/r/3383/)
jessecarbon pushed a commit to jessecarbon/service that referenced this issue Nov 4, 2016
…aults

Update server_url defaults for Graylog 2.1.x
@vpoliakov01
Copy link

For anyone wondering, it was implemented via these options: https://github.com/kardianos/service/blob/master/service.go#L197-L206

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants