]>
| 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); |