Hello everybody,
I have a Go application that I want to run as a Windows service. When I execute the application directly (interactive mode), everything works as expected, including logging to a file. However, when I try to run it as a Windows service, it fails to start, and I get the following error:
StartService FAILED 1053: The service did not respond to the start or control request in a timely fashion.
What I’ve Tried
- Logging to a File:
- I attempted to initialize logging to a file in the directory where the executable is located using the
os.OpenFile
method. This works fine in interactive mode but does not create or write to the log file when run as a service.
- Changing File Path for Logging:
- I tried changing the log file path to a simpler location (like
d:\Logs
), but it still did not resolve the issue when running as a service.
- Checking Permissions:
- I ensured that the service runs with administrator privileges. I also verified that the directory where the log file is supposed to be written exists and has the appropriate write permissions.
- Using
sc start
for Debugging:
- I tried using
sc start som.kalenderview debug
to start the service in debug mode, but I encountered the same1053
error.
The error occurs instant after starting the service.
- The service is configured correctly with
sc create
and runs under an administrator account. - I am using the
golang.org/x/sys/windows/svc
package to manage the Windows service. - The service seems to start successfully (as indicated by the service control manager) but immediately fails with error 1053, suggesting it does not respond within the timeout period.
package main
import (
"encoding/xml"
"flag"
"fmt"
"html/template"
"log"
"net/http"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
cfg "myapp/config"
out "myapp/output"
rq "myapp/requestdata"
)
var (
staticDir = getAbsDirPath() + "/static/"
templatesDir = staticDir + "/templates"
templates = template.Must(template.ParseFiles(templatesDir + "/index.html"))
version string
)
var config cfg.Config
type myService struct{}
func (m *myService) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
s <- svc.Status{State: svc.StartPending}
elog, err := eventlog.Open("myapp")
if err != nil {
log.Fatalf("Failed to open event log: %v", err)
}
defer elog.Close()
// Sende sofort die Running-Meldung, um den SCM zu informieren
s <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
elog.Info(1, "myapp-service started.")
// Starte den HTTP-Server in einer separaten Goroutine, um Blockierungen zu vermeiden
go func() {
err := startHTTPServer(elog)
if err != nil {
elog.Error(1, fmt.Sprintf("Failed to start HTTP server: %v", err))
}
}()
// Warte auf Stop- oder Shutdown-Befehle
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Stop, svc.Shutdown:
elog.Info(1, "myapp-service is stopping.")
break loop
default:
elog.Error(1, "Unexpected control request.")
}
}
}
s <- svc.Status{State: svc.StopPending}
return false, 0
}
func startHTTPServer(elog *eventlog.Log) error {
if elog != nil {
elog.Info(1, "Starting HTTP server...")
} else {
log.Println("Starting HTTP server...")
}
http.HandleFunc("/", indexHandler)
http.HandleFunc("/scripts/", staticHandler)
http.HandleFunc("/css/", staticHandler)
http.HandleFunc("/images/", staticHandler)
err := http.ListenAndServe(":"+config.LocalPort, nil)
if err != nil && elog != nil {
elog.Error(1, fmt.Sprintf("Failed to start HTTP server: %v", err))
}
return err
}
func init() {
// Ermittelt den Pfad zur ausführbaren Datei
exePath, err := os.Executable()
if err != nil {
log.Fatalf("Failed to get executable path: %v", err)
}
// Bestimmt das Verzeichnis der ausführbaren Datei
exeDir := filepath.Dir(exePath)
// Logdateipfad in demselben Verzeichnis wie die ausführbare Datei
logFilePath := filepath.Join(exeDir, "somkalenderview_debug.log")
// Öffne die Logdatei und leite die Ausgaben dorthin um
f, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("Failed to open log file: %v", err)
}
log.SetOutput(f) // Setze die Log-Ausgabe auf die Datei
log.Println("Logging initialized successfully in", logFilePath)
}
func main() {
isInteractive, err := svc.IsAnInteractiveSession()
if err != nil {
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
}
if isInteractive {
runInteractive()
} else {
runService()
}
var vFlag bool
flag.BoolVar(&vFlag, "v", false, "show version")
flag.Parse()
if vFlag {
println(version)
}
var weiter bool = true
file, err := os.Open("config.xml")
if err != nil {
fmt.Println("Fehler beim Öffnen der XML-Datei:", err)
weiter = false
}
defer file.Close()
// Konfigurationsdaten aus der XML-Datei lesen
if weiter {
if err := xml.NewDecoder(file).Decode(&config); err != nil {
fmt.Println("Fehler beim Lesen der XML-Datei:", err)
weiter = false
}
}
}
func runService() {
run := svc.Run
err := run("myapp", &myService{})
if err != nil {
log.Fatalf("failed to run service: %v", err)
}
}
func runInteractive() {
log.Println("Running in interactive mode...")
go startHTTPServer(nil) // Starte den HTTP-Server auch im interaktiven Modus
for {
time.Sleep(10 * time.Second)
}
}
My service does not run even when I remove the HTTP server code. The service fails to start with error 1053 (“The service did not respond to the start or control request in a timely fashion”). I have tried running the service without any blocking operations (like the HTTP server), but it still fails with the same error.
The application runs perfectly fine in interactive mode (from the command line), including logging to a file, but it fails to start correctly when running as a Windows service.
What could be causing the service to fail even when the HTTP server code is removed?
Best regards Jens