Merge pull request #3339 from jkohen/patch-1
[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 "plugin.h"
30 #include "utils/common/common.h"
31
32 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) ||                \
33     defined(__OpenBSD__)
34 /* Implies have BSD variant */
35 #include <sys/sysctl.h>
36 #endif
37
38 #define UUID_RAW_LENGTH 16
39 #define UUID_PRINTABLE_COMPACT_LENGTH (UUID_RAW_LENGTH * 2)
40 #define UUID_PRINTABLE_NORMAL_LENGTH (UUID_PRINTABLE_COMPACT_LENGTH + 4)
41
42 static char *uuidfile;
43
44 static const char *config_keys[] = {"UUIDFile"};
45
46 static int looks_like_a_uuid(const char *uuid) {
47   if (!uuid)
48     return 0;
49
50   size_t len = strlen(uuid);
51   if (len < UUID_PRINTABLE_COMPACT_LENGTH)
52     return 0;
53
54   while (*uuid) {
55     if (!isxdigit((int)*uuid) && *uuid != '-')
56       return 0;
57     uuid++;
58   }
59   return 1;
60 }
61
62 static char *uuid_parse_dmidecode(FILE *file) {
63   char line[1024];
64
65   while (fgets(line, sizeof(line), file) != NULL) {
66     char *fields[4];
67     int fields_num;
68
69     strstripnewline(line);
70
71     /* Look for a line reading:
72      *   UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
73      */
74     fields_num = strsplit(line, fields, STATIC_ARRAY_SIZE(fields));
75     if (fields_num != 2)
76       continue;
77
78     if (strcmp("UUID:", fields[0]) != 0)
79       continue;
80
81     if (!looks_like_a_uuid(fields[1]))
82       continue;
83
84     return strdup(fields[1]);
85   }
86   return NULL;
87 }
88
89 static char *uuid_get_from_dmidecode(void) {
90   FILE *dmidecode = popen("dmidecode -t system 2>/dev/null", "r");
91   char *uuid;
92
93   if (!dmidecode)
94     return NULL;
95
96   uuid = uuid_parse_dmidecode(dmidecode);
97
98   pclose(dmidecode);
99   return uuid;
100 }
101
102 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
103 static char *uuid_get_from_sysctlbyname(const char *name) {
104   char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1];
105   size_t len = sizeof(uuid);
106   if (sysctlbyname(name, &uuid, &len, NULL, 0) == -1)
107     return NULL;
108   return strdup(uuid);
109 }
110 #elif defined(__OpenBSD__)
111 static char *uuid_get_from_sysctl(void) {
112   char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1];
113   size_t len = sizeof(uuid);
114   int mib[2];
115
116   mib[0] = CTL_HW;
117   mib[1] = HW_UUID;
118
119   if (sysctl(mib, 2, uuid, &len, NULL, 0) == -1)
120     return NULL;
121   return strdup(uuid);
122 }
123 #endif
124
125 static char *uuid_get_from_file(const char *path) {
126   FILE *file;
127   char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1] = "";
128
129   file = fopen(path, "r");
130   if (file == NULL)
131     return NULL;
132
133   if (!fgets(uuid, sizeof(uuid), file)) {
134     fclose(file);
135     return NULL;
136   }
137   fclose(file);
138   strstripnewline(uuid);
139
140   return strdup(uuid);
141 }
142
143 static char *uuid_get_local(void) {
144   char *uuid;
145
146   /* Check /etc/uuid / UUIDFile before any other method. */
147   if ((uuid = uuid_get_from_file(uuidfile ? uuidfile : "/etc/uuid")) != NULL)
148     return uuid;
149
150 #if defined(__APPLE__)
151   if ((uuid = uuid_get_from_sysctlbyname("kern.uuid")) != NULL)
152     return uuid;
153 #elif defined(__FreeBSD__)
154   if ((uuid = uuid_get_from_sysctlbyname("kern.hostuuid")) != NULL)
155     return uuid;
156 #elif defined(__NetBSD__)
157   if ((uuid = uuid_get_from_sysctlbyname("machdep.dmi.system-uuid")) != NULL)
158     return uuid;
159 #elif defined(__OpenBSD__)
160   if ((uuid = uuid_get_from_sysctl()) != NULL)
161     return uuid;
162 #elif defined(__linux__)
163   if ((uuid = uuid_get_from_file("/sys/class/dmi/id/product_uuid")) != NULL)
164     return uuid;
165 #endif
166
167   if ((uuid = uuid_get_from_dmidecode()) != NULL)
168     return uuid;
169
170 #if defined(__linux__)
171   if ((uuid = uuid_get_from_file("/sys/hypervisor/uuid")) != NULL)
172     return uuid;
173 #endif
174
175   return NULL;
176 }
177
178 static int uuid_config(const char *key, const char *value) {
179   if (strcasecmp(key, "UUIDFile") == 0) {
180     char *tmp = strdup(value);
181     if (tmp == NULL)
182       return -1;
183     sfree(uuidfile);
184     uuidfile = tmp;
185     return 0;
186   }
187
188   return 1;
189 }
190
191 static int uuid_init(void) {
192   char *uuid = uuid_get_local();
193
194   if (uuid) {
195     hostname_set(uuid);
196     sfree(uuid);
197     return 0;
198   }
199
200   WARNING("uuid: could not read UUID using any known method");
201   return 0;
202 }
203
204 void module_register(void) {
205   plugin_register_config("uuid", uuid_config, config_keys,
206                          STATIC_ARRAY_SIZE(config_keys));
207   plugin_register_init("uuid", uuid_init);
208 }