kardianos / service

Run go programs as a service on major platforms.
zlib License
4.44k stars 677 forks source link

Propagate windows service start parameters #82

Open 2opremio opened 7 years ago

2opremio commented 7 years ago

This library simply ignores and doesn't propagate the start parameters of windows services.

Those live parameters are very convenient and flexible, allowing to avoid configuration files or registry keys to store the parameters.

Related to #75

2opremio commented 7 years ago

A new method (e.g. Args() []string) could be added to Service without breaking backwards compatibility.

Would you be interested in a PR?

2opremio commented 7 years ago

The Start() method should also pass the arguments (and that one is tricky to change without breaking backwards compatibility)

kardianos commented 7 years ago

Are the args passed into Execute different then os.Args()?

2opremio commented 7 years ago

Yes, Windows seems to distinguish between:

AFAIU the arguments passed to Execute() are the ones provided to StartService (from advapi.dll)

As an example, both sc.exe and the Services UI let you pass those arguments on start:

$ sc.exe start
DESCRIPTION:
        Starts a service running.
USAGE:
        sc <server> start [service name] <arg1> <arg2> ...
The following WinRM command responded with a non-zero exit status.
screen shot 2017-02-20 at 19 46 30

Note that, in the last example you are free to choose service start arguments different from the ones passed to the binary (-k netsvcs)

zhengxiaoyao0716 commented 7 years ago

@kardianos I got the problem too. First, when I run my code with go run, it works correctly: image The first two line print by fmt, execute before service start. The other print by log, witch would redirect to log file in service mode.

However, when I run my code with builded binary executer, the problem appeared: image image You can see that fmt print the os.Args correctly, while log missing them.

rgl commented 7 years ago

Any news about this? Something like what @2opremio proposed would be really nice to have :-)

rgl commented 7 years ago

Oh, we can already manually propagate arguments! Sorry about the noise...

I ended up using something like:

    nameFlag := flag.String("name", "dummy", "Service name.")
    flag.Parse()
    s, err := service.New(
        &dummyService{},
        &service.Config{
            Arguments:   []string{"-name", *nameFlag},
            Name:        *nameFlag,
            DisplayName: "Dummy Windows Service",
            Description: "Dummy Windows Service",
        })
jonathan-automox commented 6 years ago

Can this technique of passing arguments be used to say create a "Delayed" start service?

kardianos commented 6 years ago

Yes.

chuanbozhang-okta commented 4 years ago

Oh, we can already manually propagate arguments! Sorry about the noise... &service.Config{ Arguments: []string{"-name", *nameFlag}, })


@rgl , does this example actually propagate the live argument as @2opremio called out?
And how to access this parameters from the actual service start method when the parameter not passed through executable but through sc.exe or service control manager?
chuanbozhang-okta commented 4 years ago

I did more investigation, and see adding a line of ws.Arguments = append(ws.Arguments, args...) in Execute method of service_windows.go solved the issue for me.

func (ws *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) {
    const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
    changes <- svc.Status{State: svc.StartPending}
    ws.Arguments = append(ws.Arguments, args...)    

then in my usage, I declare service.Config to my program struct, and then in program Start method access the parameter from p.config.Arguments, and I've validated that could propagate the live arguments from service control manager.

@kardianos , how do you feel this solution?

msays2000 commented 4 years ago

@chuanbozhang-okta @kardianos @rgl Is support for runtime arguments like below supported in windows service start method?

sc start serviceName arg1 arg2 ... argN

to be explicit when a customer starts the service with a runtime port number, I need to access this runtime parameter somehow or injected somhow.

sc start MyGrpcService runtimePortNumber

chuanbozhang-okta commented 3 years ago

@msays2000 , yes, my proposal fix above to add "ws.Arguments = append(ws.Arguments, args...)" to Execute method could fix the issue and make your usage work.

@kardianos , do you accept contribution for this? If so I could send a PR for it.

msays2000 commented 3 years ago

@chuanbozhang-okta thank you for confirming. @kardianos please review and help in merging the above PR of @chuanbozhang-okta

We have a use case where we need to start a grpc service on a determined port passed to the service via its start arguments.

msays2000 commented 3 years ago

@kardianos is there an alternative to pass the arguments to a service.