chiark / gitweb /
9ee40ab91eaaee64a3f13dc61dc354bc8de26400
[elogind.git] / src / libsystemd-id128 / sd-id128.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25
26 #include "util.h"
27 #include "macro.h"
28 #include "sd-id128.h"
29
30 _public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) {
31         unsigned n;
32
33         assert_return(s, NULL);
34
35         for (n = 0; n < 16; n++) {
36                 s[n*2] = hexchar(id.bytes[n] >> 4);
37                 s[n*2+1] = hexchar(id.bytes[n] & 0xF);
38         }
39
40         s[32] = 0;
41
42         return s;
43 }
44
45 _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
46         unsigned n, i;
47         sd_id128_t t;
48         bool is_guid = false;
49
50         assert_return(s, -EINVAL);
51         assert_return(ret, -EINVAL);
52
53         for (n = 0, i = 0; n < 16;) {
54                 int a, b;
55
56                 if (s[i] == '-') {
57                         /* Is this a GUID? Then be nice, and skip over
58                          * the dashes */
59
60                         if (i == 8)
61                                 is_guid = true;
62                         else if (i == 13 || i == 18 || i == 23) {
63                                 if (!is_guid)
64                                         return -EINVAL;
65                         } else
66                                 return -EINVAL;
67
68                         i++;
69                         continue;
70                 }
71
72                 a = unhexchar(s[i++]);
73                 if (a < 0)
74                         return -EINVAL;
75
76                 b = unhexchar(s[i++]);
77                 if (b < 0)
78                         return -EINVAL;
79
80                 t.bytes[n++] = (a << 4) | b;
81         }
82
83         if (i != (is_guid ? 36 : 32))
84                 return -EINVAL;
85
86         if (s[i] != 0)
87                 return -EINVAL;
88
89         *ret = t;
90         return 0;
91 }
92
93 static sd_id128_t make_v4_uuid(sd_id128_t id) {
94         /* Stolen from generate_random_uuid() of drivers/char/random.c
95          * in the kernel sources */
96
97         /* Set UUID version to 4 --- truly random generation */
98         id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
99
100         /* Set the UUID variant to DCE */
101         id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
102
103         return id;
104 }
105
106 _public_ int sd_id128_get_machine(sd_id128_t *ret) {
107         static thread_local sd_id128_t saved_machine_id;
108         static thread_local bool saved_machine_id_valid = false;
109         _cleanup_close_ int fd = -1;
110         char buf[33];
111         ssize_t k;
112         unsigned j;
113         sd_id128_t t;
114
115         assert_return(ret, -EINVAL);
116
117         if (saved_machine_id_valid) {
118                 *ret = saved_machine_id;
119                 return 0;
120         }
121
122         fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
123         if (fd < 0)
124                 return -errno;
125
126         k = loop_read(fd, buf, 33, false);
127         if (k < 0)
128                 return (int) k;
129
130         if (k != 33)
131                 return -EIO;
132
133         if (buf[32] !='\n')
134                 return -EIO;
135
136         for (j = 0; j < 16; j++) {
137                 int a, b;
138
139                 a = unhexchar(buf[j*2]);
140                 b = unhexchar(buf[j*2+1]);
141
142                 if (a < 0 || b < 0)
143                         return -EIO;
144
145                 t.bytes[j] = a << 4 | b;
146         }
147
148         saved_machine_id = t;
149         saved_machine_id_valid = true;
150
151         *ret = t;
152         return 0;
153 }
154
155 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
156         static thread_local sd_id128_t saved_boot_id;
157         static thread_local bool saved_boot_id_valid = false;
158         _cleanup_close_ int fd = -1;
159         char buf[36];
160         ssize_t k;
161         unsigned j;
162         sd_id128_t t;
163         char *p;
164
165         assert_return(ret, -EINVAL);
166
167         if (saved_boot_id_valid) {
168                 *ret = saved_boot_id;
169                 return 0;
170         }
171
172         fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
173         if (fd < 0)
174                 return -errno;
175
176         k = loop_read(fd, buf, 36, false);
177         if (k < 0)
178                 return (int) k;
179
180         if (k != 36)
181                 return -EIO;
182
183         for (j = 0, p = buf; j < 16; j++) {
184                 int a, b;
185
186                 if (p >= buf + k)
187                         return -EIO;
188
189                 if (*p == '-')
190                         p++;
191
192                 a = unhexchar(p[0]);
193                 b = unhexchar(p[1]);
194
195                 if (a < 0 || b < 0)
196                         return -EIO;
197
198                 t.bytes[j] = a << 4 | b;
199
200                 p += 2;
201         }
202
203         saved_boot_id = t;
204         saved_boot_id_valid = true;
205
206         *ret = t;
207         return 0;
208 }
209
210 _public_ int sd_id128_randomize(sd_id128_t *ret) {
211         _cleanup_close_ int fd = -1;
212         sd_id128_t t;
213         ssize_t k;
214
215         assert_return(ret, -EINVAL);
216
217         fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
218         if (fd < 0)
219                 return -errno;
220
221         k = loop_read(fd, &t, 16, false);
222         if (k < 0)
223                 return (int) k;
224
225         if (k != 16)
226                 return -EIO;
227
228         /* Turn this into a valid v4 UUID, to be nice. Note that we
229          * only guarantee this for newly generated UUIDs, not for
230          * pre-existing ones.*/
231
232         *ret = make_v4_uuid(t);
233         return 0;
234 }