Skip to content

[agent] add generic readiness notification fd support - #3478

Open
agners wants to merge 1 commit into
openthread:mainfrom
agners:agent-notify-fd
Open

[agent] add generic readiness notification fd support#3478
agners wants to merge 1 commit into
openthread:mainfrom
agners:agent-notify-fd

Conversation

@agners

@agners agners commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Motivation

otbr-agent already notifies systemd (via sd_notify(READY=1)) and Upstart (via SIGSTOP) when it is ready. Other service supervisors — s6, dinit, and similar — use a simpler, fd-based readiness protocol: the daemon writes a newline to a file descriptor provided by the supervisor (see https://skarnet.org/software/s6/notifywhenup.html).

This PR adds support for that protocol, mirroring the existing Upstart integration: a new CMake option OTBR_NOTIFY_FD (default ON, like OTBR_NOTIFY_UPSTART) compiles the support in, and at runtime the agent writes a newline to the file descriptor given in the OTBR_NOTIFY_FD environment variable — at the same point in Application::Run() where systemd/Upstart readiness is signalled. Behavior is unchanged when the variable is not set, and no new dependencies are needed.

This allows supervising otbr-agent under s6/s6-overlay (e.g. in containers) with exact, event-driven readiness instead of polling for the control socket: dependent services (ot-ctl based configuration, otbr-web) can be started exactly when the agent is initialized.

Testing

  • Built with the option ON (default) and OFF (code compiled out).
  • Ran under s6-overlay (Docker, Debian trixie) with an OpenThread simulation RCP: s6 marks the service ready exactly when the agent logs Notify readiness on file descriptor 3., and dependent services start afterwards.
  • Verified no behavior change when OTBR_NOTIFY_FD is unset, and that an invalid value only logs a warning.

🤖 Generated with Claude Code

@google-cla

google-cla Bot commented Jul 24, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for generic readiness notification for fd-based supervisors (such as s6 or dinit) in src/agent/application.cpp. It reads the OTBR_NOTIFY_FD environment variable, parses it to obtain a file descriptor, writes a newline character to signal readiness, and then closes the file descriptor. Necessary standard headers are also included. There are no review comments, and I have no feedback to provide.

@agners
agners force-pushed the agent-notify-fd branch 2 times, most recently from 88380eb to bb60222 Compare July 24, 2026 12:33
Allow fd-based service supervisors (s6, dinit, ...) to be notified of
agent readiness: when built with the OTBR_NOTIFY_FD option (default ON,
mirroring OTBR_NOTIFY_UPSTART) and the OTBR_NOTIFY_FD environment
variable is set, write a newline to that file descriptor once the agent
is ready, at the same point where systemd/Upstart readiness is
signalled.

See https://skarnet.org/software/s6/notifywhenup.html for the protocol.
@agners
agners force-pushed the agent-notify-fd branch from bb60222 to 736aefd Compare July 24, 2026 12:35
Comment thread etc/cmake/options.cmake
option(OTBR_NOTIFY_FD "Support readiness notification via file descriptor (s6, dinit, ...)." ON)
if(OTBR_NOTIFY_FD)
target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_NOTIFY_FD=1)
endif()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If we want to avoid too many compile time options, this change could also be part of the default build since the code only becomes active if OTBR_NOTIFY_FD is set.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Makes sense to me. I agree with simplifying the build config.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 8.33333% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.45%. Comparing base (2b41187) to head (736aefd).
⚠️ Report is 1573 commits behind head on main.

Files with missing lines Patch % Lines
src/agent/application.cpp 8.33% 10 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #3478       +/-   ##
===========================================
- Coverage   55.77%   44.45%   -11.32%     
===========================================
  Files          87      144       +57     
  Lines        6890    17390    +10500     
  Branches        0     1433     +1433     
===========================================
+ Hits         3843     7731     +3888     
- Misses       3047     9109     +6062     
- Partials        0      550      +550     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/agent/application.cpp
Comment on lines +40 to +43
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: can we move this above HAVE_LIBSYSTEMD above?

Comment thread src/agent/application.cpp
Comment on lines +190 to +195
char *end = nullptr;
long notifyFd = strtol(notifyFdEnv, &end, 10);

if (end != notifyFdEnv && *end == '\0' && notifyFd >= 0)
{
ssize_t rval;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggest adding bounds checking and errno validation for strtol, and ensure the file descriptor does not point to standard I/O streams (0, 1, or 2).

Suggested change
char *end = nullptr;
long notifyFd = strtol(notifyFdEnv, &end, 10);
if (end != notifyFdEnv && *end == '\0' && notifyFd >= 0)
{
ssize_t rval;
errno = 0;
long notifyFdLong = strtol(notifyFdEnv, &end, 10);
if (end != notifyFdEnv && *end == '\0' && errno == 0 && notifyFdLong >= 3 && notifyFdLong <= INT_MAX)
{
int notifyFd = static_cast<int>(notifyFdLong);
ssize_t rval;
// ... use `notifyFd` directly without repeated static_cast

Comment thread src/agent/application.cpp
Comment on lines +191 to +195
long notifyFd = strtol(notifyFdEnv, &end, 10);

if (end != notifyFdEnv && *end == '\0' && notifyFd >= 0)
{
ssize_t rval;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggest adding bounds checking and errno validation for strtol, and ensure the file descriptor does not point to standard I/O streams (0, 1, or 2).

Suggested change
long notifyFd = strtol(notifyFdEnv, &end, 10);
if (end != notifyFdEnv && *end == '\0' && notifyFd >= 0)
{
ssize_t rval;
errno = 0;
long notifyFdLong = strtol(notifyFdEnv, &end, 10);
if (end != notifyFdEnv && *end == '\0' && errno == 0 && notifyFdLong >= 3 && notifyFdLong <= INT_MAX)
{
int notifyFd = static_cast<int>(notifyFdLong);
ssize_t rval;
// ... use `notifyFd` directly without repeated static_cast

Comment thread src/agent/application.cpp
}
#endif

#if OTBR_ENABLE_NOTIFY_FD

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we move this after signal(SIGPIPE, SIG_IGN) below?

Comment thread src/agent/application.cpp
otbrLogWarning("Failed to notify readiness on file descriptor %ld: %s", notifyFd, strerror(errno));
}

close(static_cast<int>(notifyFd));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Application::Run() can be executed multiple times within the same process lifecycle (e.g., after a software reset handled via otPlatReset).

When notifyFd is closed during the initial run, the environment variable OTBR_NOTIFY_FD remains set. On a subsequent re-run of Application::Run(), the code will attempt to write to and close that file descriptor number again. By that time, the OS may have reassigned that file descriptor number to a new socket or DBus connection, causing data corruption and closing an active resource.

Should we call unsetenv() after closing the notifyFd?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants