implemented ITA Get Settings and ITA Settings attributes
[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 <collections/linked_list.h>
22 #include <utils/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_numeric_version, void,
62 private_os_info_t *this, u_int32_t *major, u_int32_t *minor)
63 {
64 u_char *pos;
65
66 if (major)
67 {
68 *major = atol(this->version.ptr);
69 }
70 pos = memchr(this->version.ptr, '.', this->version.len);
71 if (minor)
72 {
73 *minor = pos ? atol(pos + 1) : 0;
74 }
75 }
76
77 METHOD(os_info_t, get_version, chunk_t,
78 private_os_info_t *this)
79 {
80 return this->version;
81 }
82
83 METHOD(os_info_t, get_fwd_status, os_fwd_status_t,
84 private_os_info_t *this)
85 {
86 const char ip_forward[] = "/proc/sys/net/ipv4/ip_forward";
87 char buf[2];
88 FILE *file;
89
90 os_fwd_status_t fwd_status = OS_FWD_UNKNOWN;
91
92 file = fopen(ip_forward, "r");
93 if (file)
94 {
95 if (fread(buf, 1, 1, file) == 1)
96 {
97 switch (buf[0])
98 {
99 case '0':
100 fwd_status = OS_FWD_DISABLED;
101 break;
102 case '1':
103 fwd_status = OS_FWD_ENABLED;
104 break;
105 default:
106 DBG1(DBG_IMC, "\"%s\" returns invalid value ", ip_forward);
107 break;
108 }
109 }
110 else
111 {
112 DBG1(DBG_IMC, "could not read from \"%s\"", ip_forward);
113 }
114 fclose(file);
115 }
116 else
117 {
118 DBG1(DBG_IMC, "failed to open \"%s\"", ip_forward);
119 }
120
121 return fwd_status;
122 }
123
124 METHOD(os_info_t, get_uptime, time_t,
125 private_os_info_t *this)
126 {
127 const char proc_uptime[] = "/proc/uptime";
128 FILE *file;
129 time_t uptime;
130
131 file = fopen(proc_uptime, "r");
132 if (!file)
133 {
134 DBG1(DBG_IMC, "failed to open \"%s\"", proc_uptime);
135 return 0;
136 }
137 if (fscanf(file, "%u", &uptime) != 1)
138 {
139 DBG1(DBG_IMC, "failed to read file \"%s\"", proc_uptime);
140 uptime = 0;
141 }
142 fclose(file);
143
144 return uptime;
145 }
146
147 METHOD(os_info_t, get_setting, chunk_t,
148 private_os_info_t *this, char *name)
149 {
150 FILE *file;
151 u_char buf[2048];
152 size_t i = 0;
153 chunk_t value;
154
155 if (!strneq(name, "/etc/", 5) && !strneq(name, "/proc/", 6) &&
156 !strneq(name, "/sys/", 5))
157 {
158 /**
159 * In order to guarantee privacy, only settings from the
160 * /etc/, /proc/ and /sys/ directories can be retrieved
161 */
162 return chunk_empty;
163 }
164
165 file = fopen(name, "r");
166 while (i < sizeof(buf) && fread(buf + i, 1, 1, file) == 1)
167 {
168 i++;
169 }
170 fclose(file);
171
172 value = chunk_create(buf, i);
173
174 return chunk_clone(value);
175 }
176
177 METHOD(os_info_t, create_package_enumerator, enumerator_t*,
178 private_os_info_t *this)
179 {
180 /* TODO */
181
182 return NULL;
183 }
184
185
186 METHOD(os_info_t, destroy, void,
187 private_os_info_t *this)
188 {
189 free(this->name.ptr);
190 free(this->version.ptr);
191 free(this);
192 }
193
194 #define RELEASE_LSB 0
195 #define RELEASE_DEBIAN 1
196
197 /**
198 * Determine Linux distribution version and hardware platform
199 */
200 static bool extract_platform_info(chunk_t *name, chunk_t *version)
201 {
202 FILE *file;
203 u_char buf[BUF_LEN], *pos = buf;
204 int len = BUF_LEN - 1;
205 chunk_t os_name = chunk_empty;
206 chunk_t os_version = chunk_empty;
207 struct utsname uninfo;
208 int i;
209
210 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
211 const char* releases[] = {
212 "/etc/lsb-release", "/etc/debian_version",
213 "/etc/SuSE-release", "/etc/novell-release",
214 "/etc/sles-release", "/etc/redhat-release",
215 "/etc/fedora-release", "/etc/gentoo-release",
216 "/etc/slackware-version", "/etc/annvix-release",
217 "/etc/arch-release", "/etc/arklinux-release",
218 "/etc/aurox-release", "/etc/blackcat-release",
219 "/etc/cobalt-release", "/etc/conectiva-release",
220 "/etc/debian_release", "/etc/immunix-release",
221 "/etc/lfs-release", "/etc/linuxppc-release",
222 "/etc/mandrake-release", "/etc/mandriva-release",
223 "/etc/mandrakelinux-release", "/etc/mklinux-release",
224 "/etc/pld-release", "/etc/redhat_version",
225 "/etc/slackware-release", "/etc/e-smith-release",
226 "/etc/release", "/etc/sun-release",
227 "/etc/tinysofa-release", "/etc/turbolinux-release",
228 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
229 "/etc/va-release", "/etc/yellowdog-release"
230 };
231
232 const char lsb_distrib_id[] = "DISTRIB_ID=";
233 const char lsb_distrib_release[] = "DISTRIB_RELEASE=";
234
235 for (i = 0; i < countof(releases); i++)
236 {
237 file = fopen(releases[i], "r");
238 if (!file)
239 {
240 continue;
241 }
242
243 /* read release file into buffer */
244 fseek(file, 0, SEEK_END);
245 len = min(ftell(file), len);
246 rewind(file);
247 buf[len] = '\0';
248 if (fread(buf, 1, len, file) != len)
249 {
250 DBG1(DBG_IMC, "failed to read file \"%s\"", releases[i]);
251 fclose(file);
252 return FALSE;
253 }
254 fclose(file);
255
256 DBG1(DBG_IMC, "processing \"%s\" file", releases[i]);
257
258 switch (i)
259 {
260 case RELEASE_LSB:
261 {
262 /* Determine Distribution ID */
263 pos = strstr(buf, lsb_distrib_id);
264 if (!pos)
265 {
266 DBG1(DBG_IMC, "failed to find begin of DISTRIB_ID field");
267 return FALSE;
268 }
269 pos += strlen(lsb_distrib_id);
270
271 os_name.ptr = pos;
272
273 pos = strchr(pos, '\n');
274 if (!pos)
275 {
276 DBG1(DBG_IMC, "failed to find end of DISTRIB_ID field");
277 return FALSE;
278 }
279
280 os_name.len = pos - os_name.ptr;
281
282 /* Determine Distribution Release */
283 pos = strstr(buf, lsb_distrib_release);
284 if (!pos)
285 {
286 DBG1(DBG_IMC, "failed to find begin of DISTRIB_RELEASE field");
287 return FALSE;
288 }
289 pos += strlen(lsb_distrib_release);
290
291 os_version.ptr = pos;
292
293 pos = strchr(pos, '\n');
294 if (!pos)
295 {
296 DBG1(DBG_IMC, "failed to find end of DISTRIB_RELEASE field");
297 return FALSE;
298 }
299
300 os_version.len = pos - os_version.ptr;
301
302 break;
303 }
304 case RELEASE_DEBIAN:
305 {
306 char str_debian[] = "Debian";
307
308 os_name = chunk_create(str_debian, strlen(str_debian));
309 os_version.ptr = buf;
310
311 pos = strchr(buf, '\n');
312 if (!pos)
313 {
314 DBG1(DBG_PTS, "failed to find end of release string");
315 return FALSE;
316 }
317
318 os_version.len = pos - os_version.ptr;
319
320 break;
321 }
322 default:
323 {
324 const char str_release[] = " release ";
325
326 os_name.ptr = buf;
327
328 pos = strstr(buf, str_release);
329 if (!pos)
330 {
331 DBG1(DBG_IMC, "failed to find release keyword");
332 return FALSE;
333 }
334
335 os_name.len = pos - os_name.ptr;
336 pos += strlen(str_release);
337 os_version.ptr = pos;
338
339 pos = strchr(pos, '\n');
340 if (!pos)
341 {
342 DBG1(DBG_IMC, "failed to find end of release string");
343 return FALSE;
344 }
345
346 os_version.len = pos - os_version.ptr;
347
348 break;
349 }
350 }
351 break;
352 }
353
354 if (!os_name.ptr)
355 {
356 DBG1(DBG_IMC, "no distribution release file found");
357 return FALSE;
358 }
359
360 if (uname(&uninfo) < 0)
361 {
362 DBG1(DBG_IMC, "could not retrieve machine architecture");
363 return FALSE;
364 }
365
366 /* copy OS name */
367 *name = chunk_clone(os_name);
368
369 /* copy OS version and machine architecture */
370 *version = chunk_alloc(os_version.len + 1 + strlen(uninfo.machine));
371 pos = version->ptr;
372 memcpy(pos, os_version.ptr, os_version.len);
373 pos += os_version.len;
374 *pos++ = ' ';
375 memcpy(pos, uninfo.machine, strlen(uninfo.machine));
376
377 return TRUE;
378 }
379
380 /**
381 * See header
382 */
383 os_info_t *os_info_create(void)
384 {
385 private_os_info_t *this;
386 chunk_t name, version;
387
388 /* As an opton OS name and OS version can be configured manually */
389 name.ptr = lib->settings->get_str(lib->settings,
390 "libimcv.os_info.name", NULL);
391 version.ptr = lib->settings->get_str(lib->settings,
392 "libimcv.os_info.version", NULL);
393 if (name.ptr && version.ptr)
394 {
395 name.len = strlen(name.ptr);
396 name = chunk_clone(name);
397
398 version.len = strlen(version.ptr);
399 version = chunk_clone(version);
400 }
401 else
402 {
403 if (!extract_platform_info(&name, &version))
404 {
405 return NULL;
406 }
407 }
408 DBG1(DBG_IMC, "operating system name is '%.*s'",
409 name.len, name.ptr);
410 DBG1(DBG_IMC, "operating system version is '%.*s'",
411 version.len, version.ptr);
412
413 INIT(this,
414 .public = {
415 .get_name = _get_name,
416 .get_numeric_version = _get_numeric_version,
417 .get_version = _get_version,
418 .get_fwd_status = _get_fwd_status,
419 .get_uptime = _get_uptime,
420 .get_setting = _get_setting,
421 .create_package_enumerator = _create_package_enumerator,
422 .destroy = _destroy,
423 },
424 .name = name,
425 .version = version,
426 );
427
428 return &this->public;
429 }