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 | } |