uuid plugin: make it work on NetBSD
[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 #endif
128
129 #if HAVE_LIBHAL_H
130
131 #define UUID_PATH "/org/freedesktop/Hal/devices/computer"
132 #define UUID_PROPERTY "smbios.system.uuid"
133
134 static char *
135 uuid_get_from_hal(void)
136 {
137     LibHalContext *ctx;
138
139     DBusError error;
140     DBusConnection *con;
141
142     dbus_error_init(&error);
143
144     if (!(con = dbus_bus_get(DBUS_BUS_SYSTEM, &error)))
145         goto bailout_nobus;
146
147     ctx = libhal_ctx_new();
148     libhal_ctx_set_dbus_connection(ctx, con);
149
150     if (!libhal_ctx_init(ctx, &error))
151         goto bailout;
152
153     if (!libhal_device_property_exists(ctx,
154                                        UUID_PATH,
155                                        UUID_PROPERTY,
156                                        &error))
157         goto bailout;
158
159     char *uuid  = libhal_device_get_property_string(ctx,
160                                                     UUID_PATH,
161                                                     UUID_PROPERTY,
162                                                     &error);
163     if (looks_like_a_uuid (uuid))
164         return (uuid);
165
166  bailout:
167     {
168         DBusError ctxerror;
169         dbus_error_init(&ctxerror);
170         if (!(libhal_ctx_shutdown(ctx, &ctxerror)))
171             dbus_error_free(&ctxerror);
172     }
173
174     libhal_ctx_free(ctx);
175
176  bailout_nobus:
177     if (dbus_error_is_set(&error))
178         dbus_error_free(&error);
179     return (NULL);
180 }
181 #endif
182
183 static char *
184 uuid_get_from_file(const char *path)
185 {
186     FILE *file;
187     char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1] = "";
188
189     file = fopen (path, "r");
190     if (file == NULL)
191         return (NULL);
192
193     if (!fgets(uuid, sizeof(uuid), file)) {
194         fclose(file);
195         return (NULL);
196     }
197     fclose(file);
198     strstripnewline (uuid);
199
200     return (strdup (uuid));
201 }
202
203 static char *
204 uuid_get_local(void)
205 {
206     char *uuid;
207
208     /* Check /etc/uuid / UUIDFile before any other method. */
209     if ((uuid = uuid_get_from_file(uuidfile ? uuidfile : "/etc/uuid")) != NULL)
210         return (uuid);
211
212 #if defined(__APPLE__)
213     if ((uuid = uuid_get_from_sysctlbyname("kern.uuid")) != NULL)
214         return (uuid);
215 #elif defined(__FreeBSD__)
216     if ((uuid = uuid_get_from_sysctlbyname("kern.hostuuid")) != NULL)
217         return (uuid);
218 #elif defined(__NetBSD__)
219     if ((uuid = uuid_get_from_sysctlbyname("machdep.dmi.system-uuid")) != NULL)
220         return (uuid);
221 #elif defined(__linux__)
222     if ((uuid = uuid_get_from_file("/sys/class/dmi/id/product_uuid")) != NULL)
223         return (uuid);
224 #endif
225
226 #if HAVE_LIBHAL_H
227     if ((uuid = uuid_get_from_hal()) != NULL)
228         return (uuid);
229 #endif
230
231     if ((uuid = uuid_get_from_dmidecode()) != NULL)
232         return (uuid);
233
234 #if defined(__linux__)
235     if ((uuid = uuid_get_from_file("/sys/hypervisor/uuid")) != NULL)
236         return (uuid);
237 #endif
238
239     return (NULL);
240 }
241
242 static int
243 uuid_config (const char *key, const char *value)
244 {
245     if (strcasecmp (key, "UUIDFile") == 0) {
246         char *tmp = strdup (value);
247         if (tmp == NULL)
248             return (-1);
249         sfree (uuidfile);
250         uuidfile = tmp;
251         return (0);
252     }
253
254     return (1);
255 }
256
257 static int
258 uuid_init (void)
259 {
260     char *uuid = uuid_get_local ();
261
262     if (uuid) {
263         sstrncpy (hostname_g, uuid, DATA_MAX_NAME_LEN);
264         sfree (uuid);
265         return (0);
266     }
267
268     WARNING ("uuid: could not read UUID using any known method");
269     return (0);
270 }
271
272 void module_register (void)
273 {
274     plugin_register_config ("uuid", uuid_config,
275             config_keys, STATIC_ARRAY_SIZE (config_keys));
276     plugin_register_init ("uuid", uuid_init);
277 }
278
279 /*
280  * vim: set tabstop=4:
281  * vim: set shiftwidth=4:
282  * vim: set expandtab:
283  */
284 /*
285  * Local variables:
286  *  indent-tabs-mode: nil
287  *  c-indent-level: 4
288  *  c-basic-offset: 4
289  *  tab-width: 4
290  * End:
291  */