]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/update-done/update-done.c
3e5dfd29eee1397ac8a8e59eb93a51203a5b9c86
[thirdparty/systemd.git] / src / update-done / update-done.c
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);