Skip to content

Fix UseSystemd() silently failing with ProtectProc=invisible#125520

Open
CybCorv wants to merge 5 commits intodotnet:mainfrom
CybCorv:fix/systemd-protect-proc-invisible
Open

Fix UseSystemd() silently failing with ProtectProc=invisible#125520
CybCorv wants to merge 5 commits intodotnet:mainfrom
CybCorv:fix/systemd-protect-proc-invisible

Conversation

@CybCorv
Copy link

@CybCorv CybCorv commented Mar 13, 2026

Summary

UseSystemd() silently fails when the service is configured with ProtectProc=invisible (recommended by systemd.exec(5) for most services).

GetIsSystemdService() reads /proc/{ppid}/comm to check whether the parent process is named systemd. Under ProtectProc=invisible, this file is hidden for processes owned by other users, causing an exception that is silently swallowed — the method returns false, no IHostLifetime is registered, READY=1 is never sent, and systemd times out waiting for the service to become ready.

Fix

Use $SYSTEMD_EXEC_PID (introduced in systemd v248) as the primary detection method. systemd sets this variable to the PID of the main service process; comparing it to Environment.ProcessId is reliable and unaffected by ProtectProc.

The existing /proc/{ppid}/comm check is kept as a fallback for older systemd versions still in use on maintained distributions (Ubuntu 20.04 ships systemd 245, Debian 10 ships systemd 241).

Out of scope

Two related issues are deferred to separate PRs to keep this change focused and easy to review:

  • SystemdNotifier never unsets NOTIFY_SOCKET after capturing it
  • SystemdConsoleFormatter should use $JOURNAL_STREAM + fstat instead of unconditionally writing journal-formatted output

Testing

Tests use RemoteExecutor to isolate the static cache in SystemdHelpers._isSystemdService.

Partially addresses #88660
See #125368

GetIsSystemdService() used to read /proc/{ppid}/comm to verify the parent process is named "systemd". This file is hidden when the service runs with ProtectProc=invisible (recommended by systemd.exec(5)), causing the check to silently return false. As a result, no IHostLifetime is registered and READY=1 is never sent, leading to a systemd start timeout.

Fix this by using the SYSTEMD_EXEC_PID environment variable (systemd v248+), which is set to the PID of the main service process. Comparing it to the current PID is reliable and unaffected by ProtectProc=invisible.

The /proc fallback is kept for compatibility with older systemd versions (e.g. Ubuntu 20.04 / systemd 245, Debian 10 / systemd 241).

Partially addresses dotnet#88660
See dotnet#125368
Copilot AI review requested due to automatic review settings March 13, 2026 14:32
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Mar 13, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-extensions-hosting
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Microsoft.Extensions.Hosting.Systemd systemd-service detection so UseSystemd() works when /proc access is restricted by ProtectProc=invisible, and adds tests for the new detection behavior.

Changes:

  • Prefer $SYSTEMD_EXEC_PID (systemd v248+) to detect whether the current process is the main systemd service process.
  • Keep the existing /proc/{ppid}/comm check as a legacy fallback for older systemd versions.
  • Add Linux-only RemoteExecutor tests covering $SYSTEMD_EXEC_PID matching/mismatching/missing/malformed and the existing static caching behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs Adds $SYSTEMD_EXEC_PID-based detection before the legacy /proc fallback.
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/SystemdHelpersTests.cs New RemoteExecutor tests for detection behavior and caching.
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/Microsoft.Extensions.Hosting.Systemd.Tests.csproj Enables RemoteExecutor support for the test project and fixes a trailing space in the ProjectReference.

@CybCorv
Copy link
Author

CybCorv commented Mar 13, 2026

Contributor License Agreement

@dotnet-policy-service agree

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 13, 2026 14:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes UseSystemd() incorrectly not detecting systemd services when /proc/{ppid}/comm is inaccessible under ProtectProc=invisible, by switching to $SYSTEMD_EXEC_PID (systemd v248+) as the primary detection signal while keeping the /proc parent-name check as a legacy fallback.

Changes:

  • Add $SYSTEMD_EXEC_PID-based detection to SystemdHelpers.GetIsSystemdService() with legacy fallback retained.
  • Add Linux-only RemoteExecutor tests covering env-var detection and caching behavior.
  • Update the test project to include RemoteExecutor infrastructure and fix a malformed ProjectReference.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/SystemdHelpersTests.cs Adds RemoteExecutor-isolated tests validating SYSTEMD_EXEC_PID behavior and caching.
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/Microsoft.Extensions.Hosting.Systemd.Tests.csproj Enables RemoteExecutor support and fixes ProjectReference formatting.
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs Uses SYSTEMD_EXEC_PID as the primary systemd detection method; retains /proc fallback.

using Microsoft.Extensions.Hosting.Systemd;
using Xunit;

namespace Microsoft.Extensions.Hosting
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping Microsoft.Extensions.Hosting to match the existing convention in this test project.
See UseSystemdTests.cs.

Copilot AI review requested due to automatic review settings March 17, 2026 13:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes UseSystemd() incorrectly detecting “not running under systemd” when ProtectProc=invisible prevents reading /proc/<ppid>/comm, by preferring the SYSTEMD_EXEC_PID environment variable (systemd v248+) for detection and keeping the legacy /proc check as a fallback.

Changes:

  • Update systemd-service detection to use SYSTEMD_EXEC_PID (v248+) as the primary signal.
  • Keep the existing /proc/<ppid>/comm parent-name check as a legacy fallback path.
  • Add Linux-only unit tests (using RemoteExecutor) to cover the new detection logic and the static caching behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs Prefer SYSTEMD_EXEC_PID-based detection to avoid /proc access failures under ProtectProc=invisible, with legacy fallback retained.
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/SystemdHelpersTests.cs Adds RemoteExecutor-isolated tests for SYSTEMD_EXEC_PID match/mismatch/absent/malformed and caching behavior.
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/Microsoft.Extensions.Hosting.Systemd.Tests.csproj Enables RemoteExecutor usage for the new tests and fixes the project reference formatting.

@cincuranet cincuranet requested a review from tmds March 18, 2026 07:37
@cincuranet cincuranet self-assigned this Mar 18, 2026
@cincuranet cincuranet self-requested a review March 18, 2026 07:38
@CybCorv CybCorv marked this pull request as ready for review March 18, 2026 11:11
Copilot AI review requested due to automatic review settings March 18, 2026 11:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Microsoft.Extensions.Hosting.Systemd’s systemd-service detection so UseSystemd() doesn’t silently no-op when /proc access is restricted (e.g., ProtectProc=invisible), and adds focused unit tests for the new detection behavior.

Changes:

  • Prefer $SYSTEMD_EXEC_PID (systemd v248+) to detect running under systemd without relying on /proc.
  • Retain existing /proc/<ppid>/comm parent-name check as a fallback for older systemd versions.
  • Add RemoteExecutor-based tests to validate SYSTEMD_EXEC_PID handling and static caching; enable RemoteExecutor in the test project.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs Adds $SYSTEMD_EXEC_PID-based detection with legacy /proc fallback.
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/SystemdHelpersTests.cs New RemoteExecutor tests covering matching/mismatching/absent/malformed values and caching.
src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/Microsoft.Extensions.Hosting.Systemd.Tests.csproj Enables RemoteExecutor; fixes a stray space in the ProjectReference include.

@CybCorv
Copy link
Author

CybCorv commented Mar 18, 2026

Manual integration testing

I've validated the fix against a full test matrix using a dedicated test harness: https://github.com/CybCorv/systemd-dotnet-hosting-tests

Scenario .NET ProtectProc IsSystemdService Result
System service + notify 10 (unfixed) Yes False ❌ timeout
System service + notify 11 (this PR) Yes True ✅ Started
System service + notify 10/11 No True ✅ Started
User service + notify 10/11 Yes* True ✅ Started
Podman + --sdnotify=container 10/11 Yes* True (PID=1) ✅ Started
Docker + notify 10/11 Yes/No True (PID=1) ⚠️ timeout†
  • ProtectProc has no effect in user-scoped services (systemd limitation).
  • Docker timeout is a pre-existing issue -NOTIFY_SOCKET is not proxied to the notifier socket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Extensions-Hosting community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants