Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,6 @@
[submodule "platform/marvell-teralynx/mrvl-teralynx"]
path = platform/marvell-teralynx/mrvl-teralynx
url = https://github.com/Marvell-switching/mrvl-teralynx.git
[submodule "src/supervisor"]
path = src/supervisor
url = https://github.com/Supervisor/supervisor
Copy link
Collaborator

Choose a reason for hiding this comment

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

url

Do you want to add "branch"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure why needed, I pin submodule to specific commit

11 changes: 8 additions & 3 deletions dockers/docker-base-bookworm/Dockerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,17 @@ COPY ["pip.conf", "/etc/pip.conf"]
RUN pip3 install --upgrade pip
RUN apt purge -y python3-pip

{% if docker_base_bookworm_whls.strip() -%}
# Copy locally-built Python wheel dependencies
{{ copy_files("python-wheels/", docker_base_bookworm_whls.split(' '), "/python-wheels/") }}

# Install locally-built Python wheel dependencies
{{ install_python_wheels(docker_base_bookworm_whls.split(' ')) }}
{% endif %}

# For templating
RUN pip3 install j2cli

# Install supervisor
RUN pip3 install supervisor==4.2.5

# Add support for supervisord to handle startup dependencies
RUN pip3 install supervisord-dependent-startup==1.4.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ chmod=0700 ; socket file mode (default 0700)
[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
notifysock=/var/run/supervisord_notify.sock ; (path to the notify socket file; default None)
childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP)
user=root

Expand Down
2 changes: 2 additions & 0 deletions rules/docker-base-bookworm.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ ifeq ($(INCLUDE_FIPS), y)
$(DOCKER_BASE_BOOKWORM)_DEPENDS += $(FIPS_OPENSSL_LIBSSL) $(FIPS_OPENSSL_LIBSSL_DEV) $(FIPS_OPENSSL) $(SYMCRYPT_OPENSSL) $(FIPS_KRB5)
endif

$(DOCKER_BASE_BOOKWORM)_PYTHON_WHEELS += $(SUPERVISOR)

$(DOCKER_BASE_BOOKWORM)_DBG_IMAGE_PACKAGES += $(GDB) $(GDBSERVER) $(VIM) $(OPENSSH) $(SSHPASS) $(STRACE)

SONIC_DOCKER_IMAGES += $(DOCKER_BASE_BOOKWORM)
Expand Down
10 changes: 10 additions & 0 deletions rules/supervisor.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SPATH := $($(SUPERVISOR)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/supervisor.mk rules/supervisor.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files -- ':!:doc/*'))

$(SUPERVISOR)_CACHE_MODE := GIT_CONTENT_SHA
$(SUPERVISOR)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
$(SUPERVISOR)_DEP_FILES := $(DEP_FILES)
$(SUPERVISOR)_SMDEP_FILES := $(SMDEP_FILES)
$(SUPERVISOR)_SMDEP_PATHS := $(SPATH)
6 changes: 6 additions & 0 deletions rules/supervisor.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# supervisor python3 wheel

SUPERVISOR = supervisor-4.2.5-py2.py3-none-any.whl
$(SUPERVISOR)_SRC_PATH = $(SRC_PATH)/supervisor
$(SUPERVISOR)_PYTHON_VERSION = 3
SONIC_PYTHON_WHEELS += $(SUPERVISOR)
1 change: 1 addition & 0 deletions src/supervisor
Submodule supervisor added at 99a3e3
207 changes: 207 additions & 0 deletions src/supervisor.patch/0002-Implement-SD_NOTIFY.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
From 3ab7d6f2007188ddce80fa3c1d6196cf888b3051 Mon Sep 17 00:00:00 2001
From: Stepan Blyschak <[email protected]>
Date: Mon, 29 Sep 2025 14:09:22 +0000
Subject: [PATCH] Implement SD_NOTIFY

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you submit (just submit) a PR to https://github.com/Supervisor/supervisor ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Signed-off-by: Stepan Blyschak <[email protected]>
---
supervisor/options.py | 20 ++++++++++++++++++++
supervisor/process.py | 23 +++++++++++++++++++++++
supervisor/supervisord.py | 17 +++++++++++++++++
supervisor/tests/base.py | 5 +++++
4 files changed, 65 insertions(+)

diff --git a/supervisor/options.py b/supervisor/options.py
index f5cb0c9..49415fb 100644
--- a/supervisor/options.py
+++ b/supervisor/options.py
@@ -410,6 +410,8 @@ class ServerOptions(Options):
unlink_pidfile = False
unlink_socketfiles = False
mood = states.SupervisorStates.RUNNING
+ notify_sock_path = None
+ notify_sock = None

def __init__(self):
Options.__init__(self)
@@ -521,6 +523,8 @@ class ServerOptions(Options):

self.server_configs = sconfigs = section.server_configs

+ self.notify_sock_path = section.notify_sock_path
+
# we need to set a fallback serverurl that process.spawn can use

# prefer a unix domain socket
@@ -645,6 +649,9 @@ class ServerOptions(Options):
section.identifier = get('identifier', 'supervisor')
section.nodaemon = boolean(get('nodaemon', 'false'))
section.silent = boolean(get('silent', 'false'))
+ section.notify_sock_path = get('notifysock', None)
+ if section.notify_sock_path is not None:
+ section.notify_sock_path = existing_dirpath(section.notify_sock_path)

tempdir = tempfile.gettempdir()
section.childlogdir = existing_directory(get('childlogdir', tempdir))
@@ -1246,6 +1253,11 @@ class ServerOptions(Options):
# also https://web.archive.org/web/20160729222427/http://www.plope.com/software/collector/253
server.close()

+ def close_notify_socket(self):
+ if self.notify_sock is None:
+ return
+ self.notify_sock.close()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will you self.notify_sock = None after close it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

+
def close_logger(self):
self.logger.close()

@@ -1282,6 +1294,14 @@ class ServerOptions(Options):
except ValueError as why:
self.usage(why.args[0])

+ def open_notify_socket(self):
+ if self.notify_sock_path is None:
+ return
+ self._try_unlink(self.notify_sock_path)
+ self.notify_sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ self.notify_sock.bind(self.notify_sock_path)
+ self.notify_sock.setsockopt(socket.SOL_SOCKET, socket.SO_PASSCRED, 1)
+
def get_autochildlog_name(self, name, identifier, channel):
prefix='%s-%s---%s-' % (name, channel, identifier)
logfile = self.mktempfile(
diff --git a/supervisor/process.py b/supervisor/process.py
index b394be8..bb6a69c 100644
--- a/supervisor/process.py
+++ b/supervisor/process.py
@@ -310,6 +310,9 @@ class Subprocess(object):
# set environment
env = os.environ.copy()
env['SUPERVISOR_ENABLED'] = '1'
+ notify_sock_path = self.config.options.notify_sock_path
+ if notify_sock_path is not None:
+ env['NOTIFY_SOCKET'] = notify_sock_path
serverurl = self.config.serverurl
if serverurl is None: # unset
serverurl = self.config.options.serverurl # might still be None
@@ -717,6 +720,17 @@ class Subprocess(object):
self.pid))
self.kill(signal.SIGKILL)

+ def handle_sd_notify(self, msg):
+ for kv in msg.splitlines():
+ if kv == "READY=1":
+ if self.state == ProcessStates.STARTING:
+ self.delay = 0
+ self.backoff = 0
+ self.change_state(ProcessStates.RUNNING)
+ msg = 'entered RUNNING state, process sent READY=1'
+ self.config.options.logger.info('success: %s %s' % (self.config.name, msg))
+
+
class FastCGISubprocess(Subprocess):
"""Extends Subprocess class to handle FastCGI subprocesses"""

@@ -841,11 +855,20 @@ class ProcessGroupBase(object):
def before_remove(self):
pass

+ def handle_sd_notify(self, _msg, _pid):
+ pass
+
class ProcessGroup(ProcessGroupBase):
def transition(self):
for proc in self.processes.values():
proc.transition()

+ def handle_sd_notify(self, msg, pid):
+ for proc in self.processes.values():
+ if proc.pid == pid:
+ proc.handle_sd_notify(msg)
+ break
+
class FastCGIProcessGroup(ProcessGroup):

def __init__(self, config, **kwargs):
diff --git a/supervisor/supervisord.py b/supervisor/supervisord.py
index 0a4f3e6..968e4d2 100755
--- a/supervisor/supervisord.py
+++ b/supervisor/supervisord.py
@@ -85,6 +85,7 @@ class Supervisor:
for config in self.options.process_group_configs:
self.add_process_group(config)
self.options.openhttpservers(self)
+ self.options.open_notify_socket()
self.options.setsignals()
if (not self.options.nodaemon) and self.options.first:
self.options.daemonize()
@@ -176,6 +177,7 @@ class Supervisor:
timeout = 1 # this cannot be fewer than the smallest TickEvent (5)

socket_map = self.options.get_socket_map()
+ notify_sock = self.options.notify_sock

while 1:
combined_map = {}
@@ -200,6 +202,9 @@ class Supervisor:
# killing everything), it's OK to shutdown or reload
raise asyncore.ExitNow

+ if self.options.notify_sock:
+ self.options.poller.register_readable(notify_sock.fileno())
+
for fd, dispatcher in combined_map.items():
if dispatcher.readable():
self.options.poller.register_readable(fd)
@@ -238,6 +243,17 @@ class Supervisor:
except:
combined_map[fd].handle_error()

+ if notify_sock and notify_sock.fileno() in r:
+ import socket
+ import struct
+ data, ancdata, _, _ = notify_sock.recvmsg(4096, socket.CMSG_SPACE(struct.calcsize("3i")))
+ msg = data.decode("utf-8")
+ for cmsg_level, cmsg_type, cmsg_data in ancdata:
+ if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_CREDENTIALS:
+ pid, _, _ = struct.unpack("3i", cmsg_data)
+ for group in pgroups:
+ group.handle_sd_notify(msg, pid)
+
for group in pgroups:
group.transition()

@@ -358,6 +374,7 @@ def main(args=None, test=False):
else:
go(options)
options.close_httpservers()
+ options.close_notify_socket()
options.close_logger()
first = False
if test or (options.mood < SupervisorStates.RESTARTING):
diff --git a/supervisor/tests/base.py b/supervisor/tests/base.py
index f608b2b..ed795db 100644
--- a/supervisor/tests/base.py
+++ b/supervisor/tests/base.py
@@ -31,6 +31,8 @@ class DummyOptions:
make_pipes_exception = None
remove_exception = None
write_exception = None
+ notify_sock_path = None
+ notify_sock = None

def __init__(self):
self.identifier = 'supervisor'
@@ -117,6 +119,9 @@ class DummyOptions:
def openhttpservers(self, supervisord):
self.httpservers_opened = True

+ def open_notify_socket(self):
+ pass
+
def daemonize(self):
self.daemonized = True

--
2.39.5

1 change: 1 addition & 0 deletions src/supervisor.patch/series
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0002-Implement-SD_NOTIFY.patch
Loading