Merge remote-tracking branch 'github/pr/1845'
[collectd.git] / src / uuid.c
1 /**
2  * collectd - src/uuid.c
3  * Copyright (C) 2007  Red Hat Inc.
4  * Copyright (C) 2015  Ruben Kerkhof
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Dan Berrange <berrange@redhat.com>
21  *   Richard W.M. Jones <rjones@redhat.com>
22  *
23  * Derived from UUID detection code by Dan Berrange <berrange@redhat.com>
24  * http://hg.et.redhat.com/virt/daemons/spectre--devel?f=f6e3a1b06433;file=lib/uuid.c
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "configfile.h"
31 #include "plugin.h"
32
33 #if HAVE_SYS_SYSCTL_H
34 #include <sys/sysctl.h>
35 #endif
36
37 #if HAVE_LIBHAL_H
38 #include <libhal.h>
39 #endif
40
41 #define UUID_RAW_LENGTH 16
42 #define UUID_PRINTABLE_COMPACT_LENGTH  (UUID_RAW_LENGTH * 2)
43 #define UUID_PRINTABLE_NORMAL_LENGTH  (UUID_PRINTABLE_COMPACT_LENGTH + 4)
44
45 static char *uuidfile = NULL;
46
47 static const char *config_keys[] = {
48     "UUIDFile"
49 };
50
51 static int
52 looks_like_a_uuid (const char *uuid)
53 {
54     int len;
55
56     if (!uuid)
57         return (0);
58
59     len = strlen (uuid);
60
61     if (len < UUID_PRINTABLE_COMPACT_LENGTH)
62         return (0);
63
64     while (*uuid) {
65         if (!isxdigit ((int)*uuid) && *uuid != '-')
66             return (0);
67         uuid++;
68     }
69     return (1);
70 }
71
72 static char *
73 uuid_parse_dmidecode(FILE *file)
74 {
75     char line[1024];
76
77     while (fgets (line, sizeof (line), file) != NULL)
78     {
79         char *fields[4];
80         int fields_num;
81
82         strstripnewline (line);
83
84         /* Look for a line reading:
85          *   UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
86          */
87         fields_num = strsplit (line, fields, STATIC_ARRAY_SIZE (fields));
88         if (fields_num != 2)
89             continue;
90
91         if (strcmp("UUID:", fields[0]) != 0)
92             continue;
93
94         if (!looks_like_a_uuid (fields[1]))
95             continue;
96
97         return (strdup (fields[1]));
98     }
99     return (NULL);
100 }
101
102 static char *
103 uuid_get_from_dmidecode(void)
104 {
105     FILE *dmidecode = popen("dmidecode -t system 2>/dev/null", "r");
106     char *uuid;
107
108     if (!dmidecode)
109         return (NULL);
110
111     uuid = uuid_parse_dmidecode(dmidecode);
112
113     pclose(dmidecode);
114     return (uuid);
115 }
116
117 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
118 static char *
119 uuid_get_from_sysctlbyname(const char *name)
120 {
121     char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1];
122     size_t len = sizeof (uuid);
123     if (sysctlbyname(name, &uuid, &len, NULL, 0) == -1)
124         return NULL;
125     return (strdup (uuid));
126 }
127 #elif defined(__OpenBSD__)
128 static char *
129 uuid_get_from_sysctl(void)
130 {
131     char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1];
132     size_t len = sizeof (uuid);
133     int mib[2];
134
135     mib[0] = CTL_HW;
136     mib[1] = HW_UUID;
137
138     if (sysctl(mib, 2, uuid, &len, NULL, 0) == -1)
139         return NULL;
140     return (strdup (uuid));
141 }
142 #endif
143
144 #if HAVE_LIBHAL_H
145
146 #define UUID_PATH "/org/freedesktop/Hal/devices/computer"
147 #define UUID_PROPERTY "smbios.system.uuid"
148
149 static char *
150 uuid_get_from_hal(void)
151 {
152     LibHalContext *ctx;
153
154     DBusError error;
155     DBusConnection *con;
156
157     dbus_error_init(&error);
158
159     if (!(con = dbus_bus_get(DBUS_BUS_SYSTEM, &error)))
160         goto bailout_nobus;
161
162     ctx = libhal_ctx_new();
163     libhal_ctx_set_dbus_connection(ctx, con);
164
165     if (!libhal_ctx_init(ctx, &error))
166         goto bailout;
167
168     if (!libhal_device_property_exists(ctx,
169                                        UUID_PATH,
170                                        UUID_PROPERTY,
171                                        &error))
172         goto bailout;
173
174     char *uuid  = libhal_device_get_property_string(ctx,
175                                                     UUID_PATH,
176                                                     UUID_PROPERTY,
177                                                     &error);
178     if (looks_like_a_uuid (uuid))
179         return (uuid);
180
181  bailout:
182     {
183         DBusError ctxerror;
184         dbus_error_init(&ctxerror);
185         if (!(libhal_ctx_shutdown(ctx, &ctxerror)))
186             dbus_error_free(&ctxerror);
187     }
188
189     libhal_ctx_free(ctx);
190
191  bailout_nobus:
192     if (dbus_error_is_set(&error))
193         dbus_error_free(&error);
194     return (NULL);
195 }
196 #endif
197
198 static char *
199 uuid_get_from_file(const char *path)
200 {
201     FILE *file;
202     char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1] = "";
203
204     file = fopen (path, "r");
205     if (file == NULL)
206         return (NULL);
207
208     if (!fgets(uuid, sizeof(uuid), file)) {
209         fclose(file);
210         return (NULL);
211     }
212     fclose(file);
213     strstripnewline (uuid);
214
215     return (strdup (uuid));
216 }
217
218 static char *
219 uuid_get_local(void)
220 {
221     char *uuid;
222
223     /* Check /etc/uuid / UUIDFile before any other method. */
224     if ((uuid = uuid_get_from_file(uuidfile ? uuidfile : "/etc/uuid")) != NULL)
225         return (uuid);
226
227 #if defined(__APPLE__)
228     if ((uuid = uuid_get_from_sysctlbyname("kern.uuid")) != NULL)
229         return (uuid);
230 #elif defined(__FreeBSD__)
231     if ((uuid = uuid_get_from_sysctlbyname("kern.hostuuid")) != NULL)
232         return (uuid);
233 #elif defined(__NetBSD__)
234     if ((uuid = uuid_get_from_sysctlbyname("machdep.dmi.system-uuid")) != NULL)
235         return (uuid);
236 #elif defined(__OpenBSD__)
237     if ((uuid = uuid_get_from_sysctl()) != NULL)
238         return (uuid);
239 #elif defined(__linux__)
240     if ((uuid = uuid_get_from_file("/sys/class/dmi/id/product_uuid")) != NULL)
241         return (uuid);
242 #endif
243
244 #if HAVE_LIBHAL_H
245     if ((uuid = uuid_get_from_hal()) != NULL)
246         return (uuid);
247 #endif
248
249     if ((uuid = uuid_get_from_dmidecode()) != NULL)
250         return (uuid);
251
252 #if defined(__linux__)
253     if ((uuid = uuid_get_from_file("/sys/hypervisor/uuid")) != NULL)
254         return (uuid);
255 #endif
256
257     return (NULL);
258 }
259
260 static int
261 uuid_config (const char *key, const char *value)
262 {
263     if (strcasecmp (key, "UUIDFile") == 0) {
264         char *tmp = strdup (value);
265         if (tmp == NULL)
266             return (-1);
267         sfree (uuidfile);
268         uuidfile = tmp;
269         return (0);
270     }
271
272     return (1);
273 }
274
275 static int
276 uuid_init (void)
277 {
278     char *uuid = uuid_get_local ();
279
280     if (uuid) {
281         sstrncpy (hostname_g, uuid, DATA_MAX_NAME_LEN);
282         sfree (uuid);
283         return (0);
284     }
285
286     WARNING ("uuid: could not read UUID using any known method");
287     return (0);
288 }
289
290 void module_register (void)
291 {
292     plugin_register_config ("uuid", uuid_config,
293             config_keys, STATIC_ARRAY_SIZE (config_keys));
294     plugin_register_init ("uuid", uuid_init);
295 }
296
297 /*
298  * vim: set tabstop=4:
299  * vim: set shiftwidth=4:
300  * vim: set expandtab:
301  */
302 /*
303  * Local variables:
304  *  indent-tabs-mode: nil
305  *  c-indent-level: 4
306  *  c-basic-offset: 4
307  *  tab-width: 4
308  * End:
309  */