1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
8 #include "errno-util.h"
11 #include "label-util.h"
13 #include "main-func.h"
14 #include "parse-argument.h"
15 #include "pretty-print.h"
16 #include "string-util.h"
17 #include "time-util.h"
19 static char *arg_root
= NULL
;
21 STATIC_DESTRUCTOR_REGISTER(arg_root
, freep
);
23 static int save_timestamp(const char *dir
, struct timespec
*ts
) {
24 _cleanup_free_
char *message
= NULL
, *dirpath
= NULL
;
25 _cleanup_close_
int fd
= -EBADF
;
29 * We store the timestamp both as mtime of the file and in the file itself,
30 * to support filesystems which cannot store nanosecond-precision timestamps.
33 fd
= chase_and_open(dir
, arg_root
,
34 CHASE_PREFIX_ROOT
| CHASE_WARN
| CHASE_MUST_BE_DIRECTORY
,
35 O_DIRECTORY
| O_CLOEXEC
| O_CREAT
,
38 return log_error_errno(fd
, "Failed to open %s%s: %m", strempty(arg_root
), dir
);
40 if (asprintf(&message
,
41 "# This file was created by systemd-update-done. The timestamp below is the\n"
42 "# modification time of /usr/ for which the most recent updates of %s have\n"
43 "# been applied. See man:systemd-update-done.service(8) for details.\n"
44 "TIMESTAMP_NSEC=" NSEC_FMT
"\n",
46 timespec_load_nsec(ts
)) < 0)
49 r
= write_string_file_full(fd
, ".updated", message
,
50 WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_LABEL
,
52 if (r
== -EROFS
&& !arg_root
)
53 log_debug_errno(r
, "Cannot create \"%s/.updated\", file system is read-only.", dirpath
);
55 return log_error_errno(r
, "Failed to write \"%s/.updated\": %m", dirpath
);
57 log_debug("%s/.updated updated to TIMESTAMP_NSEC="NSEC_FMT
, dirpath
, timespec_load_nsec(ts
));
62 static int help(void) {
63 _cleanup_free_
char *link
= NULL
;
66 r
= terminal_urlify_man("systemd-update-done", "8", &link
);
70 printf("%1$s [OPTIONS...]\n\n"
71 "%5$sMark /etc/ and /var/ as fully updated.%6$s\n"
72 "\n%3$sOptions:%4$s\n"
73 " -h --help Show this help\n"
74 " --root=PATH Operate on root directory PATH\n"
75 "\nSee the %2$s for details.\n",
76 program_invocation_short_name
,
86 static int parse_argv(int argc
, char *argv
[]) {
91 static const struct option options
[] = {
92 { "help", no_argument
, NULL
, 'h' },
93 { "root", required_argument
, NULL
, ARG_ROOT
},
102 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
110 r
= parse_path_argument(optarg
, /* suppress_root= */ true, &arg_root
);
119 assert_not_reached();
123 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "This program takes no arguments.");
129 static int run(int argc
, char *argv
[]) {
133 r
= parse_argv(argc
, argv
);
139 r
= chase_and_stat("/usr", arg_root
,
140 CHASE_PREFIX_ROOT
| CHASE_WARN
| CHASE_MUST_BE_DIRECTORY
,
141 /* ret_path = */ NULL
,
144 return log_error_errno(r
, "Failed to stat %s/usr/: %m", strempty(arg_root
));
151 RET_GATHER(r
, save_timestamp("/etc/", &st
.st_mtim
));
152 RET_GATHER(r
, save_timestamp("/var/", &st
.st_mtim
));
156 DEFINE_MAIN_FUNCTION(run
);