From 64e0e9fcba8dd4dc5d325d016a99bb4e3ad9c12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 28 Feb 2025 08:09:44 +0100 Subject: [PATCH 1/3] fix: Support running as Windows Service within containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- cmd/windows_exporter/0_service.go | 50 ++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/cmd/windows_exporter/0_service.go b/cmd/windows_exporter/0_service.go index 3e70b820d..ec5600347 100644 --- a/cmd/windows_exporter/0_service.go +++ b/cmd/windows_exporter/0_service.go @@ -14,8 +14,11 @@ package main import ( + "errors" "fmt" "os" + "strings" + "unsafe" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc" @@ -54,7 +57,7 @@ var ( var IsService = func() bool { var err error - isService, err := svc.IsWindowsService() + isService, err := isWindowsServiceInsideContainer() if err != nil { logToFile(fmt.Sprintf("failed to detect service: %v", err)) @@ -156,3 +159,48 @@ func logToFile(msg string) { _ = file.Close() } } + +// isWindowsServiceInsideContainer is a clone of "golang.org/x/sys/windows/svc:IsWindowsService", but with a fix +// for Windows containers. +// Go cloned the .NET implementation of this function, which has since +// been patched to support Windows containers, which don't use Session ID 0 for services. +// https://github.com/dotnet/runtime/pull/74188 +// This function can be replaced with go's once go brings in the fix. +// +// Copyright 2023-present Datadog, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// https://github.com/DataDog/datadog-agent/blob/46740e82ef40a04c4be545ed8c16a4b0d1f046cf/pkg/util/winutil/servicemain/servicemain.go#L128 +func isWindowsServiceInsideContainer() (bool, error) { + var currentProcess windows.PROCESS_BASIC_INFORMATION + infoSize := uint32(unsafe.Sizeof(currentProcess)) + + err := windows.NtQueryInformationProcess(windows.CurrentProcess(), windows.ProcessBasicInformation, unsafe.Pointer(¤tProcess), infoSize, &infoSize) + if err != nil { + return false, err + } + + var parentProcess *windows.SYSTEM_PROCESS_INFORMATION + + for infoSize = uint32((unsafe.Sizeof(*parentProcess) + unsafe.Sizeof(uintptr(0))) * 1024); ; { + parentProcess = (*windows.SYSTEM_PROCESS_INFORMATION)(unsafe.Pointer(&make([]byte, infoSize)[0])) + + err = windows.NtQuerySystemInformation(windows.SystemProcessInformation, unsafe.Pointer(parentProcess), infoSize, &infoSize) + if err == nil { + break + } else if !errors.Is(err, windows.STATUS_INFO_LENGTH_MISMATCH) { + return false, err + } + } + + for ; ; parentProcess = (*windows.SYSTEM_PROCESS_INFORMATION)(unsafe.Pointer(uintptr(unsafe.Pointer(parentProcess)) + uintptr(parentProcess.NextEntryOffset))) { + if parentProcess.UniqueProcessID == currentProcess.InheritedFromUniqueProcessId { + return strings.EqualFold("services.exe", parentProcess.ImageName.String()), nil + } + + if parentProcess.NextEntryOffset == 0 { + break + } + } + + return false, nil +} From b2e1f6aa143e43c77720cfa9cc20079f31e1e810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sun, 2 Mar 2025 07:46:11 +0100 Subject: [PATCH 2/3] fix: Support running as Windows Service within containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- cmd/windows_exporter/0_service.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/windows_exporter/0_service.go b/cmd/windows_exporter/0_service.go index ec5600347..09738d4fe 100644 --- a/cmd/windows_exporter/0_service.go +++ b/cmd/windows_exporter/0_service.go @@ -72,8 +72,11 @@ var IsService = func() bool { go func() { err := svc.Run(serviceName, &windowsExporterService{}) if err != nil { - if logErr := logToEventToLog(windows.EVENTLOG_ERROR_TYPE, fmt.Sprintf("failed to start service: %v", err)); logErr != nil { - logToFile(fmt.Sprintf("failed to start service: %v", err)) + // https://github.com/open-telemetry/opentelemetry-collector/pull/9042 + if !errors.Is(err, windows.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { + if logErr := logToEventToLog(windows.EVENTLOG_ERROR_TYPE, fmt.Sprintf("failed to start service: %v", err)); logErr != nil { + logToFile(fmt.Sprintf("failed to start service: %v", err)) + } } } From 75784aba19cd9f957dfbdbb947291fc995d1a0db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sun, 2 Mar 2025 07:53:46 +0100 Subject: [PATCH 3/3] fix: Support running as Windows Service within containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- cmd/windows_exporter/0_service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/windows_exporter/0_service.go b/cmd/windows_exporter/0_service.go index 09738d4fe..9707a29bf 100644 --- a/cmd/windows_exporter/0_service.go +++ b/cmd/windows_exporter/0_service.go @@ -57,7 +57,7 @@ var ( var IsService = func() bool { var err error - isService, err := isWindowsServiceInsideContainer() + isService, err := isWindowsService() if err != nil { logToFile(fmt.Sprintf("failed to detect service: %v", err)) @@ -163,7 +163,7 @@ func logToFile(msg string) { } } -// isWindowsServiceInsideContainer is a clone of "golang.org/x/sys/windows/svc:IsWindowsService", but with a fix +// isWindowsService is a clone of "golang.org/x/sys/windows/svc:IsWindowsService", but with a fix // for Windows containers. // Go cloned the .NET implementation of this function, which has since // been patched to support Windows containers, which don't use Session ID 0 for services. @@ -173,7 +173,7 @@ func logToFile(msg string) { // Copyright 2023-present Datadog, Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // https://github.com/DataDog/datadog-agent/blob/46740e82ef40a04c4be545ed8c16a4b0d1f046cf/pkg/util/winutil/servicemain/servicemain.go#L128 -func isWindowsServiceInsideContainer() (bool, error) { +func isWindowsService() (bool, error) { var currentProcess windows.PROCESS_BASIC_INFORMATION infoSize := uint32(unsafe.Sizeof(currentProcess))