chiark / gitweb /
406a7f0a64683cb3503b0817567e15856e955a56
[elogind.git] / src / libelogind / sd-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 "sd-id128.h"
27
28 #include "fd-util.h"
29 #include "hexdecoct.h"
30 #include "io-util.h"
31 #include "macro.h"
32  #include "random-util.h"
33 #include "util.h"
34
35 _public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
36         unsigned n;
37
38         assert_return(s, NULL);
39
40         for (n = 0; n < 16; n++) {
41                 s[n*2] = hexchar(id.bytes[n] >> 4);
42                 s[n*2+1] = hexchar(id.bytes[n] & 0xF);
43         }
44
45         s[32] = 0;
46
47         return s;
48 }
49
50 _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
51         unsigned n, i;
52         sd_id128_t t;
53         bool is_guid = false;
54
55         assert_return(s, -EINVAL);
56         assert_return(ret, -EINVAL);
57
58         for (n = 0, i = 0; n < 16;) {
59                 int a, b;
60
61                 if (s[i] == '-') {
62                         /* Is this a GUID? Then be nice, and skip over
63                          * the dashes */
64
65                         if (i == 8)
66                                 is_guid = true;
67                         else if (i == 13 || i == 18 || i == 23) {
68                                 if (!is_guid)
69                                         return -EINVAL;
70                         } else
71                                 return -EINVAL;
72
73                         i++;
74                         continue;
75                 }
76
77                 a = unhexchar(s[i++]);
78                 if (a < 0)
79                         return -EINVAL;
80
81                 b = unhexchar(s[i++]);
82                 if (b < 0)
83                         return -EINVAL;
84
85                 t.bytes[n++] = (a << 4) | b;
86         }
87
88         if (i != (is_guid ? 36 : 32))
89                 return -EINVAL;
90
91         if (s[i] != 0)
92                 return -EINVAL;
93
94         *ret = t;
95         return 0;
96 }
97
98 static sd_id128_t make_v4_uuid(sd_id128_t id) {
99         /* Stolen from generate_random_uuid() of drivers/char/random.c
100          * in the kernel sources */
101
102         /* Set UUID version to 4 --- truly random generation */
103         id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
104
105         /* Set the UUID variant to DCE */
106         id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
107
108         return id;
109 }
110
111 _public_ int sd_id128_get_machine(sd_id128_t *ret) {
112         static thread_local sd_id128_t saved_machine_id;
113         static thread_local bool saved_machine_id_valid = false;
114         _cleanup_close_ int fd = -1;
115         char buf[33];
116         unsigned j;
117         sd_id128_t t;
118         int r;
119
120         assert_return(ret, -EINVAL);
121
122         if (saved_machine_id_valid) {
123                 *ret = saved_machine_id;
124                 return 0;
125         }
126
127         fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
128         if (fd < 0)
129                 return -errno;
130
131         r = loop_read_exact(fd, buf, 33, false);
132         if (r < 0)
133                 return r;
134         if (buf[32] !='\n')
135                 return -EIO;
136
137         for (j = 0; j < 16; j++) {
138                 int a, b;
139
140                 a = unhexchar(buf[j*2]);
141                 b = unhexchar(buf[j*2+1]);
142
143                 if (a < 0 || b < 0)
144                         return -EIO;
145
146                 t.bytes[j] = a << 4 | b;
147         }
148
149         saved_machine_id = t;
150         saved_machine_id_valid = true;
151
152         *ret = t;
153         return 0;
154 }
155
156 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
157         static thread_local sd_id128_t saved_boot_id;
158         static thread_local bool saved_boot_id_valid = false;
159         _cleanup_close_ int fd = -1;
160         char buf[36];
161         unsigned j;
162         sd_id128_t t;
163         char *p;
164         int r;
165
166         assert_return(ret, -EINVAL);
167
168         if (saved_boot_id_valid) {
169                 *ret = saved_boot_id;
170                 return 0;
171         }
172
173         fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
174         if (fd < 0)
175                 return -errno;
176
177         r = loop_read_exact(fd, buf, 36, false);
178         if (r < 0)
179                 return r;
180
181         for (j = 0, p = buf; j < 16; j++) {
182                 int a, b;
183
184                 if (p >= buf + 35)
185                         return -EIO;
186
187                 if (*p == '-') {
188                         p++;
189                         if (p >= buf + 35)
190                                 return -EIO;
191                 }
192
193                 a = unhexchar(p[0]);
194                 b = unhexchar(p[1]);
195
196                 if (a < 0 || b < 0)
197                         return -EIO;
198
199                 t.bytes[j] = a << 4 | b;
200
201                 p += 2;
202         }
203
204         saved_boot_id = t;
205         saved_boot_id_valid = true;
206
207         *ret = t;
208         return 0;
209 }
210
211 _public_ int sd_id128_randomize(sd_id128_t *ret) {
212         sd_id128_t t;
213         int r;
214
215         assert_return(ret, -EINVAL);
216
217         r = dev_urandom(&t, sizeof(t));
218         if (r < 0)
219                 return r;
220
221         /* Turn this into a valid v4 UUID, to be nice. Note that we
222          * only guarantee this for newly generated UUIDs, not for
223          * pre-existing ones. */
224
225         *ret = make_v4_uuid(t);
226         return 0;
227 }