update-done: add --root= arg

The idea is to use this when building an image to mark the image as not
needing updates after the reboot. In general it is impossible to say if
any of the early boot update services can be safely skipped, except when
the creator of the image knows all the contents there and has made sure
that all the updates have been processed. (This is in fact what happens
in a typical package-based installation: the packages have scriptlets which
implement the changes during or after the installation process.)

With this patch, the image build process can do 'systemd-update-done --root=…'
at the appropriate point to avoid triggering of ldconfig.service,
systemd-hwdb-update.service, etc.

I didn't write --image=, because it doesn't seem immediately useful. The
approach with --root is most useful when we're building the image "offline",
which means that we have a directory we're working on.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2025-03-19 17:05:20 +01:00
parent 20f7f0a891
commit 2ff40c758e
2 changed files with 52 additions and 11 deletions

View File

@@ -69,6 +69,15 @@
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>--root=<replaceable>root</replaceable></option></term>
<listitem><para>Takes a directory path as an argument. The program
will operate on paths below the specified root directory.
</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect1>

View File

@@ -6,15 +6,23 @@
#include <unistd.h>
#include "alloc-util.h"
#include "chase.h"
#include "fd-util.h"
#include "fileio.h"
#include "main-func.h"
#include "parse-argument.h"
#include "path-util.h"
#include "pretty-print.h"
#include "selinux-util.h"
#include "time-util.h"
static char *arg_root = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
static int save_timestamp(const char *dir, struct timespec *ts) {
_cleanup_free_ char *message = NULL, *path = NULL;
_cleanup_free_ char *message = NULL, *dirpath = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
/*
@@ -22,9 +30,12 @@ static int save_timestamp(const char *dir, struct timespec *ts) {
* to support filesystems which cannot store nanosecond-precision timestamps.
*/
path = path_join(dir, ".updated");
if (!path)
return log_oom();
fd = chase_and_open(dir, arg_root,
CHASE_PREFIX_ROOT | CHASE_WARN | CHASE_MUST_BE_DIRECTORY,
O_DIRECTORY | O_CLOEXEC,
&dirpath);
if (fd < 0)
return log_error_errno(fd, "Failed to open %s%s: %m", strempty(arg_root), dir);
if (asprintf(&message,
"# This file was created by systemd-update-done. The timestamp below is the\n"
@@ -35,11 +46,16 @@ static int save_timestamp(const char *dir, struct timespec *ts) {
timespec_load_nsec(ts)) < 0)
return log_oom();
r = write_string_file_full(AT_FDCWD, path, message, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL, ts, NULL);
if (r == -EROFS)
log_debug_errno(r, "Cannot create \"%s\", file system is read-only.", path);
r = write_string_file_full(fd, ".updated", message,
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL,
ts, NULL);
if (r == -EROFS && !arg_root)
log_debug_errno(r, "Cannot create \"%s/.updated\", file system is read-only.", dirpath);
else if (r < 0)
return log_error_errno(r, "Failed to write \"%s\": %m", path);
return log_error_errno(r, "Failed to write \"%s/.updated\": %m", dirpath);
else
log_debug("%s/.updated updated to TIMESTAMP_NSEC="NSEC_FMT, dirpath, timespec_load_nsec(ts));
return 0;
}
@@ -55,6 +71,7 @@ static int help(void) {
"%5$sMark /etc/ and /var/ as fully updated.%6$s\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --root=PATH Operate on root directory PATH\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -67,12 +84,17 @@ static int help(void) {
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_ROOT = 0x100,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "root", required_argument, NULL, ARG_ROOT },
{},
};
int c;
int r, c;
assert(argc >= 0);
assert(argv);
@@ -84,6 +106,12 @@ static int parse_argv(int argc, char *argv[]) {
case 'h':
return help();
case ARG_ROOT:
r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@@ -108,8 +136,12 @@ static int run(int argc, char *argv[]) {
log_setup();
if (stat("/usr", &st) < 0)
return log_error_errno(errno, "Failed to stat /usr: %m");
r = chase_and_stat("/usr", arg_root,
CHASE_PREFIX_ROOT | CHASE_WARN | CHASE_MUST_BE_DIRECTORY,
/* ret_path = */ NULL,
&st);
if (r < 0)
return log_error_errno(r, "Failed to stat %s/usr/: %m", strempty(arg_root));
r = mac_init();
if (r < 0)