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