git: b9be7608cd13 - main - nuageinit: implement bootcmd support

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Fri, 05 Jun 2026 06:40:29 UTC
The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=b9be7608cd13888a32815bfb2263e20855706969

commit b9be7608cd13888a32815bfb2263e20855706969
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2026-06-05 05:00:00 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2026-06-05 05:00:00 +0000

    nuageinit: implement bootcmd support
    
    Add support for the 'bootcmd' cloud-config directive, which allows
    running commands very early in the boot process, before the hostname
    is set and before the network is configured.
    
    - nuageinit: bootcmd() function follows the same pattern as runcmd(),
      writing commands to /var/cache/nuageinit/bootcmds instead of runcmds.
      It is the first entry in the pre_network_calls table.
    
    - rc.d/nuageinit: execute /var/cache/nuageinit/bootcmds immediately
      after /usr/libexec/nuageinit completes, before unmounting the config
      drive. This ensures bootcmd runs before NETWORKING per cloud-init spec.
---
 libexec/nuageinit/nuageinit   | 18 ++++++++++++++++++
 libexec/nuageinit/nuageinit.7 |  5 +++++
 libexec/rc/rc.d/nuageinit     |  4 ++++
 3 files changed, 27 insertions(+)

diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit
index 166c3503735a..bfe5f893756e 100755
--- a/libexec/nuageinit/nuageinit
+++ b/libexec/nuageinit/nuageinit
@@ -524,6 +524,23 @@ local function disable_root(obj)
 	end
 end
 
+local function bootcmd(obj)
+	if obj.bootcmd == nil then return end
+	local f = nil
+	for _, c in ipairs(obj.bootcmd) do
+		if f == nil then
+			nuage.mkdir_p(root .. "/var/cache/nuageinit")
+			f = assert(io.open(root .. "/var/cache/nuageinit/bootcmds", "w"))
+			f:write("#!/bin/sh\n")
+		end
+		f:write(c .. "\n")
+	end
+	if f ~= nil then
+		f:close()
+		nuage.chmod(root .. "/var/cache/nuageinit/bootcmds", "0755")
+	end
+end
+
 local function runcmd(obj)
 	if obj.runcmd == nil then return end
 	local f = nil
@@ -794,6 +811,7 @@ if line == nil then
 --  YAML user-data
 elseif line == "#cloud-config" then
 	local pre_network_calls = {
+		bootcmd,
 		sethostname,
 		settimezone,
 		groups,
diff --git a/libexec/nuageinit/nuageinit.7 b/libexec/nuageinit/nuageinit.7
index 08a64b11ff58..3cc0eceeb2ae 100644
--- a/libexec/nuageinit/nuageinit.7
+++ b/libexec/nuageinit/nuageinit.7
@@ -362,6 +362,9 @@ does not automatically install them.
 Ensure the relevant command is listed in your
 .Nm packages
 section.
+.It Ic bootcmd
+An array of commands to be run early in the boot process,
+before the hostname is set and before the network is configured.
 .It Ic chpasswd
 Change the passwords for users, it accepts the following keys:
 .Bl -tag -width "expire"
@@ -424,6 +427,8 @@ Here is an example of a YAML configuration for
 .Nm :
 .Bd -literal
 #cloud-config
+bootcmd:
+  - kldload if_bridge
 fqdn: myhost.mynetwork.tld
 users:
   - default
diff --git a/libexec/rc/rc.d/nuageinit b/libexec/rc/rc.d/nuageinit
index 259ce3c138e5..e2f0a85bc3bc 100755
--- a/libexec/rc/rc.d/nuageinit
+++ b/libexec/rc/rc.d/nuageinit
@@ -89,6 +89,10 @@ nuageinit_start()
 		/usr/libexec/nuageinit /media/nuageinit $citype 2>&1 | tee -a /var/log/nuageinit.log
 		;;
 	esac
+	if [ -x /var/cache/nuageinit/bootcmds ]; then
+		echo "Executing 'bootcmd'" | tee -a /var/log/nuageinit.log
+		/var/cache/nuageinit/bootcmds 2>&1 | tee -a /var/log/nuageinit.log
+	fi
 	if [ -n "$drive" ]; then
 		umount /media/nuageinit
 		rmdir /media/nuageinit