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