]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
8ea48dfc | 2 | |
20f7f0a8 | 3 | #include <getopt.h> |
ca78ad1d | 4 | #include <sys/stat.h> |
ca78ad1d | 5 | |
5a1d6763 | 6 | #include "alloc-util.h" |
2ff40c75 | 7 | #include "chase.h" |
fda65211 | 8 | #include "errno-util.h" |
2ff40c75 | 9 | #include "fd-util.h" |
8eeb8709 | 10 | #include "fileio.h" |
8857aa74 | 11 | #include "label-util.h" |
93a1f792 | 12 | #include "log.h" |
bca9a6e2 | 13 | #include "main-func.h" |
2ff40c75 | 14 | #include "parse-argument.h" |
20f7f0a8 | 15 | #include "pretty-print.h" |
8857aa74 | 16 | #include "string-util.h" |
ca78ad1d | 17 | #include "time-util.h" |
8ea48dfc | 18 | |
2ff40c75 ZJS |
19 | static char *arg_root = NULL; |
20 | ||
21 | STATIC_DESTRUCTOR_REGISTER(arg_root, freep); | |
22 | ||
04db1a6a | 23 | static int save_timestamp(const char *dir, struct timespec *ts) { |
2ff40c75 ZJS |
24 | _cleanup_free_ char *message = NULL, *dirpath = NULL; |
25 | _cleanup_close_ int fd = -EBADF; | |
fb8b0869 | 26 | int r; |
8ea48dfc | 27 | |
fb8b0869 IS |
28 | /* |
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. | |
fb8b0869 | 31 | */ |
8ea48dfc | 32 | |
2ff40c75 ZJS |
33 | fd = chase_and_open(dir, arg_root, |
34 | CHASE_PREFIX_ROOT | CHASE_WARN | CHASE_MUST_BE_DIRECTORY, | |
ace814f0 | 35 | O_DIRECTORY | O_CLOEXEC | O_CREAT, |
2ff40c75 ZJS |
36 | &dirpath); |
37 | if (fd < 0) | |
38 | return log_error_errno(fd, "Failed to open %s%s: %m", strempty(arg_root), dir); | |
04db1a6a | 39 | |
872c4039 | 40 | if (asprintf(&message, |
04db1a6a ZJS |
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" | |
872c4039 | 44 | "TIMESTAMP_NSEC=" NSEC_FMT "\n", |
04db1a6a | 45 | dir, |
872c4039 ZJS |
46 | timespec_load_nsec(ts)) < 0) |
47 | return log_oom(); | |
8ea48dfc | 48 | |
2ff40c75 ZJS |
49 | r = write_string_file_full(fd, ".updated", message, |
50 | WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL, | |
51 | ts, NULL); | |
52 | if (r == -EROFS && !arg_root) | |
53 | log_debug_errno(r, "Cannot create \"%s/.updated\", file system is read-only.", dirpath); | |
1eee15c3 | 54 | else if (r < 0) |
2ff40c75 ZJS |
55 | return log_error_errno(r, "Failed to write \"%s/.updated\": %m", dirpath); |
56 | else | |
57 | log_debug("%s/.updated updated to TIMESTAMP_NSEC="NSEC_FMT, dirpath, timespec_load_nsec(ts)); | |
58 | ||
8ea48dfc LP |
59 | return 0; |
60 | } | |
61 | ||
20f7f0a8 ZJS |
62 | static int help(void) { |
63 | _cleanup_free_ char *link = NULL; | |
64 | int r; | |
65 | ||
66 | r = terminal_urlify_man("systemd-update-done", "8", &link); | |
67 | if (r < 0) | |
68 | return log_oom(); | |
69 | ||
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" | |
2ff40c75 | 74 | " --root=PATH Operate on root directory PATH\n" |
20f7f0a8 ZJS |
75 | "\nSee the %2$s for details.\n", |
76 | program_invocation_short_name, | |
77 | link, | |
78 | ansi_underline(), | |
79 | ansi_normal(), | |
80 | ansi_highlight(), | |
81 | ansi_normal()); | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | static int parse_argv(int argc, char *argv[]) { | |
2ff40c75 ZJS |
87 | enum { |
88 | ARG_ROOT = 0x100, | |
89 | }; | |
90 | ||
20f7f0a8 ZJS |
91 | static const struct option options[] = { |
92 | { "help", no_argument, NULL, 'h' }, | |
2ff40c75 | 93 | { "root", required_argument, NULL, ARG_ROOT }, |
20f7f0a8 ZJS |
94 | {}, |
95 | }; | |
96 | ||
2ff40c75 | 97 | int r, c; |
20f7f0a8 ZJS |
98 | |
99 | assert(argc >= 0); | |
100 | assert(argv); | |
101 | ||
102 | while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) | |
103 | ||
104 | switch (c) { | |
105 | ||
106 | case 'h': | |
107 | return help(); | |
108 | ||
2ff40c75 ZJS |
109 | case ARG_ROOT: |
110 | r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root); | |
111 | if (r < 0) | |
112 | return r; | |
113 | break; | |
114 | ||
20f7f0a8 ZJS |
115 | case '?': |
116 | return -EINVAL; | |
117 | ||
118 | default: | |
119 | assert_not_reached(); | |
120 | } | |
121 | ||
122 | if (optind < argc) | |
123 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); | |
124 | ||
125 | return 1; | |
126 | } | |
127 | ||
128 | ||
bca9a6e2 | 129 | static int run(int argc, char *argv[]) { |
8ea48dfc | 130 | struct stat st; |
bca9a6e2 | 131 | int r; |
8ea48dfc | 132 | |
20f7f0a8 ZJS |
133 | r = parse_argv(argc, argv); |
134 | if (r <= 0) | |
135 | return r; | |
136 | ||
d2acb93d | 137 | log_setup(); |
8ea48dfc | 138 | |
2ff40c75 ZJS |
139 | r = chase_and_stat("/usr", arg_root, |
140 | CHASE_PREFIX_ROOT | CHASE_WARN | CHASE_MUST_BE_DIRECTORY, | |
141 | /* ret_path = */ NULL, | |
142 | &st); | |
143 | if (r < 0) | |
144 | return log_error_errno(r, "Failed to stat %s/usr/: %m", strempty(arg_root)); | |
8ea48dfc | 145 | |
a452c807 | 146 | r = mac_init(); |
a9ba0e32 | 147 | if (r < 0) |
bca9a6e2 | 148 | return r; |
8ea48dfc | 149 | |
bca9a6e2 ZJS |
150 | r = 0; |
151 | RET_GATHER(r, save_timestamp("/etc/", &st.st_mtim)); | |
152 | RET_GATHER(r, save_timestamp("/var/", &st.st_mtim)); | |
153 | return r; | |
8ea48dfc | 154 | } |
bca9a6e2 ZJS |
155 | |
156 | DEFINE_MAIN_FUNCTION(run); |