| File: | build-scan/../src/basic/string-util.h |
| Warning: | line 63, column 13 Null pointer passed to 1st parameter expecting 'nonnull' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <blkid.h> | |||
| 4 | #include <ctype.h> | |||
| 5 | #include <dirent.h> | |||
| 6 | #include <errno(*__errno_location ()).h> | |||
| 7 | #include <ftw.h> | |||
| 8 | #include <getopt.h> | |||
| 9 | #include <limits.h> | |||
| 10 | #include <linux1/magic.h> | |||
| 11 | #include <stdbool.h> | |||
| 12 | #include <stdio.h> | |||
| 13 | #include <stdlib.h> | |||
| 14 | #include <string.h> | |||
| 15 | #include <sys/mman.h> | |||
| 16 | #include <sys/stat.h> | |||
| 17 | #include <sys/statfs.h> | |||
| 18 | #include <unistd.h> | |||
| 19 | ||||
| 20 | #include "sd-id128.h" | |||
| 21 | ||||
| 22 | #include "alloc-util.h" | |||
| 23 | #include "blkid-util.h" | |||
| 24 | #include "bootspec.h" | |||
| 25 | #include "copy.h" | |||
| 26 | #include "dirent-util.h" | |||
| 27 | #include "efivars.h" | |||
| 28 | #include "fd-util.h" | |||
| 29 | #include "fileio.h" | |||
| 30 | #include "fs-util.h" | |||
| 31 | #include "locale-util.h" | |||
| 32 | #include "parse-util.h" | |||
| 33 | #include "rm-rf.h" | |||
| 34 | #include "stat-util.h" | |||
| 35 | #include "string-util.h" | |||
| 36 | #include "strv.h" | |||
| 37 | #include "terminal-util.h" | |||
| 38 | #include "umask-util.h" | |||
| 39 | #include "util.h" | |||
| 40 | #include "verbs.h" | |||
| 41 | #include "virt.h" | |||
| 42 | ||||
| 43 | static char *arg_path = NULL((void*)0); | |||
| 44 | static bool_Bool arg_print_path = false0; | |||
| 45 | static bool_Bool arg_touch_variables = true1; | |||
| 46 | ||||
| 47 | static int acquire_esp( | |||
| 48 | bool_Bool unprivileged_mode, | |||
| 49 | uint32_t *ret_part, | |||
| 50 | uint64_t *ret_pstart, | |||
| 51 | uint64_t *ret_psize, | |||
| 52 | sd_id128_t *ret_uuid) { | |||
| 53 | ||||
| 54 | char *np; | |||
| 55 | int r; | |||
| 56 | ||||
| 57 | /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on its own, | |||
| 58 | * except for ENOKEY (which is good, we want to show our own message in that case, suggesting use of --path=) | |||
| 59 | * and EACCESS (only when we request unprivileged mode; in this case we simply eat up the error here, so that | |||
| 60 | * --list and --status work too, without noise about this). */ | |||
| 61 | ||||
| 62 | r = find_esp_and_warn(arg_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid); | |||
| 63 | if (r == -ENOKEY126) | |||
| 64 | return log_error_errno(r,({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 66, __func__, "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n" "Alternatively, use --path= to specify path to mount point." ) : -abs(_e); }) | |||
| 65 | "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 66, __func__, "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n" "Alternatively, use --path= to specify path to mount point." ) : -abs(_e); }) | |||
| 66 | "Alternatively, use --path= to specify path to mount point.")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 66, __func__, "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n" "Alternatively, use --path= to specify path to mount point." ) : -abs(_e); }); | |||
| 67 | if (r < 0) | |||
| 68 | return r; | |||
| 69 | ||||
| 70 | free_and_replace(arg_path, np)({ free(arg_path); (arg_path) = (np); (np) = ((void*)0); 0; } ); | |||
| 71 | ||||
| 72 | log_debug("Using EFI System Partition at %s.", arg_path)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 72, __func__, "Using EFI System Partition at %s." , arg_path) : -abs(_e); }); | |||
| 73 | ||||
| 74 | return 0; | |||
| 75 | } | |||
| 76 | ||||
| 77 | /* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */ | |||
| 78 | static int get_file_version(int fd, char **v) { | |||
| 79 | struct stat st; | |||
| 80 | char *buf; | |||
| 81 | const char *s, *e; | |||
| 82 | char *x = NULL((void*)0); | |||
| 83 | int r = 0; | |||
| 84 | ||||
| 85 | assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/boot/bootctl.c", 85 , __PRETTY_FUNCTION__); } while (0); | |||
| 86 | assert(v)do { if ((__builtin_expect(!!(!(v)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("v"), "../src/boot/bootctl.c", 86, __PRETTY_FUNCTION__ ); } while (0); | |||
| 87 | ||||
| 88 | if (fstat(fd, &st) < 0) | |||
| 89 | return log_error_errno(errno, "Failed to stat EFI binary: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 89, __func__, "Failed to stat EFI binary: %m" ) : -abs(_e); }); | |||
| 90 | ||||
| 91 | if (st.st_size < 27) { | |||
| 92 | *v = NULL((void*)0); | |||
| 93 | return 0; | |||
| 94 | } | |||
| 95 | ||||
| 96 | buf = mmap(NULL((void*)0), st.st_size, PROT_READ0x1, MAP_PRIVATE0x02, fd, 0); | |||
| 97 | if (buf == MAP_FAILED((void *) -1)) | |||
| 98 | return log_error_errno(errno, "Failed to memory map EFI binary: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 98, __func__, "Failed to memory map EFI binary: %m" ) : -abs(_e); }); | |||
| 99 | ||||
| 100 | s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17); | |||
| 101 | if (!s) | |||
| 102 | goto finish; | |||
| 103 | s += 17; | |||
| 104 | ||||
| 105 | e = memmem(s, st.st_size - (s - buf), " ####", 5); | |||
| 106 | if (!e || e - s < 3) { | |||
| 107 | log_error("Malformed version string.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 107, __func__, "Malformed version string." ) : -abs(_e); }); | |||
| 108 | r = -EINVAL22; | |||
| 109 | goto finish; | |||
| 110 | } | |||
| 111 | ||||
| 112 | x = strndup(s, e - s); | |||
| 113 | if (!x) { | |||
| 114 | r = log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 114 , __func__); | |||
| 115 | goto finish; | |||
| 116 | } | |||
| 117 | r = 1; | |||
| 118 | ||||
| 119 | finish: | |||
| 120 | (void) munmap(buf, st.st_size); | |||
| 121 | *v = x; | |||
| 122 | return r; | |||
| 123 | } | |||
| 124 | ||||
| 125 | static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) { | |||
| 126 | char *p; | |||
| 127 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
| 128 | struct dirent *de; | |||
| 129 | int r = 0, c = 0; | |||
| 130 | ||||
| 131 | p = strjoina(esp_path, "/", path)({ const char *_appendees_[] = { esp_path, "/", path }; char * _d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 132 | d = opendir(p); | |||
| 133 | if (!d) { | |||
| 134 | if (errno(*__errno_location ()) == ENOENT2) | |||
| 135 | return 0; | |||
| 136 | ||||
| 137 | return log_error_errno(errno, "Failed to read \"%s\": %m", p)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 137, __func__, "Failed to read \"%s\": %m" , p) : -abs(_e); }); | |||
| 138 | } | |||
| 139 | ||||
| 140 | FOREACH_DIRENT(de, d, break)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { break; } break; } else if (hidden_or_backup_file ((de)->d_name)) continue; else { | |||
| 141 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
| 142 | _cleanup_free___attribute__((cleanup(freep))) char *v = NULL((void*)0); | |||
| 143 | ||||
| 144 | if (!endswith_no_case(de->d_name, ".efi")) | |||
| 145 | continue; | |||
| 146 | ||||
| 147 | if (prefix && !startswith_no_case(de->d_name, prefix)) | |||
| 148 | continue; | |||
| 149 | ||||
| 150 | fd = openat(dirfd(d), de->d_name, O_RDONLY00|O_CLOEXEC02000000); | |||
| 151 | if (fd < 0) | |||
| 152 | return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 152, __func__, "Failed to open \"%s/%s\" for reading: %m" , p, de->d_name) : -abs(_e); }); | |||
| 153 | ||||
| 154 | r = get_file_version(fd, &v); | |||
| 155 | if (r < 0) | |||
| 156 | return r; | |||
| 157 | if (r > 0) | |||
| 158 | printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v); | |||
| 159 | else | |||
| 160 | printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name); | |||
| 161 | c++; | |||
| 162 | } | |||
| 163 | ||||
| 164 | return c; | |||
| 165 | } | |||
| 166 | ||||
| 167 | static int status_binaries(const char *esp_path, sd_id128_t partition) { | |||
| 168 | int r; | |||
| 169 | ||||
| 170 | printf("Boot Loader Binaries:\n"); | |||
| 171 | ||||
| 172 | if (!esp_path) { | |||
| 173 | printf(" ESP: Cannot find or access mount point of ESP.\n\n"); | |||
| 174 | return -ENOENT2; | |||
| 175 | } | |||
| 176 | ||||
| 177 | printf(" ESP: %s", esp_path); | |||
| 178 | if (!sd_id128_is_null(partition)) | |||
| 179 | printf(" (/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x)", SD_ID128_FORMAT_VAL(partition)(partition).bytes[0], (partition).bytes[1], (partition).bytes [2], (partition).bytes[3], (partition).bytes[4], (partition). bytes[5], (partition).bytes[6], (partition).bytes[7], (partition ).bytes[8], (partition).bytes[9], (partition).bytes[10], (partition ).bytes[11], (partition).bytes[12], (partition).bytes[13], (partition ).bytes[14], (partition).bytes[15]); | |||
| 180 | printf("\n"); | |||
| 181 | ||||
| 182 | r = enumerate_binaries(esp_path, "EFI/systemd", NULL((void*)0)); | |||
| 183 | if (r == 0) | |||
| 184 | log_error("systemd-boot not installed in ESP.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 184, __func__, "systemd-boot not installed in ESP." ) : -abs(_e); }); | |||
| 185 | else if (r < 0) | |||
| 186 | return r; | |||
| 187 | ||||
| 188 | r = enumerate_binaries(esp_path, "EFI/BOOT", "boot"); | |||
| 189 | if (r == 0) | |||
| 190 | log_error("No default/fallback boot loader installed in ESP.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 190, __func__, "No default/fallback boot loader installed in ESP." ) : -abs(_e); }); | |||
| 191 | else if (r < 0) | |||
| 192 | return r; | |||
| 193 | ||||
| 194 | printf("\n"); | |||
| 195 | ||||
| 196 | return 0; | |||
| 197 | } | |||
| 198 | ||||
| 199 | static int print_efi_option(uint16_t id, bool_Bool in_order) { | |||
| 200 | _cleanup_free___attribute__((cleanup(freep))) char *title = NULL((void*)0); | |||
| 201 | _cleanup_free___attribute__((cleanup(freep))) char *path = NULL((void*)0); | |||
| 202 | sd_id128_t partition; | |||
| 203 | bool_Bool active; | |||
| 204 | int r = 0; | |||
| 205 | ||||
| 206 | r = efi_get_boot_option(id, &title, &partition, &path, &active); | |||
| 207 | if (r < 0) | |||
| 208 | return r; | |||
| 209 | ||||
| 210 | /* print only configured entries with partition information */ | |||
| 211 | if (!path || sd_id128_is_null(partition)) | |||
| 212 | return 0; | |||
| 213 | ||||
| 214 | efi_tilt_backslashes(path); | |||
| 215 | ||||
| 216 | printf(" Title: %s\n", strna(title)); | |||
| 217 | printf(" ID: 0x%04X\n", id); | |||
| 218 | printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : ""); | |||
| 219 | printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)(partition).bytes[0], (partition).bytes[1], (partition).bytes [2], (partition).bytes[3], (partition).bytes[4], (partition). bytes[5], (partition).bytes[6], (partition).bytes[7], (partition ).bytes[8], (partition).bytes[9], (partition).bytes[10], (partition ).bytes[11], (partition).bytes[12], (partition).bytes[13], (partition ).bytes[14], (partition).bytes[15]); | |||
| 220 | printf(" File: %s%s\n", special_glyph(TREE_RIGHT), path); | |||
| 221 | printf("\n"); | |||
| 222 | ||||
| 223 | return 0; | |||
| 224 | } | |||
| 225 | ||||
| 226 | static int status_variables(void) { | |||
| 227 | int n_options, n_order; | |||
| 228 | _cleanup_free___attribute__((cleanup(freep))) uint16_t *options = NULL((void*)0), *order = NULL((void*)0); | |||
| 229 | int i; | |||
| 230 | ||||
| 231 | n_options = efi_get_boot_options(&options); | |||
| 232 | if (n_options == -ENOENT2) | |||
| 233 | return log_error_errno(n_options,({ int _level = ((3)), _e = ((n_options)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 235, __func__, "Failed to access EFI variables, efivarfs" " needs to be available at /sys/firmware/efi/efivars/.") : - abs(_e); }) | |||
| 234 | "Failed to access EFI variables, efivarfs"({ int _level = ((3)), _e = ((n_options)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 235, __func__, "Failed to access EFI variables, efivarfs" " needs to be available at /sys/firmware/efi/efivars/.") : - abs(_e); }) | |||
| 235 | " needs to be available at /sys/firmware/efi/efivars/.")({ int _level = ((3)), _e = ((n_options)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 235, __func__, "Failed to access EFI variables, efivarfs" " needs to be available at /sys/firmware/efi/efivars/.") : - abs(_e); }); | |||
| 236 | if (n_options < 0) | |||
| 237 | return log_error_errno(n_options, "Failed to read EFI boot entries: %m")({ int _level = ((3)), _e = ((n_options)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 237, __func__, "Failed to read EFI boot entries: %m" ) : -abs(_e); }); | |||
| 238 | ||||
| 239 | n_order = efi_get_boot_order(&order); | |||
| 240 | if (n_order == -ENOENT2) | |||
| 241 | n_order = 0; | |||
| 242 | else if (n_order < 0) | |||
| 243 | return log_error_errno(n_order, "Failed to read EFI boot order.")({ int _level = ((3)), _e = ((n_order)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 243, __func__, "Failed to read EFI boot order." ) : -abs(_e); }); | |||
| 244 | ||||
| 245 | /* print entries in BootOrder first */ | |||
| 246 | printf("Boot Loader Entries in EFI Variables:\n"); | |||
| 247 | for (i = 0; i < n_order; i++) | |||
| 248 | print_efi_option(order[i], true1); | |||
| 249 | ||||
| 250 | /* print remaining entries */ | |||
| 251 | for (i = 0; i < n_options; i++) { | |||
| 252 | int j; | |||
| 253 | ||||
| 254 | for (j = 0; j < n_order; j++) | |||
| 255 | if (options[i] == order[j]) | |||
| 256 | goto next_option; | |||
| 257 | ||||
| 258 | print_efi_option(options[i], false0); | |||
| 259 | ||||
| 260 | next_option: | |||
| 261 | continue; | |||
| 262 | } | |||
| 263 | ||||
| 264 | return 0; | |||
| 265 | } | |||
| 266 | ||||
| 267 | static int status_entries(const char *esp_path, sd_id128_t partition) { | |||
| 268 | int r; | |||
| 269 | ||||
| 270 | _cleanup_(boot_config_free)__attribute__((cleanup(boot_config_free))) BootConfig config = {}; | |||
| 271 | ||||
| 272 | printf("Default Boot Entry:\n"); | |||
| 273 | ||||
| 274 | r = boot_entries_load_config(esp_path, &config); | |||
| 275 | if (r < 0) | |||
| 276 | return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 277, __func__, "Failed to load bootspec config from \"%s/loader\": %m" , esp_path) : -abs(_e); }) | |||
| 277 | esp_path)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 277, __func__, "Failed to load bootspec config from \"%s/loader\": %m" , esp_path) : -abs(_e); }); | |||
| 278 | ||||
| 279 | if (config.default_entry < 0) | |||
| 280 | printf("%zu entries, no entry suitable as default\n", config.n_entries); | |||
| 281 | else { | |||
| 282 | const BootEntry *e = &config.entries[config.default_entry]; | |||
| 283 | ||||
| 284 | printf(" title: %s\n", boot_entry_title(e)); | |||
| 285 | if (e->version) | |||
| 286 | printf(" version: %s\n", e->version); | |||
| 287 | if (e->kernel) | |||
| 288 | printf(" linux: %s\n", e->kernel); | |||
| 289 | if (!strv_isempty(e->initrd)) { | |||
| 290 | _cleanup_free___attribute__((cleanup(freep))) char *t; | |||
| 291 | ||||
| 292 | t = strv_join(e->initrd, " "); | |||
| 293 | if (!t) | |||
| 294 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 294 , __func__); | |||
| 295 | ||||
| 296 | printf(" initrd: %s\n", t); | |||
| 297 | } | |||
| 298 | if (!strv_isempty(e->options)) { | |||
| 299 | _cleanup_free___attribute__((cleanup(freep))) char *t; | |||
| 300 | ||||
| 301 | t = strv_join(e->options, " "); | |||
| 302 | if (!t) | |||
| 303 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 303 , __func__); | |||
| 304 | ||||
| 305 | printf(" options: %s\n", t); | |||
| 306 | } | |||
| 307 | if (e->device_tree) | |||
| 308 | printf(" devicetree: %s\n", e->device_tree); | |||
| 309 | puts(""); | |||
| 310 | } | |||
| 311 | ||||
| 312 | return 0; | |||
| 313 | } | |||
| 314 | ||||
| 315 | static int compare_product(const char *a, const char *b) { | |||
| 316 | size_t x, y; | |||
| 317 | ||||
| 318 | assert(a)do { if ((__builtin_expect(!!(!(a)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("a"), "../src/boot/bootctl.c", 318, __PRETTY_FUNCTION__ ); } while (0); | |||
| 319 | assert(b)do { if ((__builtin_expect(!!(!(b)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("b"), "../src/boot/bootctl.c", 319, __PRETTY_FUNCTION__ ); } while (0); | |||
| 320 | ||||
| 321 | x = strcspn(a, " "); | |||
| 322 | y = strcspn(b, " "); | |||
| 323 | if (x != y) | |||
| 324 | return x < y ? -1 : x > y ? 1 : 0; | |||
| 325 | ||||
| 326 | return strncmp(a, b, x); | |||
| 327 | } | |||
| 328 | ||||
| 329 | static int compare_version(const char *a, const char *b) { | |||
| 330 | assert(a)do { if ((__builtin_expect(!!(!(a)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("a"), "../src/boot/bootctl.c", 330, __PRETTY_FUNCTION__ ); } while (0); | |||
| 331 | assert(b)do { if ((__builtin_expect(!!(!(b)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("b"), "../src/boot/bootctl.c", 331, __PRETTY_FUNCTION__ ); } while (0); | |||
| 332 | ||||
| 333 | a += strcspn(a, " "); | |||
| 334 | a += strspn(a, " "); | |||
| 335 | b += strcspn(b, " "); | |||
| 336 | b += strspn(b, " "); | |||
| 337 | ||||
| 338 | return strverscmp(a, b); | |||
| 339 | } | |||
| 340 | ||||
| 341 | static int version_check(int fd_from, const char *from, int fd_to, const char *to) { | |||
| 342 | _cleanup_free___attribute__((cleanup(freep))) char *a = NULL((void*)0), *b = NULL((void*)0); | |||
| 343 | int r; | |||
| 344 | ||||
| 345 | assert(fd_from >= 0)do { if ((__builtin_expect(!!(!(fd_from >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd_from >= 0"), "../src/boot/bootctl.c" , 345, __PRETTY_FUNCTION__); } while (0); | |||
| 346 | assert(from)do { if ((__builtin_expect(!!(!(from)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("from"), "../src/boot/bootctl.c", 346, __PRETTY_FUNCTION__ ); } while (0); | |||
| 347 | assert(fd_to >= 0)do { if ((__builtin_expect(!!(!(fd_to >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd_to >= 0"), "../src/boot/bootctl.c" , 347, __PRETTY_FUNCTION__); } while (0); | |||
| 348 | assert(to)do { if ((__builtin_expect(!!(!(to)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("to"), "../src/boot/bootctl.c", 348, __PRETTY_FUNCTION__ ); } while (0); | |||
| 349 | ||||
| 350 | r = get_file_version(fd_from, &a); | |||
| 351 | if (r < 0) | |||
| 352 | return r; | |||
| 353 | if (r == 0) { | |||
| 354 | log_error("Source file \"%s\" does not carry version information!", from)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 354, __func__, "Source file \"%s\" does not carry version information!" , from) : -abs(_e); }); | |||
| 355 | return -EINVAL22; | |||
| 356 | } | |||
| 357 | ||||
| 358 | r = get_file_version(fd_to, &b); | |||
| 359 | if (r < 0) | |||
| 360 | return r; | |||
| 361 | if (r == 0 || compare_product(a, b) != 0) { | |||
| 362 | log_notice("Skipping \"%s\", since it's owned by another boot loader.", to)({ int _level = (((5))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 362, __func__, "Skipping \"%s\", since it's owned by another boot loader." , to) : -abs(_e); }); | |||
| 363 | return -EEXIST17; | |||
| 364 | } | |||
| 365 | ||||
| 366 | if (compare_version(a, b) < 0) { | |||
| 367 | log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to)({ int _level = (((4))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 367, __func__, "Skipping \"%s\", since a newer boot loader version exists already." , to) : -abs(_e); }); | |||
| 368 | return -ESTALE116; | |||
| 369 | } | |||
| 370 | ||||
| 371 | return 0; | |||
| 372 | } | |||
| 373 | ||||
| 374 | static int copy_file_with_version_check(const char *from, const char *to, bool_Bool force) { | |||
| 375 | _cleanup_close___attribute__((cleanup(closep))) int fd_from = -1, fd_to = -1; | |||
| 376 | _cleanup_free___attribute__((cleanup(freep))) char *t = NULL((void*)0); | |||
| 377 | int r; | |||
| 378 | ||||
| 379 | fd_from = open(from, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400); | |||
| 380 | if (fd_from < 0) | |||
| 381 | return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 381, __func__, "Failed to open \"%s\" for reading: %m" , from) : -abs(_e); }); | |||
| 382 | ||||
| 383 | if (!force) { | |||
| 384 | fd_to = open(to, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400); | |||
| 385 | if (fd_to < 0) { | |||
| 386 | if (errno(*__errno_location ()) != -ENOENT2) | |||
| 387 | return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 387, __func__, "Failed to open \"%s\" for reading: %m" , to) : -abs(_e); }); | |||
| 388 | } else { | |||
| 389 | r = version_check(fd_from, from, fd_to, to); | |||
| 390 | if (r < 0) | |||
| 391 | return r; | |||
| 392 | ||||
| 393 | if (lseek(fd_from, 0, SEEK_SET0) == (off_t) -1) | |||
| 394 | return log_error_errno(errno, "Failed to seek in \"%s\": %m", from)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 394, __func__, "Failed to seek in \"%s\": %m" , from) : -abs(_e); }); | |||
| 395 | ||||
| 396 | fd_to = safe_close(fd_to); | |||
| 397 | } | |||
| 398 | } | |||
| 399 | ||||
| 400 | r = tempfn_random(to, NULL((void*)0), &t); | |||
| 401 | if (r < 0) | |||
| 402 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 402 , __func__); | |||
| 403 | ||||
| 404 | RUN_WITH_UMASK(0000)for (__attribute__((cleanup(_reset_umask_))) struct _umask_struct_ _saved_umask_ = { umask(0000), 0 }; !_saved_umask_.quit ; _saved_umask_ .quit = 1) { | |||
| 405 | fd_to = open(t, O_WRONLY01|O_CREAT0100|O_CLOEXEC02000000|O_EXCL0200|O_NOFOLLOW0400000, 0644); | |||
| 406 | if (fd_to < 0) | |||
| 407 | return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 407, __func__, "Failed to open \"%s\" for writing: %m" , t) : -abs(_e); }); | |||
| 408 | } | |||
| 409 | ||||
| 410 | r = copy_bytes(fd_from, fd_to, (uint64_t) -1, COPY_REFLINK); | |||
| 411 | if (r < 0) { | |||
| 412 | (void) unlink(t); | |||
| 413 | return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", from, t)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 413, __func__, "Failed to copy data from \"%s\" to \"%s\": %m" , from, t) : -abs(_e); }); | |||
| 414 | } | |||
| 415 | ||||
| 416 | (void) copy_times(fd_from, fd_to); | |||
| 417 | ||||
| 418 | if (fsync(fd_to) < 0) { | |||
| 419 | (void) unlink_noerrno(t); | |||
| 420 | return log_error_errno(errno, "Failed to copy data from \"%s\" to \"%s\": %m", from, t)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 420, __func__, "Failed to copy data from \"%s\" to \"%s\": %m" , from, t) : -abs(_e); }); | |||
| 421 | } | |||
| 422 | ||||
| 423 | (void) fsync_directory_of_file(fd_to); | |||
| 424 | ||||
| 425 | if (renameat(AT_FDCWD-100, t, AT_FDCWD-100, to) < 0) { | |||
| 426 | (void) unlink_noerrno(t); | |||
| 427 | return log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", t, to)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 427, __func__, "Failed to rename \"%s\" to \"%s\": %m" , t, to) : -abs(_e); }); | |||
| 428 | } | |||
| 429 | ||||
| 430 | log_info("Copied \"%s\" to \"%s\".", from, to)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 430, __func__, "Copied \"%s\" to \"%s\"." , from, to) : -abs(_e); }); | |||
| 431 | ||||
| 432 | return 0; | |||
| 433 | } | |||
| 434 | ||||
| 435 | static int mkdir_one(const char *prefix, const char *suffix) { | |||
| 436 | char *p; | |||
| 437 | ||||
| 438 | p = strjoina(prefix, "/", suffix)({ const char *_appendees_[] = { prefix, "/", suffix }; char * _d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 439 | if (mkdir(p, 0700) < 0) { | |||
| 440 | if (errno(*__errno_location ()) != EEXIST17) | |||
| 441 | return log_error_errno(errno, "Failed to create \"%s\": %m", p)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 441, __func__, "Failed to create \"%s\": %m" , p) : -abs(_e); }); | |||
| 442 | } else | |||
| 443 | log_info("Created \"%s\".", p)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 443, __func__, "Created \"%s\".", p ) : -abs(_e); }); | |||
| 444 | ||||
| 445 | return 0; | |||
| 446 | } | |||
| 447 | ||||
| 448 | static const char *efi_subdirs[] = { | |||
| 449 | "EFI", | |||
| 450 | "EFI/systemd", | |||
| 451 | "EFI/BOOT", | |||
| 452 | "loader", | |||
| 453 | "loader/entries", | |||
| 454 | NULL((void*)0) | |||
| 455 | }; | |||
| 456 | ||||
| 457 | static int create_dirs(const char *esp_path) { | |||
| 458 | const char **i; | |||
| 459 | int r; | |||
| 460 | ||||
| 461 | STRV_FOREACH(i, efi_subdirs)for ((i) = (efi_subdirs); (i) && *(i); (i)++) { | |||
| 462 | r = mkdir_one(esp_path, *i); | |||
| 463 | if (r < 0) | |||
| 464 | return r; | |||
| 465 | } | |||
| 466 | ||||
| 467 | return 0; | |||
| 468 | } | |||
| 469 | ||||
| 470 | static int copy_one_file(const char *esp_path, const char *name, bool_Bool force) { | |||
| 471 | char *p, *q; | |||
| 472 | int r; | |||
| 473 | ||||
| 474 | p = strjoina(BOOTLIBDIR "/", name)({ const char *_appendees_[] = { "/usr/lib/systemd/boot/efi" "/" , name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for ( _i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 475 | q = strjoina(esp_path, "/EFI/systemd/", name)({ const char *_appendees_[] = { esp_path, "/EFI/systemd/", name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 476 | r = copy_file_with_version_check(p, q, force); | |||
| 477 | ||||
| 478 | if (startswith(name, "systemd-boot")) { | |||
| 479 | int k; | |||
| 480 | char *v; | |||
| 481 | ||||
| 482 | /* Create the EFI default boot loader name (specified for removable devices) */ | |||
| 483 | v = strjoina(esp_path, "/EFI/BOOT/BOOT",({ const char *_appendees_[] = { esp_path, "/EFI/BOOT/BOOT", name + (sizeof("""systemd-boot""") - 1) }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ ( __builtin_choose_expr( !__builtin_types_compatible_p(typeof(_appendees_ ), typeof(&*(_appendees_))), sizeof(_appendees_)/sizeof(( _appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_ ++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }) | |||
| 484 | name + STRLEN("systemd-boot"))({ const char *_appendees_[] = { esp_path, "/EFI/BOOT/BOOT", name + (sizeof("""systemd-boot""") - 1) }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ ( __builtin_choose_expr( !__builtin_types_compatible_p(typeof(_appendees_ ), typeof(&*(_appendees_))), sizeof(_appendees_)/sizeof(( _appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_ ++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 485 | ascii_strupper(strrchr(v, '/') + 1); | |||
| 486 | ||||
| 487 | k = copy_file_with_version_check(p, v, force); | |||
| 488 | if (k < 0 && r == 0) | |||
| 489 | r = k; | |||
| 490 | } | |||
| 491 | ||||
| 492 | return r; | |||
| 493 | } | |||
| 494 | ||||
| 495 | static int install_binaries(const char *esp_path, bool_Bool force) { | |||
| 496 | struct dirent *de; | |||
| 497 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
| 498 | int r = 0; | |||
| 499 | ||||
| 500 | if (force) { | |||
| 501 | /* Don't create any of these directories when we are | |||
| 502 | * just updating. When we update we'll drop-in our | |||
| 503 | * files (unless there are newer ones already), but we | |||
| 504 | * won't create the directories for them in the first | |||
| 505 | * place. */ | |||
| 506 | r = create_dirs(esp_path); | |||
| 507 | if (r < 0) | |||
| 508 | return r; | |||
| 509 | } | |||
| 510 | ||||
| 511 | d = opendir(BOOTLIBDIR"/usr/lib/systemd/boot/efi"); | |||
| 512 | if (!d) | |||
| 513 | return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 513, __func__, "Failed to open \"" "/usr/lib/systemd/boot/efi""\": %m") : -abs(_e); }); | |||
| 514 | ||||
| 515 | FOREACH_DIRENT(de, d, break)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { break; } break; } else if (hidden_or_backup_file ((de)->d_name)) continue; else { | |||
| 516 | int k; | |||
| 517 | ||||
| 518 | if (!endswith_no_case(de->d_name, ".efi")) | |||
| 519 | continue; | |||
| 520 | ||||
| 521 | k = copy_one_file(esp_path, de->d_name, force); | |||
| 522 | if (k < 0 && r == 0) | |||
| 523 | r = k; | |||
| 524 | } | |||
| 525 | ||||
| 526 | return r; | |||
| 527 | } | |||
| 528 | ||||
| 529 | static bool_Bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) { | |||
| 530 | _cleanup_free___attribute__((cleanup(freep))) char *opath = NULL((void*)0); | |||
| 531 | sd_id128_t ouuid; | |||
| 532 | int r; | |||
| 533 | ||||
| 534 | r = efi_get_boot_option(id, NULL((void*)0), &ouuid, &opath, NULL((void*)0)); | |||
| 535 | if (r < 0) | |||
| 536 | return false0; | |||
| 537 | if (!sd_id128_equal(uuid, ouuid)) | |||
| 538 | return false0; | |||
| 539 | if (!streq_ptr(path, opath)) | |||
| 540 | return false0; | |||
| 541 | ||||
| 542 | return true1; | |||
| 543 | } | |||
| 544 | ||||
| 545 | static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) { | |||
| 546 | _cleanup_free___attribute__((cleanup(freep))) uint16_t *options = NULL((void*)0); | |||
| 547 | int n, i; | |||
| 548 | ||||
| 549 | n = efi_get_boot_options(&options); | |||
| 550 | if (n < 0) | |||
| 551 | return n; | |||
| 552 | ||||
| 553 | /* find already existing systemd-boot entry */ | |||
| 554 | for (i = 0; i < n; i++) | |||
| 555 | if (same_entry(options[i], uuid, path)) { | |||
| 556 | *id = options[i]; | |||
| 557 | return 1; | |||
| 558 | } | |||
| 559 | ||||
| 560 | /* find free slot in the sorted BootXXXX variable list */ | |||
| 561 | for (i = 0; i < n; i++) | |||
| 562 | if (i != options[i]) { | |||
| 563 | *id = i; | |||
| 564 | return 1; | |||
| 565 | } | |||
| 566 | ||||
| 567 | /* use the next one */ | |||
| 568 | if (i == 0xffff) | |||
| 569 | return -ENOSPC28; | |||
| 570 | *id = i; | |||
| 571 | return 0; | |||
| 572 | } | |||
| 573 | ||||
| 574 | static int insert_into_order(uint16_t slot, bool_Bool first) { | |||
| 575 | _cleanup_free___attribute__((cleanup(freep))) uint16_t *order = NULL((void*)0); | |||
| 576 | uint16_t *t; | |||
| 577 | int n, i; | |||
| 578 | ||||
| 579 | n = efi_get_boot_order(&order); | |||
| 580 | if (n <= 0) | |||
| 581 | /* no entry, add us */ | |||
| 582 | return efi_set_boot_order(&slot, 1); | |||
| 583 | ||||
| 584 | /* are we the first and only one? */ | |||
| 585 | if (n == 1 && order[0] == slot) | |||
| 586 | return 0; | |||
| 587 | ||||
| 588 | /* are we already in the boot order? */ | |||
| 589 | for (i = 0; i < n; i++) { | |||
| 590 | if (order[i] != slot) | |||
| 591 | continue; | |||
| 592 | ||||
| 593 | /* we do not require to be the first one, all is fine */ | |||
| 594 | if (!first) | |||
| 595 | return 0; | |||
| 596 | ||||
| 597 | /* move us to the first slot */ | |||
| 598 | memmove(order + 1, order, i * sizeof(uint16_t)); | |||
| 599 | order[0] = slot; | |||
| 600 | return efi_set_boot_order(order, n); | |||
| 601 | } | |||
| 602 | ||||
| 603 | /* extend array */ | |||
| 604 | t = realloc(order, (n + 1) * sizeof(uint16_t)); | |||
| 605 | if (!t) | |||
| 606 | return -ENOMEM12; | |||
| 607 | order = t; | |||
| 608 | ||||
| 609 | /* add us to the top or end of the list */ | |||
| 610 | if (first) { | |||
| 611 | memmove(order + 1, order, n * sizeof(uint16_t)); | |||
| 612 | order[0] = slot; | |||
| 613 | } else | |||
| 614 | order[n] = slot; | |||
| 615 | ||||
| 616 | return efi_set_boot_order(order, n + 1); | |||
| 617 | } | |||
| 618 | ||||
| 619 | static int remove_from_order(uint16_t slot) { | |||
| 620 | _cleanup_free___attribute__((cleanup(freep))) uint16_t *order = NULL((void*)0); | |||
| 621 | int n, i; | |||
| 622 | ||||
| 623 | n = efi_get_boot_order(&order); | |||
| 624 | if (n <= 0) | |||
| 625 | return n; | |||
| 626 | ||||
| 627 | for (i = 0; i < n; i++) { | |||
| 628 | if (order[i] != slot) | |||
| 629 | continue; | |||
| 630 | ||||
| 631 | if (i + 1 < n) | |||
| 632 | memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t)); | |||
| 633 | return efi_set_boot_order(order, n - 1); | |||
| 634 | } | |||
| 635 | ||||
| 636 | return 0; | |||
| 637 | } | |||
| 638 | ||||
| 639 | static int install_variables(const char *esp_path, | |||
| 640 | uint32_t part, uint64_t pstart, uint64_t psize, | |||
| 641 | sd_id128_t uuid, const char *path, | |||
| 642 | bool_Bool first) { | |||
| 643 | char *p; | |||
| 644 | uint16_t slot; | |||
| 645 | int r; | |||
| 646 | ||||
| 647 | if (!is_efi_boot()) { | |||
| 648 | log_warning("Not booted with EFI, skipping EFI variable setup.")({ int _level = (((4))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 648, __func__, "Not booted with EFI, skipping EFI variable setup." ) : -abs(_e); }); | |||
| 649 | return 0; | |||
| 650 | } | |||
| 651 | ||||
| 652 | p = strjoina(esp_path, path)({ const char *_appendees_[] = { esp_path, path }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 653 | if (access(p, F_OK0) < 0) { | |||
| 654 | if (errno(*__errno_location ()) == ENOENT2) | |||
| 655 | return 0; | |||
| 656 | ||||
| 657 | return log_error_errno(errno, "Cannot access \"%s\": %m", p)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 657, __func__, "Cannot access \"%s\": %m" , p) : -abs(_e); }); | |||
| 658 | } | |||
| 659 | ||||
| 660 | r = find_slot(uuid, path, &slot); | |||
| 661 | if (r < 0) | |||
| 662 | return log_error_errno(r,({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 665, __func__, r == -2 ? "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" : "Failed to determine current boot order: %m") : -abs(_e); } ) | |||
| 663 | r == -ENOENT ?({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 665, __func__, r == -2 ? "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" : "Failed to determine current boot order: %m") : -abs(_e); } ) | |||
| 664 | "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 665, __func__, r == -2 ? "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" : "Failed to determine current boot order: %m") : -abs(_e); } ) | |||
| 665 | "Failed to determine current boot order: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 665, __func__, r == -2 ? "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" : "Failed to determine current boot order: %m") : -abs(_e); } ); | |||
| 666 | ||||
| 667 | if (first || r == 0) { | |||
| 668 | r = efi_add_boot_option(slot, "Linux Boot Manager", | |||
| 669 | part, pstart, psize, | |||
| 670 | uuid, path); | |||
| 671 | if (r < 0) | |||
| 672 | return log_error_errno(r, "Failed to create EFI Boot variable entry: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 672, __func__, "Failed to create EFI Boot variable entry: %m" ) : -abs(_e); }); | |||
| 673 | ||||
| 674 | log_info("Created EFI boot entry \"Linux Boot Manager\".")({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 674, __func__, "Created EFI boot entry \"Linux Boot Manager\"." ) : -abs(_e); }); | |||
| 675 | } | |||
| 676 | ||||
| 677 | return insert_into_order(slot, first); | |||
| 678 | } | |||
| 679 | ||||
| 680 | static int remove_boot_efi(const char *esp_path) { | |||
| 681 | char *p; | |||
| 682 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
| 683 | struct dirent *de; | |||
| 684 | int r, c = 0; | |||
| 685 | ||||
| 686 | p = strjoina(esp_path, "/EFI/BOOT")({ const char *_appendees_[] = { esp_path, "/EFI/BOOT" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 687 | d = opendir(p); | |||
| 688 | if (!d) { | |||
| 689 | if (errno(*__errno_location ()) == ENOENT2) | |||
| 690 | return 0; | |||
| 691 | ||||
| 692 | return log_error_errno(errno, "Failed to open directory \"%s\": %m", p)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 692, __func__, "Failed to open directory \"%s\": %m" , p) : -abs(_e); }); | |||
| 693 | } | |||
| 694 | ||||
| 695 | FOREACH_DIRENT(de, d, break)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { break; } break; } else if (hidden_or_backup_file ((de)->d_name)) continue; else { | |||
| 696 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
| 697 | _cleanup_free___attribute__((cleanup(freep))) char *v = NULL((void*)0); | |||
| 698 | ||||
| 699 | if (!endswith_no_case(de->d_name, ".efi")) | |||
| 700 | continue; | |||
| 701 | ||||
| 702 | if (!startswith_no_case(de->d_name, "boot")) | |||
| 703 | continue; | |||
| 704 | ||||
| 705 | fd = openat(dirfd(d), de->d_name, O_RDONLY00|O_CLOEXEC02000000); | |||
| 706 | if (fd < 0) | |||
| 707 | return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 707, __func__, "Failed to open \"%s/%s\" for reading: %m" , p, de->d_name) : -abs(_e); }); | |||
| 708 | ||||
| 709 | r = get_file_version(fd, &v); | |||
| 710 | if (r < 0) | |||
| 711 | return r; | |||
| 712 | if (r > 0 && startswith(v, "systemd-boot ")) { | |||
| 713 | r = unlinkat(dirfd(d), de->d_name, 0); | |||
| 714 | if (r < 0) | |||
| 715 | return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 715, __func__, "Failed to remove \"%s/%s\": %m" , p, de->d_name) : -abs(_e); }); | |||
| 716 | ||||
| 717 | log_info("Removed \"%s/%s\".", p, de->d_name)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 717, __func__, "Removed \"%s/%s\"." , p, de->d_name) : -abs(_e); }); | |||
| 718 | } | |||
| 719 | ||||
| 720 | c++; | |||
| 721 | } | |||
| 722 | ||||
| 723 | return c; | |||
| 724 | } | |||
| 725 | ||||
| 726 | static int rmdir_one(const char *prefix, const char *suffix) { | |||
| 727 | char *p; | |||
| 728 | ||||
| 729 | p = strjoina(prefix, "/", suffix)({ const char *_appendees_[] = { prefix, "/", suffix }; char * _d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 730 | if (rmdir(p) < 0) { | |||
| 731 | if (!IN_SET(errno, ENOENT, ENOTEMPTY)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){2, 39})/sizeof(int)]; switch((*__errno_location ())) { case 2: case 39: _found = 1; break; default: break; } _found; })) | |||
| 732 | return log_error_errno(errno, "Failed to remove \"%s\": %m", p)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/boot/bootctl.c", 732, __func__, "Failed to remove \"%s\": %m" , p) : -abs(_e); }); | |||
| 733 | } else | |||
| 734 | log_info("Removed \"%s\".", p)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 734, __func__, "Removed \"%s\".", p ) : -abs(_e); }); | |||
| 735 | ||||
| 736 | return 0; | |||
| 737 | } | |||
| 738 | ||||
| 739 | static int remove_binaries(const char *esp_path) { | |||
| 740 | char *p; | |||
| 741 | int r, q; | |||
| 742 | unsigned i; | |||
| 743 | ||||
| 744 | p = strjoina(esp_path, "/EFI/systemd")({ const char *_appendees_[] = { esp_path, "/EFI/systemd" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 745 | r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); | |||
| 746 | ||||
| 747 | q = remove_boot_efi(esp_path); | |||
| 748 | if (q < 0 && r == 0) | |||
| 749 | r = q; | |||
| 750 | ||||
| 751 | for (i = ELEMENTSOF(efi_subdirs)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(efi_subdirs), typeof(&*(efi_subdirs))), sizeof(efi_subdirs )/sizeof((efi_subdirs)[0]), ((void)0)))-1; i > 0; i--) { | |||
| 752 | q = rmdir_one(esp_path, efi_subdirs[i-1]); | |||
| 753 | if (q < 0 && r == 0) | |||
| 754 | r = q; | |||
| 755 | } | |||
| 756 | ||||
| 757 | return r; | |||
| 758 | } | |||
| 759 | ||||
| 760 | static int remove_variables(sd_id128_t uuid, const char *path, bool_Bool in_order) { | |||
| 761 | uint16_t slot; | |||
| 762 | int r; | |||
| 763 | ||||
| 764 | if (!is_efi_boot()) | |||
| 765 | return 0; | |||
| 766 | ||||
| 767 | r = find_slot(uuid, path, &slot); | |||
| 768 | if (r != 1) | |||
| 769 | return 0; | |||
| 770 | ||||
| 771 | r = efi_remove_boot_option(slot); | |||
| 772 | if (r < 0) | |||
| 773 | return r; | |||
| 774 | ||||
| 775 | if (in_order) | |||
| 776 | return remove_from_order(slot); | |||
| 777 | ||||
| 778 | return 0; | |||
| 779 | } | |||
| 780 | ||||
| 781 | static int install_loader_config(const char *esp_path) { | |||
| 782 | ||||
| 783 | char machine_string[SD_ID128_STRING_MAX33]; | |||
| 784 | _cleanup_(unlink_and_freep)__attribute__((cleanup(unlink_and_freep))) char *t = NULL((void*)0); | |||
| 785 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0); | |||
| 786 | sd_id128_t machine_id; | |||
| 787 | const char *p; | |||
| 788 | int r, fd; | |||
| 789 | ||||
| 790 | r = sd_id128_get_machine(&machine_id); | |||
| 791 | if (r < 0) | |||
| 792 | return log_error_errno(r, "Failed to get machine id: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 792, __func__, "Failed to get machine id: %m" ) : -abs(_e); }); | |||
| 793 | ||||
| 794 | p = strjoina(esp_path, "/loader/loader.conf")({ const char *_appendees_[] = { esp_path, "/loader/loader.conf" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 795 | ||||
| 796 | if (access(p, F_OK0) >= 0) /* Silently skip creation if the file already exists (early check) */ | |||
| 797 | return 0; | |||
| 798 | ||||
| 799 | fd = open_tmpfile_linkable(p, O_WRONLY01|O_CLOEXEC02000000, &t); | |||
| 800 | if (fd < 0) | |||
| 801 | return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p)({ int _level = ((3)), _e = ((fd)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 801, __func__, "Failed to open \"%s\" for writing: %m" , p) : -abs(_e); }); | |||
| 802 | ||||
| 803 | f = fdopen(fd, "we"); | |||
| 804 | if (!f) { | |||
| 805 | safe_close(fd); | |||
| 806 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 806 , __func__); | |||
| 807 | } | |||
| 808 | ||||
| 809 | fprintf(f, "#timeout 3\n"); | |||
| 810 | fprintf(f, "#console-mode keep\n"); | |||
| 811 | fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string)); | |||
| 812 | ||||
| 813 | r = fflush_sync_and_check(f); | |||
| 814 | if (r < 0) | |||
| 815 | return log_error_errno(r, "Failed to write \"%s\": %m", p)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 815, __func__, "Failed to write \"%s\": %m" , p) : -abs(_e); }); | |||
| 816 | ||||
| 817 | r = link_tmpfile(fd, t, p); | |||
| 818 | if (r == -EEXIST17) | |||
| 819 | return 0; /* Silently skip creation if the file exists now (recheck) */ | |||
| 820 | if (r < 0) | |||
| 821 | return log_error_errno(r, "Failed to move \"%s\" into place: %m", p)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 821, __func__, "Failed to move \"%s\" into place: %m" , p) : -abs(_e); }); | |||
| 822 | ||||
| 823 | t = mfree(t); | |||
| 824 | ||||
| 825 | return 1; | |||
| 826 | } | |||
| 827 | ||||
| 828 | static int help(int argc, char *argv[], void *userdata) { | |||
| 829 | ||||
| 830 | printf("%s [COMMAND] [OPTIONS...]\n" | |||
| 831 | "\n" | |||
| 832 | "Install, update or remove the systemd-boot EFI boot manager.\n\n" | |||
| 833 | " -h --help Show this help\n" | |||
| 834 | " --version Print version\n" | |||
| 835 | " --path=PATH Path to the EFI System Partition (ESP)\n" | |||
| 836 | " -p --print-path Print path to the EFI partition\n" | |||
| 837 | " --no-variables Don't touch EFI variables\n" | |||
| 838 | "\n" | |||
| 839 | "Commands:\n" | |||
| 840 | " status Show status of installed systemd-boot and EFI variables\n" | |||
| 841 | " list List boot entries\n" | |||
| 842 | " install Install systemd-boot to the ESP and EFI variables\n" | |||
| 843 | " update Update systemd-boot in the ESP and EFI variables\n" | |||
| 844 | " remove Remove systemd-boot from the ESP and EFI variables\n", | |||
| 845 | program_invocation_short_name); | |||
| 846 | ||||
| 847 | return 0; | |||
| 848 | } | |||
| 849 | ||||
| 850 | static int parse_argv(int argc, char *argv[]) { | |||
| 851 | enum { | |||
| 852 | ARG_PATH = 0x100, | |||
| 853 | ARG_VERSION, | |||
| 854 | ARG_NO_VARIABLES, | |||
| 855 | }; | |||
| 856 | ||||
| 857 | static const struct option options[] = { | |||
| 858 | { "help", no_argument0, NULL((void*)0), 'h' }, | |||
| 859 | { "version", no_argument0, NULL((void*)0), ARG_VERSION }, | |||
| 860 | { "path", required_argument1, NULL((void*)0), ARG_PATH }, | |||
| 861 | { "print-path", no_argument0, NULL((void*)0), 'p' }, | |||
| 862 | { "no-variables", no_argument0, NULL((void*)0), ARG_NO_VARIABLES }, | |||
| 863 | { NULL((void*)0), 0, NULL((void*)0), 0 } | |||
| 864 | }; | |||
| 865 | ||||
| 866 | int c, r; | |||
| 867 | ||||
| 868 | assert(argc >= 0)do { if ((__builtin_expect(!!(!(argc >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("argc >= 0"), "../src/boot/bootctl.c" , 868, __PRETTY_FUNCTION__); } while (0); | |||
| 869 | assert(argv)do { if ((__builtin_expect(!!(!(argv)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("argv"), "../src/boot/bootctl.c", 869, __PRETTY_FUNCTION__ ); } while (0); | |||
| 870 | ||||
| 871 | while ((c = getopt_long(argc, argv, "hp", options, NULL((void*)0))) >= 0) | |||
| 872 | switch (c) { | |||
| 873 | ||||
| 874 | case 'h': | |||
| 875 | help(0, NULL((void*)0), NULL((void*)0)); | |||
| 876 | return 0; | |||
| 877 | ||||
| 878 | case ARG_VERSION: | |||
| 879 | return version(); | |||
| 880 | ||||
| 881 | case ARG_PATH: | |||
| 882 | r = free_and_strdup(&arg_path, optarg); | |||
| 883 | if (r < 0) | |||
| 884 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 884 , __func__); | |||
| 885 | break; | |||
| 886 | ||||
| 887 | case 'p': | |||
| 888 | arg_print_path = true1; | |||
| 889 | break; | |||
| 890 | ||||
| 891 | case ARG_NO_VARIABLES: | |||
| 892 | arg_touch_variables = false0; | |||
| 893 | break; | |||
| 894 | ||||
| 895 | case '?': | |||
| 896 | return -EINVAL22; | |||
| 897 | ||||
| 898 | default: | |||
| 899 | assert_not_reached("Unknown option")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, ( "Unknown option"), "../src/boot/bootctl.c", 899, __PRETTY_FUNCTION__ ); } while (0); | |||
| 900 | } | |||
| 901 | ||||
| 902 | return 1; | |||
| 903 | } | |||
| 904 | ||||
| 905 | static void read_loader_efi_var(const char *name, char **var) { | |||
| 906 | int r; | |||
| 907 | ||||
| 908 | r = efi_get_variable_string(EFI_VENDOR_LOADER((const sd_id128_t) { .bytes = { 0x4a, 0x67, 0xb0, 0x82, 0x0a , 0x4c, 0x41, 0xcf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f }}), name, var); | |||
| 909 | if (r < 0 && r != -ENOENT2) | |||
| 910 | log_warning_errno(r, "Failed to read EFI variable %s: %m", name)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 910, __func__, "Failed to read EFI variable %s: %m" , name) : -abs(_e); }); | |||
| 911 | } | |||
| 912 | ||||
| 913 | static int verb_status(int argc, char *argv[], void *userdata) { | |||
| 914 | ||||
| 915 | sd_id128_t uuid = SD_ID128_NULL((const sd_id128_t) { .qwords = { 0, 0 }}); | |||
| 916 | int r, k; | |||
| 917 | ||||
| 918 | r = acquire_esp(geteuid() != 0, NULL((void*)0), NULL((void*)0), NULL((void*)0), &uuid); | |||
| 919 | ||||
| 920 | if (arg_print_path) { | |||
| 921 | if (r == -EACCES13) /* If we couldn't acquire the ESP path, log about access errors (which is the only | |||
| 922 | * error the find_esp_and_warn() won't log on its own) */ | |||
| 923 | return log_error_errno(r, "Failed to determine ESP: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 923, __func__, "Failed to determine ESP: %m" ) : -abs(_e); }); | |||
| 924 | if (r < 0) | |||
| 925 | return r; | |||
| 926 | ||||
| 927 | puts(arg_path); | |||
| 928 | return 0; | |||
| 929 | } | |||
| 930 | ||||
| 931 | r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we | |||
| 932 | * can show */ | |||
| 933 | ||||
| 934 | if (is_efi_boot()) { | |||
| 935 | _cleanup_free___attribute__((cleanup(freep))) char *fw_type = NULL((void*)0), *fw_info = NULL((void*)0), *loader = NULL((void*)0), *loader_path = NULL((void*)0), *stub = NULL((void*)0); | |||
| 936 | sd_id128_t loader_part_uuid = SD_ID128_NULL((const sd_id128_t) { .qwords = { 0, 0 }}); | |||
| 937 | ||||
| 938 | read_loader_efi_var("LoaderFirmwareType", &fw_type); | |||
| 939 | read_loader_efi_var("LoaderFirmwareInfo", &fw_info); | |||
| 940 | read_loader_efi_var("LoaderInfo", &loader); | |||
| 941 | read_loader_efi_var("StubInfo", &stub); | |||
| 942 | read_loader_efi_var("LoaderImageIdentifier", &loader_path); | |||
| 943 | ||||
| 944 | if (loader_path) | |||
| 945 | efi_tilt_backslashes(loader_path); | |||
| 946 | ||||
| 947 | k = efi_loader_get_device_part_uuid(&loader_part_uuid); | |||
| 948 | if (k < 0 && k != -ENOENT2) | |||
| 949 | r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m")({ int _level = ((4)), _e = ((k)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 949, __func__, "Failed to read EFI variable LoaderDevicePartUUID: %m" ) : -abs(_e); }); | |||
| 950 | ||||
| 951 | printf("System:\n"); | |||
| 952 | printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info)); | |||
| 953 | ||||
| 954 | k = is_efi_secure_boot(); | |||
| 955 | if (k < 0) | |||
| 956 | r = log_warning_errno(k, "Failed to query secure boot status: %m")({ int _level = ((4)), _e = ((k)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 956, __func__, "Failed to query secure boot status: %m" ) : -abs(_e); }); | |||
| 957 | else | |||
| 958 | printf(" Secure Boot: %sd\n", enable_disable(k)); | |||
| 959 | ||||
| 960 | k = is_efi_secure_boot_setup_mode(); | |||
| 961 | if (k < 0) | |||
| 962 | r = log_warning_errno(k, "Failed to query secure boot mode: %m")({ int _level = ((4)), _e = ((k)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 962, __func__, "Failed to query secure boot mode: %m" ) : -abs(_e); }); | |||
| 963 | else | |||
| 964 | printf(" Setup Mode: %s\n", k ? "setup" : "user"); | |||
| 965 | printf("\n"); | |||
| 966 | ||||
| 967 | printf("Current Loader:\n"); | |||
| 968 | printf(" Product: %s\n", strna(loader)); | |||
| 969 | if (stub) | |||
| 970 | printf(" Stub: %s\n", stub); | |||
| 971 | if (!sd_id128_is_null(loader_part_uuid)) | |||
| 972 | printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", | |||
| 973 | SD_ID128_FORMAT_VAL(loader_part_uuid)(loader_part_uuid).bytes[0], (loader_part_uuid).bytes[1], (loader_part_uuid ).bytes[2], (loader_part_uuid).bytes[3], (loader_part_uuid).bytes [4], (loader_part_uuid).bytes[5], (loader_part_uuid).bytes[6] , (loader_part_uuid).bytes[7], (loader_part_uuid).bytes[8], ( loader_part_uuid).bytes[9], (loader_part_uuid).bytes[10], (loader_part_uuid ).bytes[11], (loader_part_uuid).bytes[12], (loader_part_uuid) .bytes[13], (loader_part_uuid).bytes[14], (loader_part_uuid). bytes[15]); | |||
| 974 | else | |||
| 975 | printf(" ESP: n/a\n"); | |||
| 976 | printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); | |||
| 977 | printf("\n"); | |||
| 978 | } else | |||
| 979 | printf("System:\n Not booted with EFI\n\n"); | |||
| 980 | ||||
| 981 | if (arg_path) { | |||
| 982 | k = status_binaries(arg_path, uuid); | |||
| 983 | if (k < 0) | |||
| 984 | r = k; | |||
| 985 | } | |||
| 986 | ||||
| 987 | if (is_efi_boot()) { | |||
| 988 | k = status_variables(); | |||
| 989 | if (k < 0) | |||
| 990 | r = k; | |||
| 991 | } | |||
| 992 | ||||
| 993 | if (arg_path) { | |||
| 994 | k = status_entries(arg_path, uuid); | |||
| 995 | if (k < 0) | |||
| 996 | r = k; | |||
| 997 | } | |||
| 998 | ||||
| 999 | return r; | |||
| 1000 | } | |||
| 1001 | ||||
| 1002 | static int verb_list(int argc, char *argv[], void *userdata) { | |||
| 1003 | _cleanup_(boot_config_free)__attribute__((cleanup(boot_config_free))) BootConfig config = {}; | |||
| 1004 | sd_id128_t uuid = SD_ID128_NULL((const sd_id128_t) { .qwords = { 0, 0 }}); | |||
| 1005 | unsigned n; | |||
| 1006 | int r; | |||
| 1007 | ||||
| 1008 | /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn | |||
| 1009 | * off logging about access errors and turn off potentially privileged device probing. Here we're interested in | |||
| 1010 | * the latter but not the former, hence request the mode, and log about EACCES. */ | |||
| 1011 | ||||
| 1012 | r = acquire_esp(geteuid() != 0, NULL((void*)0), NULL((void*)0), NULL((void*)0), &uuid); | |||
| 1013 | if (r == -EACCES13) /* We really need the ESP path for this call, hence also log about access errors */ | |||
| 1014 | return log_error_errno(r, "Failed to determine ESP: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 1014, __func__, "Failed to determine ESP: %m" ) : -abs(_e); }); | |||
| 1015 | if (r < 0) | |||
| 1016 | return r; | |||
| 1017 | ||||
| 1018 | r = boot_entries_load_config(arg_path, &config); | |||
| 1019 | if (r < 0) | |||
| 1020 | return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 1021, __func__, "Failed to load bootspec config from \"%s/loader\": %m" , arg_path) : -abs(_e); }) | |||
| 1021 | arg_path)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/boot/bootctl.c", 1021, __func__, "Failed to load bootspec config from \"%s/loader\": %m" , arg_path) : -abs(_e); }); | |||
| 1022 | ||||
| 1023 | printf("Available boot entries:\n"); | |||
| 1024 | ||||
| 1025 | for (n = 0; n < config.n_entries; n++) { | |||
| 1026 | const BootEntry *e = &config.entries[n]; | |||
| 1027 | ||||
| 1028 | printf(" title: %s%s%s%s%s%s\n", | |||
| 1029 | ansi_highlight(), | |||
| 1030 | boot_entry_title(e), | |||
| 1031 | ansi_normal(), | |||
| 1032 | ansi_highlight_green(), | |||
| 1033 | n == (unsigned) config.default_entry ? " (default)" : "", | |||
| 1034 | ansi_normal()); | |||
| 1035 | if (e->version) | |||
| 1036 | printf(" version: %s\n", e->version); | |||
| 1037 | if (e->machine_id) | |||
| 1038 | printf(" machine-id: %s\n", e->machine_id); | |||
| 1039 | if (e->architecture) | |||
| 1040 | printf(" architecture: %s\n", e->architecture); | |||
| 1041 | if (e->kernel) | |||
| 1042 | printf(" linux: %s\n", e->kernel); | |||
| 1043 | if (!strv_isempty(e->initrd)) { | |||
| 1044 | _cleanup_free___attribute__((cleanup(freep))) char *t; | |||
| 1045 | ||||
| 1046 | t = strv_join(e->initrd, " "); | |||
| 1047 | if (!t) | |||
| 1048 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 1048 , __func__); | |||
| 1049 | ||||
| 1050 | printf(" initrd: %s\n", t); | |||
| 1051 | } | |||
| 1052 | if (!strv_isempty(e->options)) { | |||
| 1053 | _cleanup_free___attribute__((cleanup(freep))) char *t; | |||
| 1054 | ||||
| 1055 | t = strv_join(e->options, " "); | |||
| 1056 | if (!t) | |||
| 1057 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/boot/bootctl.c", 1057 , __func__); | |||
| 1058 | ||||
| 1059 | printf(" options: %s\n", t); | |||
| 1060 | } | |||
| 1061 | if (e->device_tree) | |||
| 1062 | printf(" devicetree: %s\n", e->device_tree); | |||
| 1063 | ||||
| 1064 | puts(""); | |||
| 1065 | } | |||
| 1066 | ||||
| 1067 | return 0; | |||
| 1068 | } | |||
| 1069 | ||||
| 1070 | static int verb_install(int argc, char *argv[], void *userdata) { | |||
| 1071 | ||||
| 1072 | sd_id128_t uuid = SD_ID128_NULL((const sd_id128_t) { .qwords = { 0, 0 }}); | |||
| 1073 | uint64_t pstart = 0, psize = 0; | |||
| 1074 | uint32_t part = 0; | |||
| 1075 | bool_Bool install; | |||
| 1076 | int r; | |||
| 1077 | ||||
| 1078 | r = acquire_esp(false0, &part, &pstart, &psize, &uuid); | |||
| 1079 | if (r < 0) | |||
| 1080 | return r; | |||
| 1081 | ||||
| 1082 | install = streq(argv[0], "install")(strcmp((argv[0]),("install")) == 0); | |||
| 1083 | ||||
| 1084 | RUN_WITH_UMASK(0002)for (__attribute__((cleanup(_reset_umask_))) struct _umask_struct_ _saved_umask_ = { umask(0002), 0 }; !_saved_umask_.quit ; _saved_umask_ .quit = 1) { | |||
| 1085 | r = install_binaries(arg_path, install); | |||
| 1086 | if (r < 0) | |||
| 1087 | return r; | |||
| 1088 | ||||
| 1089 | if (install) { | |||
| 1090 | r = install_loader_config(arg_path); | |||
| 1091 | if (r < 0) | |||
| 1092 | return r; | |||
| 1093 | } | |||
| 1094 | } | |||
| 1095 | ||||
| 1096 | if (arg_touch_variables) | |||
| 1097 | r = install_variables(arg_path, | |||
| 1098 | part, pstart, psize, uuid, | |||
| 1099 | "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME"x64" ".efi", | |||
| 1100 | install); | |||
| 1101 | ||||
| 1102 | return r; | |||
| 1103 | } | |||
| 1104 | ||||
| 1105 | static int verb_remove(int argc, char *argv[], void *userdata) { | |||
| 1106 | sd_id128_t uuid = SD_ID128_NULL((const sd_id128_t) { .qwords = { 0, 0 }}); | |||
| 1107 | int r; | |||
| 1108 | ||||
| 1109 | r = acquire_esp(false0, NULL((void*)0), NULL((void*)0), NULL((void*)0), &uuid); | |||
| 1110 | if (r < 0) | |||
| ||||
| 1111 | return r; | |||
| 1112 | ||||
| 1113 | r = remove_binaries(arg_path); | |||
| 1114 | ||||
| 1115 | if (arg_touch_variables) { | |||
| 1116 | int q; | |||
| 1117 | ||||
| 1118 | q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME"x64" ".efi", true1); | |||
| 1119 | if (q < 0 && r == 0) | |||
| 1120 | r = q; | |||
| 1121 | } | |||
| 1122 | ||||
| 1123 | return r; | |||
| 1124 | } | |||
| 1125 | ||||
| 1126 | static int bootctl_main(int argc, char *argv[]) { | |||
| 1127 | ||||
| 1128 | static const Verb verbs[] = { | |||
| 1129 | { "help", VERB_ANY((unsigned) -1), VERB_ANY((unsigned) -1), 0, help }, | |||
| 1130 | { "status", VERB_ANY((unsigned) -1), 1, VERB_DEFAULT, verb_status }, | |||
| 1131 | { "list", VERB_ANY((unsigned) -1), 1, 0, verb_list }, | |||
| 1132 | { "install", VERB_ANY((unsigned) -1), 1, VERB_MUST_BE_ROOT, verb_install }, | |||
| 1133 | { "update", VERB_ANY((unsigned) -1), 1, VERB_MUST_BE_ROOT, verb_install }, | |||
| 1134 | { "remove", VERB_ANY((unsigned) -1), 1, VERB_MUST_BE_ROOT, verb_remove }, | |||
| 1135 | {} | |||
| 1136 | }; | |||
| 1137 | ||||
| 1138 | return dispatch_verb(argc, argv, verbs, NULL((void*)0)); | |||
| 1139 | } | |||
| 1140 | ||||
| 1141 | int main(int argc, char *argv[]) { | |||
| 1142 | int r; | |||
| 1143 | ||||
| 1144 | log_parse_environment()log_parse_environment_realm(LOG_REALM_SYSTEMD); | |||
| 1145 | log_open(); | |||
| 1146 | ||||
| 1147 | /* If we run in a container, automatically turn of EFI file system access */ | |||
| 1148 | if (detect_container() > 0) | |||
| 1149 | arg_touch_variables = false0; | |||
| 1150 | ||||
| 1151 | r = parse_argv(argc, argv); | |||
| 1152 | if (r <= 0) | |||
| 1153 | goto finish; | |||
| 1154 | ||||
| 1155 | r = bootctl_main(argc, argv); | |||
| 1156 | ||||
| 1157 | finish: | |||
| 1158 | free(arg_path); | |||
| 1159 | return r < 0 ? EXIT_FAILURE1 : EXIT_SUCCESS0; | |||
| 1160 | } |
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | #pragma once | |||
| 3 | ||||
| 4 | #include <alloca.h> | |||
| 5 | #include <stdbool.h> | |||
| 6 | #include <stddef.h> | |||
| 7 | #include <string.h> | |||
| 8 | ||||
| 9 | #include "macro.h" | |||
| 10 | ||||
| 11 | /* What is interpreted as whitespace? */ | |||
| 12 | #define WHITESPACE" \t\n\r" " \t\n\r" | |||
| 13 | #define NEWLINE"\n\r" "\n\r" | |||
| 14 | #define QUOTES"\"\'" "\"\'" | |||
| 15 | #define COMMENTS"#;" "#;" | |||
| 16 | #define GLOB_CHARS"*?[" "*?[" | |||
| 17 | #define DIGITS"0123456789" "0123456789" | |||
| 18 | #define LOWERCASE_LETTERS"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz" | |||
| 19 | #define UPPERCASE_LETTERS"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
| 20 | #define LETTERS"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LOWERCASE_LETTERS"abcdefghijklmnopqrstuvwxyz" UPPERCASE_LETTERS"ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
| 21 | #define ALPHANUMERICAL"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" LETTERS"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" DIGITS"0123456789" | |||
| 22 | #define HEXDIGITS"0123456789" "abcdefABCDEF" DIGITS"0123456789" "abcdefABCDEF" | |||
| 23 | ||||
| 24 | #define streq(a,b)(strcmp((a),(b)) == 0) (strcmp((a),(b)) == 0) | |||
| 25 | #define strneq(a, b, n)(strncmp((a), (b), (n)) == 0) (strncmp((a), (b), (n)) == 0) | |||
| 26 | #define strcaseeq(a,b)(strcasecmp((a),(b)) == 0) (strcasecmp((a),(b)) == 0) | |||
| 27 | #define strncaseeq(a, b, n)(strncasecmp((a), (b), (n)) == 0) (strncasecmp((a), (b), (n)) == 0) | |||
| 28 | ||||
| 29 | int strcmp_ptr(const char *a, const char *b) _pure___attribute__ ((pure)); | |||
| 30 | ||||
| 31 | static inline bool_Bool streq_ptr(const char *a, const char *b) { | |||
| 32 | return strcmp_ptr(a, b) == 0; | |||
| 33 | } | |||
| 34 | ||||
| 35 | static inline const char* strempty(const char *s) { | |||
| 36 | return s ?: ""; | |||
| 37 | } | |||
| 38 | ||||
| 39 | static inline const char* strnull(const char *s) { | |||
| 40 | return s ?: "(null)"; | |||
| 41 | } | |||
| 42 | ||||
| 43 | static inline const char *strna(const char *s) { | |||
| 44 | return s ?: "n/a"; | |||
| 45 | } | |||
| 46 | ||||
| 47 | static inline bool_Bool isempty(const char *p) { | |||
| 48 | return !p || !p[0]; | |||
| 49 | } | |||
| 50 | ||||
| 51 | static inline const char *empty_to_null(const char *p) { | |||
| 52 | return isempty(p) ? NULL((void*)0) : p; | |||
| 53 | } | |||
| 54 | ||||
| 55 | static inline const char *empty_to_dash(const char *str) { | |||
| 56 | return isempty(str) ? "-" : str; | |||
| 57 | } | |||
| 58 | ||||
| 59 | static inline char *startswith(const char *s, const char *prefix) { | |||
| 60 | size_t l; | |||
| 61 | ||||
| 62 | l = strlen(prefix); | |||
| 63 | if (strncmp(s, prefix, l) == 0) | |||
| ||||
| 64 | return (char*) s + l; | |||
| 65 | ||||
| 66 | return NULL((void*)0); | |||
| 67 | } | |||
| 68 | ||||
| 69 | static inline char *startswith_no_case(const char *s, const char *prefix) { | |||
| 70 | size_t l; | |||
| 71 | ||||
| 72 | l = strlen(prefix); | |||
| 73 | if (strncasecmp(s, prefix, l) == 0) | |||
| 74 | return (char*) s + l; | |||
| 75 | ||||
| 76 | return NULL((void*)0); | |||
| 77 | } | |||
| 78 | ||||
| 79 | char *endswith(const char *s, const char *postfix) _pure___attribute__ ((pure)); | |||
| 80 | char *endswith_no_case(const char *s, const char *postfix) _pure___attribute__ ((pure)); | |||
| 81 | ||||
| 82 | char *first_word(const char *s, const char *word) _pure___attribute__ ((pure)); | |||
| 83 | ||||
| 84 | const char* split(const char **state, size_t *l, const char *separator, bool_Bool quoted); | |||
| 85 | ||||
| 86 | #define FOREACH_WORD(word, length, s, state)for ((state) = (s), (word) = split(&(state), &(length ), (" \t\n\r"), (0)); (word); (word) = split(&(state), & (length), (" \t\n\r"), (0))) \ | |||
| 87 | _FOREACH_WORD(word, length, s, WHITESPACE, false, state)for ((state) = (s), (word) = split(&(state), &(length ), (" \t\n\r"), (0)); (word); (word) = split(&(state), & (length), (" \t\n\r"), (0))) | |||
| 88 | ||||
| 89 | #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (0)); (word); (word) = split(&(state), & (length), (separator), (0))) \ | |||
| 90 | _FOREACH_WORD(word, length, s, separator, false, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (0)); (word); (word) = split(&(state), & (length), (separator), (0))) | |||
| 91 | ||||
| 92 | #define _FOREACH_WORD(word, length, s, separator, quoted, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (quoted)); (word); (word) = split(&(state ), &(length), (separator), (quoted))) \ | |||
| 93 | for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) | |||
| 94 | ||||
| 95 | char *strappend(const char *s, const char *suffix); | |||
| 96 | char *strnappend(const char *s, const char *suffix, size_t length); | |||
| 97 | ||||
| 98 | char *strjoin_real(const char *x, ...) _sentinel___attribute__ ((sentinel)); | |||
| 99 | #define strjoin(a, ...)strjoin_real((a), ..., ((void*)0)) strjoin_real((a), __VA_ARGS__, NULL((void*)0)) | |||
| 100 | ||||
| 101 | #define strjoina(a, ...)({ const char *_appendees_[] = { a, ... }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ ( __builtin_choose_expr( !__builtin_types_compatible_p(typeof(_appendees_ ), typeof(&*(_appendees_))), sizeof(_appendees_)/sizeof(( _appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_ ++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }) \ | |||
| 102 | ({ \ | |||
| 103 | const char *_appendees_[] = { a, __VA_ARGS__ }; \ | |||
| 104 | char *_d_, *_p_; \ | |||
| 105 | size_t _len_ = 0; \ | |||
| 106 | size_t _i_; \ | |||
| 107 | for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_++) \ | |||
| 108 | _len_ += strlen(_appendees_[_i_]); \ | |||
| 109 | _p_ = _d_ = alloca(_len_ + 1)__builtin_alloca (_len_ + 1); \ | |||
| 110 | for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_++) \ | |||
| 111 | _p_ = stpcpy(_p_, _appendees_[_i_]); \ | |||
| 112 | *_p_ = 0; \ | |||
| 113 | _d_; \ | |||
| 114 | }) | |||
| 115 | ||||
| 116 | char *strstrip(char *s); | |||
| 117 | char *delete_chars(char *s, const char *bad); | |||
| 118 | char *delete_trailing_chars(char *s, const char *bad); | |||
| 119 | char *truncate_nl(char *s); | |||
| 120 | ||||
| 121 | static inline char *skip_leading_chars(const char *s, const char *bad) { | |||
| 122 | ||||
| 123 | if (!s) | |||
| 124 | return NULL((void*)0); | |||
| 125 | ||||
| 126 | if (!bad) | |||
| 127 | bad = WHITESPACE" \t\n\r"; | |||
| 128 | ||||
| 129 | return (char*) s + strspn(s, bad); | |||
| 130 | } | |||
| 131 | ||||
| 132 | char ascii_tolower(char x); | |||
| 133 | char *ascii_strlower(char *s); | |||
| 134 | char *ascii_strlower_n(char *s, size_t n); | |||
| 135 | ||||
| 136 | char ascii_toupper(char x); | |||
| 137 | char *ascii_strupper(char *s); | |||
| 138 | ||||
| 139 | int ascii_strcasecmp_n(const char *a, const char *b, size_t n); | |||
| 140 | int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); | |||
| 141 | ||||
| 142 | bool_Bool chars_intersect(const char *a, const char *b) _pure___attribute__ ((pure)); | |||
| 143 | ||||
| 144 | static inline bool_Bool _pure___attribute__ ((pure)) in_charset(const char *s, const char* charset) { | |||
| 145 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/basic/string-util.h", 145, __PRETTY_FUNCTION__); } while (0); | |||
| 146 | assert(charset)do { if ((__builtin_expect(!!(!(charset)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("charset"), "../src/basic/string-util.h" , 146, __PRETTY_FUNCTION__); } while (0); | |||
| 147 | return s[strspn(s, charset)] == '\0'; | |||
| 148 | } | |||
| 149 | ||||
| 150 | bool_Bool string_has_cc(const char *p, const char *ok) _pure___attribute__ ((pure)); | |||
| 151 | ||||
| 152 | char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); | |||
| 153 | static inline char *ellipsize(const char *s, size_t length, unsigned percent) { | |||
| 154 | return ellipsize_mem(s, strlen(s), length, percent); | |||
| 155 | } | |||
| 156 | ||||
| 157 | char *cellescape(char *buf, size_t len, const char *s); | |||
| 158 | ||||
| 159 | /* This limit is arbitrary, enough to give some idea what the string contains */ | |||
| 160 | #define CELLESCAPE_DEFAULT_LENGTH64 64 | |||
| 161 | ||||
| 162 | bool_Bool nulstr_contains(const char *nulstr, const char *needle); | |||
| 163 | ||||
| 164 | char* strshorten(char *s, size_t l); | |||
| 165 | ||||
| 166 | char *strreplace(const char *text, const char *old_string, const char *new_string); | |||
| 167 | ||||
| 168 | char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); | |||
| 169 | ||||
| 170 | char *strextend_with_separator(char **x, const char *separator, ...) _sentinel___attribute__ ((sentinel)); | |||
| 171 | ||||
| 172 | #define strextend(x, ...)strextend_with_separator(x, ((void*)0), ...) strextend_with_separator(x, NULL((void*)0), __VA_ARGS__) | |||
| 173 | ||||
| 174 | char *strrep(const char *s, unsigned n); | |||
| 175 | ||||
| 176 | int split_pair(const char *s, const char *sep, char **l, char **r); | |||
| 177 | ||||
| 178 | int free_and_strdup(char **p, const char *s); | |||
| 179 | int free_and_strndup(char **p, const char *s, size_t l); | |||
| 180 | ||||
| 181 | /* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ | |||
| 182 | static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { | |||
| 183 | ||||
| 184 | if (needlelen <= 0) | |||
| 185 | return (void*) haystack; | |||
| 186 | ||||
| 187 | if (haystacklen < needlelen) | |||
| 188 | return NULL((void*)0); | |||
| 189 | ||||
| 190 | assert(haystack)do { if ((__builtin_expect(!!(!(haystack)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("haystack"), "../src/basic/string-util.h" , 190, __PRETTY_FUNCTION__); } while (0); | |||
| 191 | assert(needle)do { if ((__builtin_expect(!!(!(needle)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("needle"), "../src/basic/string-util.h", 191, __PRETTY_FUNCTION__); } while (0); | |||
| 192 | ||||
| 193 | return memmem(haystack, haystacklen, needle, needlelen); | |||
| 194 | } | |||
| 195 | ||||
| 196 | #if !HAVE_EXPLICIT_BZERO1 | |||
| 197 | void explicit_bzero(void *p, size_t l); | |||
| 198 | #endif | |||
| 199 | ||||
| 200 | char *string_erase(char *x); | |||
| 201 | ||||
| 202 | char *string_free_erase(char *s); | |||
| 203 | DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase)static inline void string_free_erasep(char * *p) { if (*p) string_free_erase (*p); }; | |||
| 204 | #define _cleanup_string_free_erase___attribute__((cleanup(string_free_erasep))) _cleanup_(string_free_erasep)__attribute__((cleanup(string_free_erasep))) | |||
| 205 | ||||
| 206 | bool_Bool string_is_safe(const char *p) _pure___attribute__ ((pure)); | |||
| 207 | ||||
| 208 | static inline size_t strlen_ptr(const char *s) { | |||
| 209 | if (!s) | |||
| 210 | return 0; | |||
| 211 | ||||
| 212 | return strlen(s); | |||
| 213 | } | |||
| 214 | ||||
| 215 | /* Like startswith(), but operates on arbitrary memory blocks */ | |||
| 216 | static inline void *memory_startswith(const void *p, size_t sz, const char *token) { | |||
| 217 | size_t n; | |||
| 218 | ||||
| 219 | assert(token)do { if ((__builtin_expect(!!(!(token)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("token"), "../src/basic/string-util.h", 219 , __PRETTY_FUNCTION__); } while (0); | |||
| 220 | ||||
| 221 | n = strlen(token); | |||
| 222 | if (sz < n) | |||
| 223 | return NULL((void*)0); | |||
| 224 | ||||
| 225 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/string-util.h", 225, __PRETTY_FUNCTION__); } while (0); | |||
| 226 | ||||
| 227 | if (memcmp(p, token, n) != 0) | |||
| 228 | return NULL((void*)0); | |||
| 229 | ||||
| 230 | return (uint8_t*) p + n; | |||
| 231 | } |