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