Description
I have a program which, in some cases, runs under Windows Services / Launchd / Systemd, and in other cases, is launched by a supervisor program I control. When using the supervisor program, I need a way to signal to the "service" when I want it to quit. On Linux, I can use signals, but on Windows, this is not supported, so I am using an RPC call. The problem is that I can't find a way to actually get the program to terminate besides calling os.Exit()
which will not necessarily clean up all resources correctly.
I found this example which looked like you could manually call Stop from within the process: https://github.com/kardianos/service/blob/master/example/runner/runner.go#L60-L66
But when I actually tried this, the program does not exit. PoC below:
package main
import (
"fmt"
"log"
"time"
"github.com/kardianos/service"
)
var logger service.Logger
type program struct{}
func (p *program) Start(s service.Service) error {
fmt.Println("Service starting...")
go p.run(s)
return nil
}
func (p *program) run(s service.Service) {
fmt.Println("Running work function...")
fmt.Println("Work completed. This function has returned.")
// Note: Even though this function returns, the program won't exit when run interactively
p.Stop(s)
}
func (p *program) Stop(s service.Service) error {
fmt.Println("Service stopping...")
// Stop should not block. Return within a few seconds.
return nil
}
func main() {
svcConfig := &service.Config{
Name: "GoServiceExampleSimple",
DisplayName: "Go Service Example",
Description: "This is an example Go service.",
}
prg := &program{}
s, err := service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
logger, err = s.Logger(nil)
if err != nil {
log.Fatal(err)
}
// Check if running interactively using the proper API
isInteractive := service.Interactive()
if isInteractive {
fmt.Println("Running in interactive mode")
fmt.Println("Notice: This program will NOT exit automatically after work completes")
fmt.Println("Press Ctrl+C to exit manually")
}
// Add a timestamp to show when execution starts
fmt.Printf("Program started at: %s\n", time.Now().Format(time.RFC3339))
err = s.Run()
if err != nil {
logger.Error(err)
}
// This line will never be reached when running interactively
fmt.Println("Program exited normally")
}
I believe it's due to waiting on this signal, which I can't trigger or access the channel of to manually close:
Lines 397 to 401 in becf2eb
Or on macOS:
Lines 274 to 276 in becf2eb