25d64676ed8068665859e1233b6dc21630bbd233
[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 }
99 else
100 {
101 DBG1(DBG_IMC, "failed to open \"%s\"", ip_forward);
102 }
103 fclose(file);
104
105 return fwd_status;
106 }
107
108 METHOD(os_info_t, create_package_enumerator, enumerator_t*,
109 private_os_info_t *this)
110 {
111 /* TODO */
112 return NULL;
113 }
114
115
116 METHOD(os_info_t, destroy, void,
117 private_os_info_t *this)
118 {
119 free(this->name.ptr);
120 free(this->version.ptr);
121 free(this);
122 }
123
124 #define RELEASE_LSB 0
125 #define RELEASE_DEBIAN 1
126
127 /**
128 * Determine Linux distribution version and hardware platform
129 */
130 static bool extract_platform_info(chunk_t *name, chunk_t *version)
131 {
132 FILE *file;
133 u_char buf[BUF_LEN], *pos = buf;
134 int len = BUF_LEN - 1;
135 chunk_t os_name = chunk_empty;
136 chunk_t os_version = chunk_empty;
137 struct utsname uninfo;
138 int i;
139
140 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
141 const char* releases[] = {
142 "/etc/lsb-release", "/etc/debian_version",
143 "/etc/SuSE-release", "/etc/novell-release",
144 "/etc/sles-release", "/etc/redhat-release",
145 "/etc/fedora-release", "/etc/gentoo-release",
146 "/etc/slackware-version", "/etc/annvix-release",
147 "/etc/arch-release", "/etc/arklinux-release",
148 "/etc/aurox-release", "/etc/blackcat-release",
149 "/etc/cobalt-release", "/etc/conectiva-release",
150 "/etc/debian_release", "/etc/immunix-release",
151 "/etc/lfs-release", "/etc/linuxppc-release",
152 "/etc/mandrake-release", "/etc/mandriva-release",
153 "/etc/mandrakelinux-release", "/etc/mklinux-release",
154 "/etc/pld-release", "/etc/redhat_version",
155 "/etc/slackware-release", "/etc/e-smith-release",
156 "/etc/release", "/etc/sun-release",
157 "/etc/tinysofa-release", "/etc/turbolinux-release",
158 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
159 "/etc/va-release", "/etc/yellowdog-release"
160 };
161
162 const char lsb_distrib_id[] = "DISTRIB_ID=";
163 const char lsb_distrib_release[] = "DISTRIB_RELEASE=";
164
165 for (i = 0; i < countof(releases); i++)
166 {
167 file = fopen(releases[i], "r");
168 if (!file)
169 {
170 continue;
171 }
172
173 /* read release file into buffer */
174 fseek(file, 0, SEEK_END);
175 len = min(ftell(file), len);
176 rewind(file);
177 buf[len] = '\0';
178 if (fread(buf, 1, len, file) != len)
179 {
180 DBG1(DBG_IMC, "failed to read file \"%s\"", releases[i]);
181 fclose(file);
182 return FALSE;
183 }
184 fclose(file);
185
186 DBG1(DBG_IMC, "processing \"%s\" file", releases[i]);
187
188 switch (i)
189 {
190 case RELEASE_LSB:
191 {
192 /* Determine Distribution ID */
193 pos = strstr(buf, lsb_distrib_id);
194 if (!pos)
195 {
196 DBG1(DBG_IMC, "failed to find begin of DISTRIB_ID field");
197 return FALSE;
198 }
199 pos += strlen(lsb_distrib_id);
200
201 os_name.ptr = pos;
202
203 pos = strchr(pos, '\n');
204 if (!pos)
205 {
206 DBG1(DBG_IMC, "failed to find end of DISTRIB_ID field");
207 return FALSE;
208 }
209
210 os_name.len = pos - os_name.ptr;
211
212 /* Determine Distribution Release */
213 pos = strstr(buf, lsb_distrib_release);
214 if (!pos)
215 {
216 DBG1(DBG_IMC, "failed to find begin of DISTRIB_RELEASE field");
217 return FALSE;
218 }
219 pos += strlen(lsb_distrib_release);
220
221 os_version.ptr = pos;
222
223 pos = strchr(pos, '\n');
224 if (!pos)
225 {
226 DBG1(DBG_IMC, "failed to find end of DISTRIB_RELEASE field");
227 return FALSE;
228 }
229
230 os_version.len = pos - os_version.ptr;
231
232 break;
233 }
234 case RELEASE_DEBIAN:
235 {
236 char str_debian[] = "Debian";
237
238 os_name = chunk_create(str_debian, strlen(str_debian));
239 os_version.ptr = buf;
240
241 pos = strchr(buf, '\n');
242 if (!pos)
243 {
244 DBG1(DBG_PTS, "failed to find end of release string");
245 return FALSE;
246 }
247
248 os_version.len = pos - os_version.ptr;
249
250 break;
251 }
252 default:
253 {
254 const char str_release[] = " release ";
255
256 os_name.ptr = buf;
257
258 pos = strstr(buf, str_release);
259 if (!pos)
260 {
261 DBG1(DBG_IMC, "failed to find release keyword");
262 return FALSE;
263 }
264
265 os_name.len = pos - os_name.ptr;
266 pos += strlen(str_release);
267 os_version.ptr = pos;
268
269 pos = strchr(pos, '\n');
270 if (!pos)
271 {
272 DBG1(DBG_IMC, "failed to find end of release string");
273 return FALSE;
274 }
275
276 os_version.len = pos - os_version.ptr;
277
278 break;
279 }
280 }
281 break;
282 }
283
284 if (!os_name.ptr)
285 {
286 DBG1(DBG_IMC, "no distribution release file found");
287 return FALSE;
288 }
289
290 if (uname(&uninfo) < 0)
291 {
292 DBG1(DBG_IMC, "could not retrieve machine architecture");
293 return FALSE;
294 }
295
296 /* copy OS name */
297 *name = chunk_clone(os_name);
298
299 /* copy OS version and machine architecture */
300 *version = chunk_alloc(os_version.len + 1 + strlen(uninfo.machine));
301 pos = version->ptr;
302 memcpy(pos, os_version.ptr, os_version.len);
303 pos += os_version.len;
304 *pos++ = ' ';
305 memcpy(pos, uninfo.machine, strlen(uninfo.machine));
306
307 return TRUE;
308 }
309
310 /**
311 * See header
312 */
313 os_info_t *os_info_create(void)
314 {
315 private_os_info_t *this;
316 chunk_t name, version;
317
318 /* As an opton OS name and OS version can be configured manually */
319 name.ptr = lib->settings->get_str(lib->settings,
320 "libimcv.os_info.name", NULL);
321 version.ptr = lib->settings->get_str(lib->settings,
322 "libimcv.os_info.version", NULL);
323 if (name.ptr && version.ptr)
324 {
325 name.len = strlen(name.ptr);
326 name = chunk_clone(name);
327
328 version.len = strlen(version.ptr);
329 version = chunk_clone(version);
330 }
331 else
332 {
333 if (!extract_platform_info(&name, &version))
334 {
335 return NULL;
336 }
337 }
338 DBG1(DBG_IMC, "operating system name is '%.*s'",
339 name.len, name.ptr);
340 DBG1(DBG_IMC, "operating system version is '%.*s'",
341 version.len, version.ptr);
342
343 INIT(this,
344 .public = {
345 .get_name = _get_name,
346 .get_version = _get_version,
347 .get_fwd_status = _get_fwd_status,
348 .create_package_enumerator = _create_package_enumerator,
349 .destroy = _destroy,
350 },
351 .name = name,
352 .version = version,
353 );
354
355 return &this->public;
356 }