]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
910fd145 | 2 | |
dccca82b | 3 | #include <errno.h> |
910fd145 | 4 | #include <fcntl.h> |
15b1248a | 5 | #include <unistd.h> |
910fd145 LP |
6 | |
7 | #include "fd-util.h" | |
0f44b766 | 8 | #include "fs-util.h" |
910fd145 LP |
9 | #include "hexdecoct.h" |
10 | #include "id128-util.h" | |
11 | #include "io-util.h" | |
8e976dc9 YW |
12 | #include "namespace-util.h" |
13 | #include "process-util.h" | |
0408d802 | 14 | #include "sha256.h" |
910fd145 | 15 | #include "stdio-util.h" |
80851148 | 16 | #include "string-util.h" |
f789041c | 17 | #include "strv.h" |
bf819d3a | 18 | #include "sync-util.h" |
5ee5b165 | 19 | #include "virt.h" |
910fd145 | 20 | |
7c9de5d8 ZJS |
21 | int id128_from_string_nonzero(const char *s, sd_id128_t *ret) { |
22 | sd_id128_t t; | |
23 | int r; | |
24 | ||
25 | assert(ret); | |
26 | ||
27 | r = sd_id128_from_string(ASSERT_PTR(s), &t); | |
28 | if (r < 0) | |
29 | return r; | |
30 | ||
31 | if (sd_id128_is_null(t)) | |
32 | return -ENXIO; | |
33 | ||
34 | *ret = t; | |
35 | return 0; | |
36 | } | |
37 | ||
910fd145 | 38 | bool id128_is_valid(const char *s) { |
28bf2de2 | 39 | size_t l; |
910fd145 LP |
40 | |
41 | assert(s); | |
42 | ||
43 | l = strlen(s); | |
910fd145 | 44 | |
28bf2de2 | 45 | if (l == SD_ID128_STRING_MAX - 1) |
da890466 | 46 | /* Plain formatted 128-bit hex string */ |
28bf2de2 | 47 | return in_charset(s, HEXDIGITS); |
910fd145 | 48 | |
28bf2de2 | 49 | if (l == SD_ID128_UUID_STRING_MAX - 1) { |
910fd145 | 50 | /* Formatted UUID */ |
28bf2de2 | 51 | for (size_t i = 0; i < l; i++) { |
910fd145 LP |
52 | char c = s[i]; |
53 | ||
945c2931 | 54 | if (IN_SET(i, 8, 13, 18, 23)) { |
910fd145 LP |
55 | if (c != '-') |
56 | return false; | |
28bf2de2 YW |
57 | } else if (!ascii_ishex(c)) |
58 | return false; | |
910fd145 | 59 | } |
28bf2de2 YW |
60 | return true; |
61 | } | |
910fd145 | 62 | |
28bf2de2 | 63 | return false; |
910fd145 LP |
64 | } |
65 | ||
762b78de | 66 | int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) { |
28bf2de2 | 67 | char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ |
52673ef8 | 68 | sd_id128_t id; |
910fd145 | 69 | ssize_t l; |
e8a66254 | 70 | int r; |
910fd145 LP |
71 | |
72 | assert(fd >= 0); | |
910fd145 | 73 | |
da890466 | 74 | /* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both |
65548c58 LP |
75 | * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they |
76 | * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you | |
057bf780 YW |
77 | * accept". |
78 | * | |
79 | * This returns the following: | |
80 | * -ENOMEDIUM: an empty string, | |
81 | * -ENOPKG: "uninitialized" or "uninitialized\n", | |
e8a66254 | 82 | * -EUCLEAN: other invalid strings. */ |
910fd145 | 83 | |
65548c58 | 84 | l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */ |
910fd145 LP |
85 | if (l < 0) |
86 | return (int) l; | |
87 | if (l == 0) /* empty? */ | |
88 | return -ENOMEDIUM; | |
89 | ||
65548c58 | 90 | switch (l) { |
910fd145 | 91 | |
057bf780 YW |
92 | case STRLEN("uninitialized"): |
93 | case STRLEN("uninitialized\n"): | |
94 | return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL; | |
80851148 | 95 | |
28bf2de2 YW |
96 | case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */ |
97 | if (buffer[SD_ID128_STRING_MAX-1] != '\n') | |
e8a66254 | 98 | return -EUCLEAN; |
910fd145 | 99 | |
4831981d | 100 | _fallthrough_; |
28bf2de2 | 101 | case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */ |
057bf780 | 102 | if (!FLAGS_SET(f, ID128_FORMAT_PLAIN)) |
e8a66254 | 103 | return -EUCLEAN; |
65548c58 | 104 | |
28bf2de2 | 105 | buffer[SD_ID128_STRING_MAX-1] = 0; |
65548c58 | 106 | break; |
910fd145 | 107 | |
28bf2de2 YW |
108 | case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */ |
109 | if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n') | |
e8a66254 | 110 | return -EUCLEAN; |
910fd145 | 111 | |
4831981d | 112 | _fallthrough_; |
28bf2de2 | 113 | case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */ |
057bf780 | 114 | if (!FLAGS_SET(f, ID128_FORMAT_UUID)) |
e8a66254 | 115 | return -EUCLEAN; |
910fd145 | 116 | |
28bf2de2 | 117 | buffer[SD_ID128_UUID_STRING_MAX-1] = 0; |
65548c58 LP |
118 | break; |
119 | ||
120 | default: | |
e8a66254 | 121 | return -EUCLEAN; |
65548c58 | 122 | } |
910fd145 | 123 | |
52673ef8 YW |
124 | r = sd_id128_from_string(buffer, &id); |
125 | if (r == -EINVAL) | |
126 | return -EUCLEAN; | |
127 | if (r < 0) | |
128 | return r; | |
129 | ||
130 | if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) | |
131 | return -ENOMEDIUM; | |
132 | ||
133 | if (ret) | |
134 | *ret = id; | |
135 | return 0; | |
910fd145 LP |
136 | } |
137 | ||
0f44b766 | 138 | int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) { |
254d1313 | 139 | _cleanup_close_ int fd = -EBADF; |
910fd145 | 140 | |
0f44b766 | 141 | assert(dir_fd >= 0 || dir_fd == AT_FDCWD); |
169d91b7 YW |
142 | assert(path); |
143 | ||
e40b11be | 144 | fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY); |
910fd145 | 145 | if (fd < 0) |
0f44b766 | 146 | return fd; |
910fd145 LP |
147 | |
148 | return id128_read_fd(fd, f, ret); | |
149 | } | |
150 | ||
762b78de | 151 | int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) { |
28bf2de2 | 152 | char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ |
910fd145 | 153 | size_t sz; |
15b1248a | 154 | int r; |
910fd145 LP |
155 | |
156 | assert(fd >= 0); | |
057bf780 | 157 | assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID)); |
910fd145 | 158 | |
52673ef8 YW |
159 | if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) |
160 | return -ENOMEDIUM; | |
161 | ||
057bf780 | 162 | if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) { |
b7416360 | 163 | assert_se(sd_id128_to_string(id, buffer)); |
b7416360 | 164 | sz = SD_ID128_STRING_MAX; |
910fd145 | 165 | } else { |
b7416360 | 166 | assert_se(sd_id128_to_uuid_string(id, buffer)); |
b7416360 | 167 | sz = SD_ID128_UUID_STRING_MAX; |
910fd145 LP |
168 | } |
169 | ||
28bf2de2 | 170 | buffer[sz - 1] = '\n'; |
e22c60a9 | 171 | r = loop_write(fd, buffer, sz); |
15b1248a LP |
172 | if (r < 0) |
173 | return r; | |
174 | ||
b40c8ebd | 175 | if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) { |
bf819d3a | 176 | r = fsync_full(fd); |
8ac2f74f LP |
177 | if (r < 0) |
178 | return r; | |
15b1248a LP |
179 | } |
180 | ||
8ac2f74f | 181 | return 0; |
910fd145 LP |
182 | } |
183 | ||
c227c46b | 184 | int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) { |
254d1313 | 185 | _cleanup_close_ int fd = -EBADF; |
910fd145 | 186 | |
c227c46b | 187 | assert(dir_fd >= 0 || dir_fd == AT_FDCWD); |
169d91b7 YW |
188 | assert(path); |
189 | ||
e40b11be | 190 | fd = xopenat_full(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444); |
910fd145 | 191 | if (fd < 0) |
c227c46b | 192 | return fd; |
910fd145 | 193 | |
b40c8ebd | 194 | return id128_write_fd(fd, f, id); |
910fd145 | 195 | } |
4b58153d | 196 | |
7a08d314 | 197 | void id128_hash_func(const sd_id128_t *p, struct siphash *state) { |
c01a5c05 | 198 | siphash24_compress_typesafe(*p, state); |
4b58153d LP |
199 | } |
200 | ||
7a08d314 | 201 | int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) { |
c01a5c05 | 202 | return memcmp(a, b, sizeof(sd_id128_t)); |
4b58153d LP |
203 | } |
204 | ||
1293a168 LP |
205 | sd_id128_t id128_make_v4_uuid(sd_id128_t id) { |
206 | /* Stolen from generate_random_uuid() of drivers/char/random.c | |
207 | * in the kernel sources */ | |
208 | ||
209 | /* Set UUID version to 4 --- truly random generation */ | |
210 | id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; | |
211 | ||
212 | /* Set the UUID variant to DCE */ | |
213 | id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; | |
214 | ||
215 | return id; | |
216 | } | |
217 | ||
7a08d314 | 218 | DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); |
3e61656f | 219 | DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free); |
b4be4ff8 LP |
220 | |
221 | int id128_get_product(sd_id128_t *ret) { | |
222 | sd_id128_t uuid; | |
223 | int r; | |
224 | ||
225 | assert(ret); | |
226 | ||
227 | /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is | |
228 | * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ | |
229 | ||
5ee5b165 LP |
230 | r = detect_container(); |
231 | if (r < 0) | |
232 | return r; | |
233 | if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but | |
234 | * of the host */ | |
235 | return -ENOENT; | |
236 | ||
f789041c MY |
237 | FOREACH_STRING(i, |
238 | "/sys/class/dmi/id/product_uuid", /* KVM */ | |
239 | "/proc/device-tree/vm,uuid", /* Device tree */ | |
240 | "/sys/hypervisor/uuid") { /* Xen */ | |
241 | ||
242 | r = id128_read(i, ID128_FORMAT_UUID, &uuid); | |
243 | if (r != -ENOENT) | |
244 | break; | |
245 | } | |
b4be4ff8 LP |
246 | if (r < 0) |
247 | return r; | |
248 | ||
249 | if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid)) | |
250 | return -EADDRNOTAVAIL; /* Recognizable error */ | |
251 | ||
252 | *ret = uuid; | |
253 | return 0; | |
254 | } | |
0408d802 LP |
255 | |
256 | sd_id128_t id128_digest(const void *data, size_t size) { | |
257 | assert(data || size == 0); | |
258 | ||
259 | /* Hashes a UUID from some arbitrary data */ | |
260 | ||
261 | if (size == SIZE_MAX) | |
262 | size = strlen(data); | |
263 | ||
264 | uint8_t h[SHA256_DIGEST_SIZE]; | |
265 | sd_id128_t id; | |
266 | ||
267 | /* Take the first half of the SHA256 result */ | |
268 | assert_cc(sizeof(h) >= sizeof(id.bytes)); | |
269 | memcpy(id.bytes, sha256_direct(data, size, h), sizeof(id.bytes)); | |
270 | ||
271 | return id128_make_v4_uuid(id); | |
272 | } | |
8e976dc9 YW |
273 | |
274 | int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret) { | |
275 | _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF; | |
276 | _cleanup_close_pair_ int pair[2] = EBADF_PAIR; | |
277 | pid_t pid, child; | |
278 | sd_id128_t id; | |
279 | ssize_t k; | |
280 | int r; | |
281 | ||
282 | assert(ret); | |
283 | ||
284 | if (isempty(machine)) | |
285 | return sd_id128_get_boot(ret); | |
286 | ||
287 | r = container_get_leader(machine, &pid); | |
288 | if (r < 0) | |
289 | return r; | |
290 | ||
291 | r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd); | |
292 | if (r < 0) | |
293 | return r; | |
294 | ||
295 | if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) | |
296 | return -errno; | |
297 | ||
298 | r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, | |
299 | pidnsfd, mntnsfd, -1, -1, rootfd, &child); | |
300 | if (r < 0) | |
301 | return r; | |
302 | if (r == 0) { | |
303 | pair[0] = safe_close(pair[0]); | |
304 | ||
305 | r = id128_get_boot(&id); | |
306 | if (r < 0) | |
307 | _exit(EXIT_FAILURE); | |
308 | ||
309 | k = send(pair[1], &id, sizeof(id), MSG_NOSIGNAL); | |
310 | if (k != sizeof(id)) | |
311 | _exit(EXIT_FAILURE); | |
312 | ||
313 | _exit(EXIT_SUCCESS); | |
314 | } | |
315 | ||
316 | pair[1] = safe_close(pair[1]); | |
317 | ||
318 | r = wait_for_terminate_and_check("(sd-bootidns)", child, 0); | |
319 | if (r < 0) | |
320 | return r; | |
321 | if (r != EXIT_SUCCESS) | |
322 | return -EIO; | |
323 | ||
324 | k = recv(pair[0], &id, sizeof(id), 0); | |
325 | if (k != sizeof(id)) | |
326 | return -EIO; | |
327 | ||
328 | if (sd_id128_is_null(id)) | |
329 | return -EIO; | |
330 | ||
331 | *ret = id; | |
332 | return 0; | |
333 | } |