chiark / gitweb /
Introduce loop_read_exact helper
[elogind.git] / src / libsystemd / 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 "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         unsigned j;
112         sd_id128_t t;
113         int r;
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         r = loop_read_exact(fd, buf, 33, false);
127         if (r < 0)
128                 return r;
129         if (buf[32] !='\n')
130                 return -EIO;
131
132         for (j = 0; j < 16; j++) {
133                 int a, b;
134
135                 a = unhexchar(buf[j*2]);
136                 b = unhexchar(buf[j*2+1]);
137
138                 if (a < 0 || b < 0)
139                         return -EIO;
140
141                 t.bytes[j] = a << 4 | b;
142         }
143
144         saved_machine_id = t;
145         saved_machine_id_valid = true;
146
147         *ret = t;
148         return 0;
149 }
150
151 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
152         static thread_local sd_id128_t saved_boot_id;
153         static thread_local bool saved_boot_id_valid = false;
154         _cleanup_close_ int fd = -1;
155         char buf[36];
156         unsigned j;
157         sd_id128_t t;
158         char *p;
159         int r;
160
161         assert_return(ret, -EINVAL);
162
163         if (saved_boot_id_valid) {
164                 *ret = saved_boot_id;
165                 return 0;
166         }
167
168         fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
169         if (fd < 0)
170                 return -errno;
171
172         r = loop_read_exact(fd, buf, 36, false);
173         if (r < 0)
174                 return r;
175
176         for (j = 0, p = buf; j < 16; j++) {
177                 int a, b;
178
179                 if (p >= buf + 35)
180                         return -EIO;
181
182                 if (*p == '-') {
183                         p++;
184                         if (p >= buf + 35)
185                                 return -EIO;
186                 }
187
188                 a = unhexchar(p[0]);
189                 b = unhexchar(p[1]);
190
191                 if (a < 0 || b < 0)
192                         return -EIO;
193
194                 t.bytes[j] = a << 4 | b;
195
196                 p += 2;
197         }
198
199         saved_boot_id = t;
200         saved_boot_id_valid = true;
201
202         *ret = t;
203         return 0;
204 }
205
206 _public_ int sd_id128_randomize(sd_id128_t *ret) {
207         sd_id128_t t;
208         int r;
209
210         assert_return(ret, -EINVAL);
211
212         r = dev_urandom(&t, sizeof(t));
213         if (r < 0)
214                 return r;
215
216         /* Turn this into a valid v4 UUID, to be nice. Note that we
217          * only guarantee this for newly generated UUIDs, not for
218          * pre-existing ones. */
219
220         *ret = make_v4_uuid(t);
221         return 0;
222 }