]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include <getopt.h> | |
4 | #include <sys/stat.h> | |
5 | ||
6 | #include "alloc-util.h" | |
7 | #include "chase.h" | |
8 | #include "errno-util.h" | |
9 | #include "fd-util.h" | |
10 | #include "fileio.h" | |
11 | #include "label-util.h" | |
12 | #include "log.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" | |
18 | ||
19 | static char *arg_root = NULL; | |
20 | ||
21 | STATIC_DESTRUCTOR_REGISTER(arg_root, freep); | |
22 | ||
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; | |
26 | int r; | |
27 | ||
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. | |
31 | */ | |
32 | ||
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, | |
36 | &dirpath); | |
37 | if (fd < 0) | |
38 | return log_error_errno(fd, "Failed to open %s%s: %m", strempty(arg_root), dir); | |
39 | ||
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", | |
45 | dir, | |
46 | timespec_load_nsec(ts)) < 0) | |
47 | return log_oom(); | |
48 | ||
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); | |
54 | else if (r < 0) | |
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 | ||
59 | return 0; | |
60 | } | |
61 | ||
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" | |
74 | " --root=PATH Operate on root directory PATH\n" | |
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[]) { | |
87 | enum { | |
88 | ARG_ROOT = 0x100, | |
89 | }; | |
90 | ||
91 | static const struct option options[] = { | |
92 | { "help", no_argument, NULL, 'h' }, | |
93 | { "root", required_argument, NULL, ARG_ROOT }, | |
94 | {}, | |
95 | }; | |
96 | ||
97 | int r, c; | |
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 | ||
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 | ||
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 | ||
129 | static int run(int argc, char *argv[]) { | |
130 | struct stat st; | |
131 | int r; | |
132 | ||
133 | r = parse_argv(argc, argv); | |
134 | if (r <= 0) | |
135 | return r; | |
136 | ||
137 | log_setup(); | |
138 | ||
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)); | |
145 | ||
146 | r = mac_init(); | |
147 | if (r < 0) | |
148 | return r; | |
149 | ||
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; | |
154 | } | |
155 | ||
156 | DEFINE_MAIN_FUNCTION(run); |