fd7d6ce48657332211b46c706b42ad7bc5c98aa0
[strongswan.git] / src / libimcv / os_info / os_info.c
1 /*
2 * Copyright (C) 2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
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 MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "os_info.h"
17
18 #include <sys/utsname.h>
19 #include <stdio.h>
20
21 #include <utils/linked_list.h>
22 #include <debug.h>
23
24 typedef struct private_os_info_t private_os_info_t;
25
26 ENUM(os_fwd_status_names, OS_FWD_DISABLED, OS_FWD_UNKNOWN,
27 "disabled",
28 "enabled",
29 "unknown"
30 );
31
32 /**
33 * Private data of an os_info_t object.
34 *
35 */
36 struct private_os_info_t {
37
38 /**
39 * Public os_info_t interface.
40 */
41 os_info_t public;
42
43 /**
44 * OS name
45 */
46 chunk_t name;
47
48 /**
49 * OS version
50 */
51 chunk_t version;
52
53 };
54
55 METHOD(os_info_t, get_name, chunk_t,
56 private_os_info_t *this)
57 {
58 return this->name;
59 }
60
61 METHOD(os_info_t, get_version, chunk_t,
62 private_os_info_t *this)
63 {
64 return this->version;
65 }
66
67 METHOD(os_info_t, get_fwd_status, os_fwd_status_t,
68 private_os_info_t *this)
69 {
70 const char ip_forward[] = "/proc/sys/net/ipv4/ip_forward";
71 char buf[2];
72 FILE *file;
73
74 os_fwd_status_t fwd_status = OS_FWD_UNKNOWN;
75
76 file = fopen(ip_forward, "r");
77 if (file)
78 {
79 if (fread(buf, 1, 1, file) == 1)
80 {
81 switch (buf[0])
82 {
83 case '0':
84 fwd_status = OS_FWD_DISABLED;
85 break;
86 case '1':
87 fwd_status = OS_FWD_ENABLED;
88 break;
89 default:
90 DBG1(DBG_IMC, "\"%s\" returns invalid value ", ip_forward);
91 break;
92 }
93 }
94 else
95 {
96 DBG1(DBG_IMC, "could not read from \"%s\"", ip_forward);
97 }
98 fclose(file);
99 }
100 else
101 {
102 DBG1(DBG_IMC, "failed to open \"%s\"", ip_forward);
103 }
104
105 return fwd_status;
106 }
107
108 METHOD(os_info_t, get_uptime, time_t,
109 private_os_info_t *this)
110 {
111 const char proc_uptime[] = "/proc/uptime";
112 FILE *file;
113 time_t uptime;
114
115 file = fopen(proc_uptime, "r");
116 if (!file)
117 {
118 DBG1(DBG_IMC, "failed to open \"%s\"", proc_uptime);
119 return 0;
120 }
121 if (fscanf(file, "%u", &uptime) != 1)
122 {
123 DBG1(DBG_IMC, "failed to read file \"%s\"", proc_uptime);
124 uptime = 0;
125 }
126 fclose(file);
127
128 return uptime;
129 }
130
131 METHOD(os_info_t, create_package_enumerator, enumerator_t*,
132 private_os_info_t *this)
133 {
134 /* TODO */
135
136 return NULL;
137 }
138
139
140 METHOD(os_info_t, destroy, void,
141 private_os_info_t *this)
142 {
143 free(this->name.ptr);
144 free(this->version.ptr);
145 free(this);
146 }
147
148 #define RELEASE_LSB 0
149 #define RELEASE_DEBIAN 1
150
151 /**
152 * Determine Linux distribution version and hardware platform
153 */
154 static bool extract_platform_info(chunk_t *name, chunk_t *version)
155 {
156 FILE *file;
157 u_char buf[BUF_LEN], *pos = buf;
158 int len = BUF_LEN - 1;
159 chunk_t os_name = chunk_empty;
160 chunk_t os_version = chunk_empty;
161 struct utsname uninfo;
162 int i;
163
164 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
165 const char* releases[] = {
166 "/etc/lsb-release", "/etc/debian_version",
167 "/etc/SuSE-release", "/etc/novell-release",
168 "/etc/sles-release", "/etc/redhat-release",
169 "/etc/fedora-release", "/etc/gentoo-release",
170 "/etc/slackware-version", "/etc/annvix-release",
171 "/etc/arch-release", "/etc/arklinux-release",
172 "/etc/aurox-release", "/etc/blackcat-release",
173 "/etc/cobalt-release", "/etc/conectiva-release",
174 "/etc/debian_release", "/etc/immunix-release",
175 "/etc/lfs-release", "/etc/linuxppc-release",
176 "/etc/mandrake-release", "/etc/mandriva-release",
177 "/etc/mandrakelinux-release", "/etc/mklinux-release",
178 "/etc/pld-release", "/etc/redhat_version",
179 "/etc/slackware-release", "/etc/e-smith-release",
180 "/etc/release", "/etc/sun-release",
181 "/etc/tinysofa-release", "/etc/turbolinux-release",
182 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
183 "/etc/va-release", "/etc/yellowdog-release"
184 };
185
186 const char lsb_distrib_id[] = "DISTRIB_ID=";
187 const char lsb_distrib_release[] = "DISTRIB_RELEASE=";
188
189 for (i = 0; i < countof(releases); i++)
190 {
191 file = fopen(releases[i], "r");
192 if (!file)
193 {
194 continue;
195 }
196
197 /* read release file into buffer */
198 fseek(file, 0, SEEK_END);
199 len = min(ftell(file), len);
200 rewind(file);
201 buf[len] = '\0';
202 if (fread(buf, 1, len, file) != len)
203 {
204 DBG1(DBG_IMC, "failed to read file \"%s\"", releases[i]);
205 fclose(file);
206 return FALSE;
207 }
208 fclose(file);
209
210 DBG1(DBG_IMC, "processing \"%s\" file", releases[i]);
211
212 switch (i)
213 {
214 case RELEASE_LSB:
215 {
216 /* Determine Distribution ID */
217 pos = strstr(buf, lsb_distrib_id);
218 if (!pos)
219 {
220 DBG1(DBG_IMC, "failed to find begin of DISTRIB_ID field");
221 return FALSE;
222 }
223 pos += strlen(lsb_distrib_id);
224
225 os_name.ptr = pos;
226
227 pos = strchr(pos, '\n');
228 if (!pos)
229 {
230 DBG1(DBG_IMC, "failed to find end of DISTRIB_ID field");
231 return FALSE;
232 }
233
234 os_name.len = pos - os_name.ptr;
235
236 /* Determine Distribution Release */
237 pos = strstr(buf, lsb_distrib_release);
238 if (!pos)
239 {
240 DBG1(DBG_IMC, "failed to find begin of DISTRIB_RELEASE field");
241 return FALSE;
242 }
243 pos += strlen(lsb_distrib_release);
244
245 os_version.ptr = pos;
246
247 pos = strchr(pos, '\n');
248 if (!pos)
249 {
250 DBG1(DBG_IMC, "failed to find end of DISTRIB_RELEASE field");
251 return FALSE;
252 }
253
254 os_version.len = pos - os_version.ptr;
255
256 break;
257 }
258 case RELEASE_DEBIAN:
259 {
260 char str_debian[] = "Debian";
261
262 os_name = chunk_create(str_debian, strlen(str_debian));
263 os_version.ptr = buf;
264
265 pos = strchr(buf, '\n');
266 if (!pos)
267 {
268 DBG1(DBG_PTS, "failed to find end of release string");
269 return FALSE;
270 }
271
272 os_version.len = pos - os_version.ptr;
273
274 break;
275 }
276 default:
277 {
278 const char str_release[] = " release ";
279
280 os_name.ptr = buf;
281
282 pos = strstr(buf, str_release);
283 if (!pos)
284 {
285 DBG1(DBG_IMC, "failed to find release keyword");
286 return FALSE;
287 }
288
289 os_name.len = pos - os_name.ptr;
290 pos += strlen(str_release);
291 os_version.ptr = pos;
292
293 pos = strchr(pos, '\n');
294 if (!pos)
295 {
296 DBG1(DBG_IMC, "failed to find end of release string");
297 return FALSE;
298 }
299
300 os_version.len = pos - os_version.ptr;
301
302 break;
303 }
304 }
305 break;
306 }
307
308 if (!os_name.ptr)
309 {
310 DBG1(DBG_IMC, "no distribution release file found");
311 return FALSE;
312 }
313
314 if (uname(&uninfo) < 0)
315 {
316 DBG1(DBG_IMC, "could not retrieve machine architecture");
317 return FALSE;
318 }
319
320 /* copy OS name */
321 *name = chunk_clone(os_name);
322
323 /* copy OS version and machine architecture */
324 *version = chunk_alloc(os_version.len + 1 + strlen(uninfo.machine));
325 pos = version->ptr;
326 memcpy(pos, os_version.ptr, os_version.len);
327 pos += os_version.len;
328 *pos++ = ' ';
329 memcpy(pos, uninfo.machine, strlen(uninfo.machine));
330
331 return TRUE;
332 }
333
334 /**
335 * See header
336 */
337 os_info_t *os_info_create(void)
338 {
339 private_os_info_t *this;
340 chunk_t name, version;
341
342 /* As an opton OS name and OS version can be configured manually */
343 name.ptr = lib->settings->get_str(lib->settings,
344 "libimcv.os_info.name", NULL);
345 version.ptr = lib->settings->get_str(lib->settings,
346 "libimcv.os_info.version", NULL);
347 if (name.ptr && version.ptr)
348 {
349 name.len = strlen(name.ptr);
350 name = chunk_clone(name);
351
352 version.len = strlen(version.ptr);
353 version = chunk_clone(version);
354 }
355 else
356 {
357 if (!extract_platform_info(&name, &version))
358 {
359 return NULL;
360 }
361 }
362 DBG1(DBG_IMC, "operating system name is '%.*s'",
363 name.len, name.ptr);
364 DBG1(DBG_IMC, "operating system version is '%.*s'",
365 version.len, version.ptr);
366
367 INIT(this,
368 .public = {
369 .get_name = _get_name,
370 .get_version = _get_version,
371 .get_fwd_status = _get_fwd_status,
372 .get_uptime = _get_uptime,
373 .create_package_enumerator = _create_package_enumerator,
374 .destroy = _destroy,
375 },
376 .name = name,
377 .version = version,
378 );
379
380 return &this->public;
381 }