Skip to content

confd: set IITO startup/failure conditions (fixes status LEDs) - #1587

Open
pjator wants to merge 2 commits into
kernelkit:mainfrom
pjator:nanopi-r2s-led
Open

confd: set IITO startup/failure conditions (fixes status LEDs)#1587
pjator wants to merge 2 commits into
kernelkit:mainfrom
pjator:nanopi-r2s-led

Conversation

@pjator

@pjator pjator commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

iitod.json expects /run/finit/cond/run/startup/success before the LAN and status LEDs light up, but nothing ever creates it. Add a small finit service (after confd/ready) that sets the condition on every boot.

Description

Checklist

Tick relevant boxes, this PR is-a or has-a:

  • Bugfix
    • Regression tests
    • ChangeLog updates (for next release)
  • Feature
    • YANG model change => revision updated?
    • Regression tests added?
    • ChangeLog updates (for next release)
    • Documentation added?
  • Test changes
    • Checked in changed Readme.adoc (make test-spec)
    • Added new test to group Readme.adoc and yaml file
  • Code style update (formatting, renaming)
  • Refactoring (please detail in commit messages)
  • Build related changes
  • Documentation content changes
    • ChangeLog updated (for major changes)
  • Other (please describe):

@wkz

wkz commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

When confd became its own daemon (in 77a7915), the conditions used by IITO where also removed.

Currently, a condition is set when startup is applied:

symlink("/run/finit/cond/reconf", "/run/finit/cond/usr/bootstrap");

But there are no corresponding conditions for the failure cases. I suggest we add those and then update the IITO configuration to match.

@wkz wkz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's fix the root cause so that it (1) fixes all boards and (2) also fixes the error cases when startup fails to apply cleanly.

@pjator

pjator commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Fixed per your suggestion. confd now sets run/startup/success after a clean bootstrap and run/failure/success/run/failure/failure in handle_startup_failure() (fail-secure + panic paths). The board-specific workaround is dropped. Verified on hardware: LED lights on boot, eMMC visible.

@pjator pjator changed the title board: nanopi-r2s: set iitod startup condition for LAN/status LEDs confd: set IITO startup/failure conditions (fixes status LEDs) Aug 14, 2026

@wkz wkz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for taking another stab at this! Sorry about the slew of comments 😅 - you picked a pretty core piece of code for your first contribution.

In addition to these changes, I think handle_startup_failure() might need some additional changes. Let's wait and see what @troglobit thinks.

Also, the IITO config needs to be updated to reference the new condition names.

Comment thread src/confd/src/main.c Outdated
char path[128];
char *p;

snprintf(path, sizeof(path), "/run/finit/cond/%s", cond);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
snprintf(path, sizeof(path), "/run/finit/cond/%s", cond);
snprintf(path, sizeof(path), "/run/finit/cond/usr/%s", cond);

I understand the impulse to use the old condition names from IITO's config. However, the run/ namespace is reserved by Finit to signal the status of run jobs under its control. The usr/ namespace is allocated for general use by the system.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agreed.

Comment thread src/confd/src/main.c Outdated
Comment on lines +64 to +71
/* mkdir -p parent directories (e.g. /run/finit/cond/run/startup) */
for (p = path + strlen("/run/finit/cond/"); *p; p++) {
if (*p == '/') {
*p = '\0';
mkdir(path, 0755);
*p = '/';
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
/* mkdir -p parent directories (e.g. /run/finit/cond/run/startup) */
for (p = path + strlen("/run/finit/cond/"); *p; p++) {
if (*p == '/') {
*p = '\0';
mkdir(path, 0755);
*p = '/';
}
}

Finit guarantees that /run/finit/cond/usr exists (via a tmpfiles snippet). So with the suggested change above, together with limiting condition names to a single level, we can safely skip this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agreed.

Comment thread src/confd/src/main.c Outdated
Comment on lines +872 to +876
symlink("/run/finit/cond/reconf", "/run/finit/cond/usr/bootstrap");

/* Signal IITO that startup applied cleanly (status LEDs) */
set_finit_cond("run/startup/success");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
symlink("/run/finit/cond/reconf", "/run/finit/cond/usr/bootstrap");
/* Signal IITO that startup applied cleanly (status LEDs) */
set_finit_cond("run/startup/success");
set_finit_cond("bootstrap");

Let's move the existing condition to use the new helper, but I don't think we can be sure that startup has cleanly applied at this point. I have marked that point in a separate comment.

Comment thread src/confd/src/main.c
if (r != SR_ERR_OK)
WARN("Failed to sync startup datastore: %s", sr_strerror(r));

return 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
set_finit_cond("startup-config-ok");
return 0;

I believe this is the point where we want to signal a successful application of startup-config

Comment thread src/confd/src/main.c
int r;

ERROR("Failed loading startup-config, reverting to Fail Secure mode!");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
set_finit_cond("startup-config-error");

Whatever happens beyond this point, I think we should leave a marker that we could not apply startup-config.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is good, agreed.

Comment thread src/confd/src/main.c
/* Nuclear option: wipe everything */
systemf("rm -f /etc/sysrepo/data/*startup* /etc/sysrepo/data/*running* /dev/shm/sr_*");
set_finit_cond("run/failure/failure");
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@troglobit: Should we not emit the CRITICAL ERROR: ... in this scenario as well? The system is still without any defined config in this case, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agreed. The more feedback we can provide the user with in these cases the better. Possibly those cases could also be consolidated better than what I managed in my refactor.

Comment thread src/confd/src/main.c Outdated
ERROR("Failed loading failure-config, aborting!");
banner_append("CRITICAL ERROR: Logins are disabled, no credentials available");
systemf("initctl -nbq runlevel 9");
set_finit_cond("run/failure/failure");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
set_finit_cond("run/failure/failure");
set_finit_cond("failure-config-error");

Depending on @troglobit's input, this path may have to be merged with the earlier error path. But I think at think point we want to signal "we tried, but failed, to apply failure-config.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yup, agreed. Just a heads up on the grammar there, I've tried to consistently apply the progressive form of the noun in error and status messages, throughout. So something like, CRIT("failed applying failure-config, error %d: %s"); or something.

Comment thread src/confd/src/main.c Outdated
}

banner_append("ERROR: Corrupt startup-config, system has reverted to default login credentials");
set_finit_cond("run/failure/success");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
set_finit_cond("run/failure/success");
set_finit_cond("failure-config-ok");

For symmetry with the other condition names.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agreed.

@pjator

pjator commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

@wkz Thanks for the thorough review — it's really helpful, and I agree with the direction (usr/ namespace, single-level condition names, and updating the IITO config accordingly).

I'll wait for @troglobit's input before pushing an updated version, so we can address everything in one pass and match the structure you'd prefer.

I'll follow up once he's had a chance to weigh in.

@troglobit

Copy link
Copy Markdown
Contributor

Impressive going into the deepest and darkest places, which admittedly needs better lighting. I agree in general with @wkz, only had a minor comment to add.

The product and common iitod.json configurations reference finit
conditions to drive the status LEDs, but nothing ever creates them,
so the LEDs stay dark on every boot.

confd now signals the outcome of the bootstrap sequence via finit
conditions in the usr/ namespace:

  usr/bootstrap               bootstrap entered (Finit guarantees
                              /run/finit/cond/usr exists)
  usr/startup-config-ok       startup-config applied cleanly
  usr/startup-config-error    startup-config failed, fail-secure mode
  usr/failure-config-ok       failure-config (or factory default) loaded
  usr/failure-config-error    failure-config could not be applied

The IITO configuration is updated to reference the new condition
names, and the failure paths now emit a CRITICAL ERROR banner so the
user always gets feedback when the system is left without a defined
configuration.

Co-authored-by: Jarvis (Hermes Agent) <jarvis@woxblom.com>
@pjator

pjator commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

@wkz @troglobit Thanks for the thorough review! @troglobit — the final frontier admittedly doesn't come with streetlights. 🖖

All feedback addressed and force-pushed (a7c95cd):

  • Conditions now live in the usr/ namespace (/run/finit/cond/usr/<name>), single-level names, and the mkdir loop is gone (Finit guarantees the directory exists)
  • confd now signals: usr/bootstrap, usr/startup-config-ok, usr/startup-config-error, usr/failure-config-ok, usr/failure-config-error
  • startup-config-ok is only set after startup-config applied cleanly; startup-config-error marks the fail-secure path
  • Failure paths now also emit a CRITICAL ERROR banner, with messages in progressive form per your convention
  • Both iitod.json files (common + nanopi-r2s) reference the new condition names

Build verified (aarch64), hardware test planned for this evening when I get home from work.

@troglobit troglobit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome, lgtm!

Update: @wkz will do some testing on actual HW to verify.

@pjator

pjator commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Hardware test: IITO conditions verified on physical device

Tested on a NanoPi R2S Plus (32 GB eMMC variant) with the full chain: main + #1586 + #1588 + #1589 + this PR. Built clean from scratch, installed via RAUC (secondary slot), boot status good.

Results:

usr/startup-config-ok is set by confd after a successful startup-config apply — verified in /run/finit/cond/usr/
Status LEDs behave as designed: LAN on (startup-config-ok → green:lan), WAN on when link is up (wan-up), SYS off in steady state
The same conditions are set in test mode (test-config path) — the condition signalling is transport-independent of startup/test config
Reboot survival: device reboots back into the same slot, boot status: good
WebUI serves over HTTPS with a generated self-signed cert; IPv4 and IPv6 networking verified

Caveat: the test unit failed at the end of the session. A power-rail component — identified as the 3.3 V LDO (U13) on the USB-C power path per the FriendlyELEC schematic — released smoke and the board no longer powers up. Notably, the unit was powered by a 100 W USB-PD charger; a PD negotiation to a higher voltage (9/12 V) on a circuit designed for 5 V would exceed the LDO's rating and is the most plausible cause (and my mistake). We found no mechanism by which the software changes could cause a power-rail failure, but we cannot entirely rule out that the code or test procedure played a part. The conditions/LED behaviour above was observed before the unit failed.

All software-side validation (boot, conditions, LEDs, WebUI, networking) is complete and passes. Replacement parts are ordered and I will attempt to repair the device, but I cannot provide Infamy test results since the device died while I was running those tests overnight.

Image of component that is smoking.

image image

@troglobit

Copy link
Copy Markdown
Contributor

Hardware test: IITO conditions verified on physical device

Tested on a NanoPi R2S Plus (32 GB eMMC variant) with the full chain: main + #1586 + #1588 + #1589 + this PR. Built clean from scratch, installed via RAUC (secondary slot), boot status good.

Thanks for verifying!

Caveat: the test unit failed at the end of the session. A power-rail component — identified as the 3.3 V LDO (U13) on the USB-C power path per the FriendlyELEC schematic — released smoke and the board no longer powers up. Notably, the unit was powered by a 100 W USB-PD charger; a PD negotiation to a higher voltage (9/12 V) on a circuit designed for 5 V would exceed the LDO's rating and is the most plausible cause (and my mistake). We found no mechanism by which the software changes could cause a power-rail failure, but we cannot entirely rule out that the code or test procedure played a part. The conditions/LED behaviour above was observed before the unit failed.

Ouch! 😞

@pjator

pjator commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Hardware test NanoPi R2S Plus (eMMC): PASS — no regressions observed

Tested on NanoPi R2S Plus (eMMC) with the full chain: main + #1586 + #1588 + #1589 + this PR (clean build, v26.06.0-53-g0e1fbc38b).

A note on the test unit: as commented above it suffered a hardware failure on the power side during my initial testing (the M.2/WiFi regulator, U13 / SY8089A, blew up). After some soldering-iron magic I removed U13 — it only supplies the M.2 expansion slot, which is not in use — and the board came back to life, which allowed me to complete the Infamy tests below.

Infamy suite (test mode, RESTCONF):

  • meta: reproducible, bootorder, check-version — pass (3/3)
  • system: hostname, add/delete user, admin user, NACM, timezone (name + UTC offset), schedule-reboot — pass (7/7)
  • hardware: watchdog (hard lockup → trip → reboot) — pass (6/6)
  • system: upgrade (RAUC install + reboot + boot order) — pass (post-verify step fails on test-mode/topology interaction)
  • NTP, GPS — skip (topology/hardware not applicable to R2S)

Manual verification (normal operation):

  • factory boot (startup-config cleared → factory config applied, hostname r2s-97-09-58, startup-config regenerated) — pass
  • NTP client synced (chronyd, stratum 3) — pass
  • IPv4/IPv6 networking — pass
  • WebUI + generated cert — pass
  • status LEDs via usr/startup-config-okpass (this PR's change)

Not passed (pre-existing, not caused by this PR):

  • USB tests fail because the device reports a duplicate hardware component soc in ietf-hardware:hardware (temperature sensor double-reported via hwmon + thermal zone). I will follow up with a separate PR for the hardware data issue, I think I understand why but want to look at it a little bit more before I come back separately on that.

On a personal note: the Infamy tests are not the easiest to set up and run on physical hardware for a beginner, and I have not managed to produce a result.json yet. I hope to include that in future test runs once I have learned the ropes a bit better.

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.

3 participants