Skip to content

Commit 340e33e

Browse files
committed
confd: reset to fail-secure if loading startup-config hangs
A plugin callback that never returns while loading startup-config hangs the boot, and the unit never reaches failure-config. Supervise the bootstrap event pump with watchdogd. If it misses its deadline, the supervisor script marks the boot as failed and watchdogd resets the unit, so the next boot goes straight to failure-config. The fail-secure boot itself is not supervised, a hang there would be a reset loop. Issue #1637 Signed-off-by: Joachim Wiberg <[email protected]>
1 parent ac584df commit 340e33e

9 files changed

Lines changed: 58 additions & 7 deletions

File tree

‎board/common/rootfs/etc/watchdogd.conf‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ device /dev/watchdog {
6262
# priority = 98
6363
# script = "/path/to/supervisor-script.sh"
6464
#}
65+
supervisor {
66+
enabled = true
67+
priority = 98
68+
script = "/usr/libexec/infix/supervisor"
69+
}
6570

6671
### Reset reason #######################################################
6772
# The following section controls if/how the reset reason & reset counter
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
# Called by watchdogd when a supervised process misses its deadline:
3+
#
4+
# $0 supervisor CODE PID LABEL
5+
#
6+
# A hung confd bootstrap goes to fail-secure on next boot, issue #1637.
7+
# Exit non-zero so watchdogd saves the reset reason and resets.
8+
9+
if [ "$4" = "confd-bootstrap" ]; then
10+
touch /mnt/aux/startup-config.failed
11+
sync
12+
fi
13+
14+
exit 1

‎doc/ChangeLog.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ All notable changes to the project are documented in this file.
116116
the WebUI software page shows when the other partition has a
117117
different version
118118
- A startup-config that fails to load now resets the unit, and the next
119-
boot goes straight to failure-config from a clean state, issue #1637
119+
boot goes straight to failure-config from a clean state, issue #1637.
120+
The same applies if loading startup-config hangs, the system watchdog
121+
then resets the unit
120122
- Fix #1619: Raspberry Pi kernel panic when configure Wi-Fi
121123
- WebUI: "Save" in the interface editor and "OK" in Add Interface
122124
did nothing for Wi-Fi and WireGuard interfaces. The inline "+ New"

‎doc/boot.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ the next boot starts from a clean state and goes straight to *Fail
190190
Secure Mode*. The mark is cleared on that boot, so a reboot after it
191191
tries `startup-config` again.
192192

193+
The same happens if loading `startup-config` hangs, e.g., a service that
194+
never responds. The system watchdog then resets the unit, by default
195+
after 70 seconds, and the next boot goes straight to *Fail Secure
196+
Mode*.
197+
193198
> [!TIP]
194199
> Please see the [Branding & Releases](branding.md) document for how to
195200
> provide per-product `failure-config`, or `factory-config` to suit your

‎package/confd/Config.in‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ config BR2_PACKAGE_CONFD
66
select BR2_PACKAGE_SYSREPO
77
select BR2_PACKAGE_LIBSRX
88
select BR2_PACKAGE_SUPPORT
9+
select BR2_PACKAGE_WATCHDOGD
910
help
1011
A plugin to sysrepo that provides the core YANG models used to
1112
manage an Infix based system. Configuration can be done using

‎package/confd/confd.mk‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd
1010
CONFD_LICENSE = BSD-3-Clause
1111
CONFD_LICENSE_FILES = LICENSE
1212
CONFD_REDISTRIBUTE = NO
13-
CONFD_DEPENDENCIES = host-sysrepo sysrepo rousette netopeer2 jansson libite sysrepo libsrx libglib2 libev sysklogd
13+
CONFD_DEPENDENCIES = host-sysrepo sysrepo rousette netopeer2 jansson libite sysrepo libsrx libglib2 libev sysklogd watchdogd
1414
CONFD_AUTORECONF = YES
1515
CONFD_CONF_OPTS += --disable-silent-rules --with-crypt=$(BR2_PACKAGE_CONFD_DEFAULT_CRYPT)
1616
CONFD_SYSREPO_SHM_PREFIX = sr_buildroot$(subst /,_,$(CONFIG_DIR))_confd

‎src/confd/configure.ac‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ PKG_CHECK_MODULES([libyang], [libyang >= 4.2.2])
120120
PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0])
121121
PKG_CHECK_MODULES([libsyslog], [libsyslog >= 2.7.0])
122122
PKG_CHECK_MODULES([libcrypto], [libcrypto])
123+
PKG_CHECK_MODULES([libwdog], [libwdog >= 4.0])
123124

124125
AC_CHECK_HEADER([ev.h],
125126
[saved_LIBS="$LIBS"

‎src/confd/src/Makefile.am‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ plugin_LTLIBRARIES = confd-plugin.la
66

77
sbin_PROGRAMS = confd
88

9-
confd_CFLAGS = $(sysrepo_CFLAGS) $(libyang_CFLAGS) $(jansson_CFLAGS) $(libite_CFLAGS) $(libsrx_CFLAGS)
10-
confd_LDADD = $(sysrepo_LIBS) $(libyang_LIBS) $(jansson_LIBS) $(libite_LIBS) $(libsrx_LIBS) $(EV_LIBS) -ldl
9+
confd_CFLAGS = $(sysrepo_CFLAGS) $(libyang_CFLAGS) $(jansson_CFLAGS) $(libite_CFLAGS) $(libsrx_CFLAGS) $(libwdog_CFLAGS)
10+
confd_LDADD = $(sysrepo_LIBS) $(libyang_LIBS) $(jansson_LIBS) $(libite_LIBS) $(libsrx_LIBS) $(libwdog_LIBS) $(EV_LIBS) -ldl
1111
confd_SOURCES = main.c
1212

1313
confd_plugin_la_LDFLAGS = -module -avoid-version -shared

‎src/confd/src/main.c‎

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <srx/common.h>
3636
#include <sysrepo.h>
3737
#include <sysrepo/version.h>
38+
#include <wdog/wdog.h>
3839

3940
/* Maximum number of sysrepo event pipe file descriptors across all plugins */
4041
#define MAX_EVENT_FDS 64
@@ -192,11 +193,15 @@ static void pump_sigterm(int sig)
192193
pump_running = 0;
193194
}
194195

195-
static void event_pump(struct plugin *plugins, int plugin_count)
196+
/* With a deadline, a hung callback resets us to fail-secure, issue #1637 */
197+
static void event_pump(struct plugin *plugins, int plugin_count, uint32_t deadline_ms)
196198
{
197199
sr_subscription_ctx_t *subs[MAX_EVENT_FDS];
198200
struct pollfd fds[MAX_EVENT_FDS];
201+
time_t now, last = 0;
202+
unsigned int ack;
199203
int nfds = 0;
204+
int id = -1;
200205

201206
for (int i = 0; i < plugin_count; i++) {
202207
struct plugin *p = &plugins[i];
@@ -215,14 +220,30 @@ static void event_pump(struct plugin *plugins, int plugin_count)
215220

216221
signal(SIGTERM, pump_sigterm);
217222

223+
if (deadline_ms) {
224+
id = wdog_subscribe("confd-bootstrap", deadline_ms, &ack);
225+
if (id < 0)
226+
WARN("Not supervised by watchdogd: %s", strerror(-id));
227+
}
228+
218229
while (pump_running) {
219230
if (poll(fds, nfds, 100) > 0) {
220231
for (int i = 0; i < nfds; i++)
221232
if (fds[i].revents & POLLIN)
222233
sr_subscription_process_events(subs[i], NULL, NULL);
223234
}
235+
236+
/* Each kick is a round trip to watchdogd, once a second is enough */
237+
now = time(NULL);
238+
if (id >= 0 && now != last) {
239+
last = now;
240+
wdog_kick2(id, &ack);
241+
}
224242
}
225243

244+
if (id >= 0)
245+
wdog_unsubscribe(id, ack);
246+
226247
_exit(0);
227248
}
228249

@@ -719,7 +740,7 @@ int main(int argc, char **argv)
719740
uint32_t timeout_s = 60;
720741
int plugin_count = 0;
721742
int fatal_fail = 0;
722-
uint32_t timeout_ms;
743+
uint32_t timeout_ms, deadline_ms;
723744
int status;
724745

725746
struct option options[] = {
@@ -912,13 +933,15 @@ int main(int argc, char **argv)
912933
/* Phase 9: Fork event pump process for bootstrap.
913934
* With SR_SUBSCR_NO_THREAD, sr_replace_config() blocks waiting
914935
* for callbacks. The pump process processes those events. */
936+
/* Past the sysrepo timeout, not on a fail-secure boot (reset loop) */
937+
deadline_ms = fexist(FAILED_PATH) ? 0 : timeout_ms + 10000;
915938
pump_pid = fork();
916939
if (pump_pid < 0) {
917940
ERRNO("Failed to fork event pump");
918941
goto cleanup;
919942
}
920943
if (pump_pid == 0)
921-
event_pump(plugins, plugin_count);
944+
event_pump(plugins, plugin_count, deadline_ms);
922945

923946
/* Phase 10: Load startup config -- plugins are now subscribed, so
924947
* sr_replace_config() will trigger their change callbacks.

0 commit comments

Comments
 (0)