]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
firstboot: Add --reset option 25836/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 22 Dec 2022 10:05:08 +0000 (11:05 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Sat, 1 Apr 2023 08:50:15 +0000 (10:50 +0200)
This can be used to prepare an image for firstboot by removing all
files that systemd knows about that contain machine specific
information.

man/systemd-firstboot.xml
src/firstboot/firstboot.c
test/units/testsuite-74.firstboot.sh

index cfce8a40ad58bcabe728d5a8750d036332b23a61..42666c96f8852be81b4ae621d03acd0a4c354354 100644 (file)
         <literal>root</literal> user instead of overwriting the entire file.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--reset</option></term>
+
+        <listitem><para>If specified, all existing files that are configured by
+        <command>systemd-firstboot</command> are removed. Note that the files are removed regardless of
+        whether they'll be configured with a new value or not. This operation ensures that the next boot of
+        the image will be considered a first boot, and <command>systemd-firstboot</command> will prompt again
+        to configure each of the removed files.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--delete-root-password</option></term>
 
index 7119f9970e860476b4d5a39185a670dc487d9938..3e68ed1cb029c3e2889ba422fc1e5f4bc6cf53b9 100644 (file)
@@ -72,6 +72,7 @@ static bool arg_force = false;
 static bool arg_delete_root_password = false;
 static bool arg_root_password_is_hashed = false;
 static bool arg_welcome = true;
+static bool arg_reset = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -1089,6 +1090,48 @@ static int process_kernel_cmdline(int rfd) {
         return 0;
 }
 
+static int reset_one(int rfd, const char *path) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_free_ char *f = NULL;
+
+        assert(rfd >= 0);
+        assert(path);
+
+        pfd = chase_and_open_parent_at(rfd, path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_WARN|CHASE_NOFOLLOW, &f);
+        if (pfd == -ENOENT)
+                return 0;
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to resolve %s: %m", path);
+
+        if (unlinkat(pfd, f, 0) < 0)
+                return errno == ENOENT ? 0 : log_error_errno(errno, "Failed to remove %s: %m", path);
+
+        log_info("Removed %s", path);
+        return 0;
+}
+
+static int process_reset(int rfd) {
+        int r;
+
+        assert(rfd >= 0);
+
+        if (!arg_reset)
+                return 0;
+
+        FOREACH_STRING(p,
+                       "/etc/locale.conf",
+                       "/etc/vconsole.conf",
+                       "/etc/hostname",
+                       "/etc/machine-id",
+                       "/etc/kernel/cmdline") {
+                r = reset_one(rfd, p);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -1130,6 +1173,7 @@ static int help(void) {
                "     --force                      Overwrite existing files\n"
                "     --delete-root-password       Delete root password\n"
                "     --welcome=no                 Disable the welcome text\n"
+               "     --reset                      Remove existing files\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                link);
@@ -1171,6 +1215,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FORCE,
                 ARG_DELETE_ROOT_PASSWORD,
                 ARG_WELCOME,
+                ARG_RESET,
         };
 
         static const struct option options[] = {
@@ -1206,6 +1251,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "force",                   no_argument,       NULL, ARG_FORCE                   },
                 { "delete-root-password",    no_argument,       NULL, ARG_DELETE_ROOT_PASSWORD    },
                 { "welcome",                 required_argument, NULL, ARG_WELCOME                 },
+                { "reset",                   no_argument,       NULL, ARG_RESET                   },
                 {}
         };
 
@@ -1408,6 +1454,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_welcome = r;
                         break;
 
+                case ARG_RESET:
+                        arg_reset = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -1497,6 +1547,10 @@ static int run(int argc, char *argv[]) {
                         return r;
         }
 
+        r = process_reset(rfd);
+        if (r < 0)
+                return r;
+
         r = process_locale(rfd);
         if (r < 0)
                 return r;
index 92a607501b7a1f9c9165e66d7f71319f4fd6aeb1..be08575c9e69d49b34bc646ffe96699bc488292e 100755 (executable)
@@ -119,6 +119,15 @@ grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
 grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow"
 grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline"
 
+# Test that --reset removes all files configured by firstboot.
+systemd-firstboot --root="$ROOT" --reset
+[[ ! -e "$ROOT/etc/locale.conf" ]]
+[[ ! -e "$ROOT/etc/vconsole.conf" ]]
+[[ ! -e "$ROOT/etc/localtime" ]]
+[[ ! -e "$ROOT/etc/hostname" ]]
+[[ ! -e "$ROOT/etc/machine-id" ]]
+[[ ! -e "$ROOT/etc/kernel/cmdline" ]]
+
 # --copy-* options
 rm -fr "$ROOT"
 mkdir "$ROOT"