Merge branch 'collectd-5.5' into collectd-5.6
[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 "plugin.h"
31
32 #if HAVE_SYS_SYSCTL_H
33 #include <sys/sysctl.h>
34 #endif
35
36 #if HAVE_LIBHAL_H
37 #include <libhal.h>
38 #endif
39
40 #define UUID_RAW_LENGTH 16
41 #define UUID_PRINTABLE_COMPACT_LENGTH (UUID_RAW_LENGTH * 2)
42 #define UUID_PRINTABLE_NORMAL_LENGTH (UUID_PRINTABLE_COMPACT_LENGTH + 4)
43
44 static char *uuidfile = NULL;
45
46 static const char *config_keys[] = {"UUIDFile"};
47
48 static int looks_like_a_uuid(const char *uuid) {
49   int len;
50
51   if (!uuid)
52     return (0);
53
54   len = strlen(uuid);
55
56   if (len < UUID_PRINTABLE_COMPACT_LENGTH)
57     return (0);
58
59   while (*uuid) {
60     if (!isxdigit((int)*uuid) && *uuid != '-')
61       return (0);
62     uuid++;
63   }
64   return (1);
65 }
66
67 static char *uuid_parse_dmidecode(FILE *file) {
68   char line[1024];
69
70   while (fgets(line, sizeof(line), file) != NULL) {
71     char *fields[4];
72     int fields_num;
73
74     strstripnewline(line);
75
76     /* Look for a line reading:
77      *   UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
78      */
79     fields_num = strsplit(line, fields, STATIC_ARRAY_SIZE(fields));
80     if (fields_num != 2)
81       continue;
82
83     if (strcmp("UUID:", fields[0]) != 0)
84       continue;
85
86     if (!looks_like_a_uuid(fields[1]))
87       continue;
88
89     return (strdup(fields[1]));
90   }
91   return (NULL);
92 }
93
94 static char *uuid_get_from_dmidecode(void) {
95   FILE *dmidecode = popen("dmidecode -t system 2>/dev/null", "r");
96   char *uuid;
97
98   if (!dmidecode)
99     return (NULL);
100
101   uuid = uuid_parse_dmidecode(dmidecode);
102
103   pclose(dmidecode);
104   return (uuid);
105 }
106
107 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
108 static char *uuid_get_from_sysctlbyname(const char *name) {
109   char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1];
110   size_t len = sizeof(uuid);
111   if (sysctlbyname(name, &uuid, &len, NULL, 0) == -1)
112     return NULL;
113   return (strdup(uuid));
114 }
115 #elif defined(__OpenBSD__)
116 static char *uuid_get_from_sysctl(void) {
117   char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1];
118   size_t len = sizeof(uuid);
119   int mib[2];
120
121   mib[0] = CTL_HW;
122   mib[1] = HW_UUID;
123
124   if (sysctl(mib, 2, uuid, &len, NULL, 0) == -1)
125     return NULL;
126   return (strdup(uuid));
127 }
128 #endif
129
130 #if HAVE_LIBHAL_H
131
132 #define UUID_PATH "/org/freedesktop/Hal/devices/computer"
133 #define UUID_PROPERTY "smbios.system.uuid"
134
135 static char *uuid_get_from_hal(void) {
136   LibHalContext *ctx;
137
138   DBusError error;
139   DBusConnection *con;
140
141   dbus_error_init(&error);
142
143   if (!(con = dbus_bus_get(DBUS_BUS_SYSTEM, &error)))
144     goto bailout_nobus;
145
146   ctx = libhal_ctx_new();
147   libhal_ctx_set_dbus_connection(ctx, con);
148
149   if (!libhal_ctx_init(ctx, &error))
150     goto bailout;
151
152   if (!libhal_device_property_exists(ctx, UUID_PATH, UUID_PROPERTY, &error))
153     goto bailout;
154
155   char *uuid =
156       libhal_device_get_property_string(ctx, UUID_PATH, UUID_PROPERTY, &error);
157   if (looks_like_a_uuid(uuid))
158     return (uuid);
159
160 bailout : {
161   DBusError ctxerror;
162   dbus_error_init(&ctxerror);
163   if (!(libhal_ctx_shutdown(ctx, &ctxerror)))
164     dbus_error_free(&ctxerror);
165 }
166
167   libhal_ctx_free(ctx);
168
169 bailout_nobus:
170   if (dbus_error_is_set(&error))
171     dbus_error_free(&error);
172   return (NULL);
173 }
174 #endif
175
176 static char *uuid_get_from_file(const char *path) {
177   FILE *file;
178   char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1] = "";
179
180   file = fopen(path, "r");
181   if (file == NULL)
182     return (NULL);
183
184   if (!fgets(uuid, sizeof(uuid), file)) {
185     fclose(file);
186     return (NULL);
187   }
188   fclose(file);
189   strstripnewline(uuid);
190
191   return (strdup(uuid));
192 }
193
194 static char *uuid_get_local(void) {
195   char *uuid;
196
197   /* Check /etc/uuid / UUIDFile before any other method. */
198   if ((uuid = uuid_get_from_file(uuidfile ? uuidfile : "/etc/uuid")) != NULL)
199     return (uuid);
200
201 #if defined(__APPLE__)
202   if ((uuid = uuid_get_from_sysctlbyname("kern.uuid")) != NULL)
203     return (uuid);
204 #elif defined(__FreeBSD__)
205   if ((uuid = uuid_get_from_sysctlbyname("kern.hostuuid")) != NULL)
206     return (uuid);
207 #elif defined(__NetBSD__)
208   if ((uuid = uuid_get_from_sysctlbyname("machdep.dmi.system-uuid")) != NULL)
209     return (uuid);
210 #elif defined(__OpenBSD__)
211   if ((uuid = uuid_get_from_sysctl()) != NULL)
212     return (uuid);
213 #elif defined(__linux__)
214   if ((uuid = uuid_get_from_file("/sys/class/dmi/id/product_uuid")) != NULL)
215     return (uuid);
216 #endif
217
218 #if HAVE_LIBHAL_H
219   if ((uuid = uuid_get_from_hal()) != NULL)
220     return (uuid);
221 #endif
222
223   if ((uuid = uuid_get_from_dmidecode()) != NULL)
224     return (uuid);
225
226 #if defined(__linux__)
227   if ((uuid = uuid_get_from_file("/sys/hypervisor/uuid")) != NULL)
228     return (uuid);
229 #endif
230
231   return (NULL);
232 }
233
234 static int uuid_config(const char *key, const char *value) {
235   if (strcasecmp(key, "UUIDFile") == 0) {
236     char *tmp = strdup(value);
237     if (tmp == NULL)
238       return (-1);
239     sfree(uuidfile);
240     uuidfile = tmp;
241     return (0);
242   }
243
244   return (1);
245 }
246
247 static int uuid_init(void) {
248   char *uuid = uuid_get_local();
249
250   if (uuid) {
251     sstrncpy(hostname_g, uuid, DATA_MAX_NAME_LEN);
252     sfree(uuid);
253     return (0);
254   }
255
256   WARNING("uuid: could not read UUID using any known method");
257   return (0);
258 }
259
260 void module_register(void) {
261   plugin_register_config("uuid", uuid_config, config_keys,
262                          STATIC_ARRAY_SIZE(config_keys));
263   plugin_register_init("uuid", uuid_init);
264 }
265
266 /*
267  * vim: set tabstop=4:
268  * vim: set shiftwidth=4:
269  * vim: set expandtab:
270  */
271 /*
272  * Local variables:
273  *  indent-tabs-mode: nil
274  *  c-indent-level: 4
275  *  c-basic-offset: 4
276  *  tab-width: 4
277  * End:
278  */