]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-id128/id128-util.c
sd-id128: move get_boot_id_for_machine() to id128-util.c
[thirdparty/systemd.git] / src / libsystemd / sd-id128 / id128-util.c
CommitLineData
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
21int 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 38bool 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 66int 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 138int 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 151int 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 184int 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 197void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
c01a5c05 198 siphash24_compress_typesafe(*p, state);
4b58153d
LP
199}
200
7a08d314 201int 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
205sd_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 218DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
3e61656f 219DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free);
b4be4ff8
LP
220
221int 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
256sd_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
274int 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}