Cast first argument for %.*s to int
[strongswan.git] / src / libimcv / plugins / imv_os / pacman.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 #define _GNU_SOURCE
17 #include <getopt.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <syslog.h>
23 #include <time.h>
24
25 #include "imv_os_state.h"
26
27 #include <library.h>
28 #include <utils/debug.h>
29
30 /**
31 * global debug output variables
32 */
33 static int debug_level = 1;
34 static bool stderr_quiet = TRUE;
35
36 /**
37 * pacman dbg function
38 */
39 static void pacman_dbg(debug_t group, level_t level, char *fmt, ...)
40 {
41 int priority = LOG_INFO;
42 char buffer[8192];
43 char *current = buffer, *next;
44 va_list args;
45
46 if (level <= debug_level)
47 {
48 if (!stderr_quiet)
49 {
50 va_start(args, fmt);
51 vfprintf(stderr, fmt, args);
52 fprintf(stderr, "\n");
53 va_end(args);
54 }
55
56 /* write in memory buffer first */
57 va_start(args, fmt);
58 vsnprintf(buffer, sizeof(buffer), fmt, args);
59 va_end(args);
60
61 /* do a syslog with every line */
62 while (current)
63 {
64 next = strchr(current, '\n');
65 if (next)
66 {
67 *(next++) = '\0';
68 }
69 syslog(priority, "%s\n", current);
70 current = next;
71 }
72 }
73 }
74
75 /**
76 * atexit handler to close everything on shutdown
77 */
78 static void cleanup(void)
79 {
80 closelog();
81 library_deinit();
82 }
83
84 static void usage(void)
85 {
86 printf("Usage:\n"
87 "ipsec pacman --product <name> --file <filename> [--update]\n");
88 }
89
90 /**
91 * Extract the time the package file was generated
92 */
93 static time_t extract_time(char *line)
94 {
95 struct tm t;
96 char wday[4], mon[4];
97 char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
98 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
99 int i;
100
101 if (sscanf(line, "Generated: %3s %3s %2d %2d:%2d:%2d %4d UTC", wday, mon,
102 &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &t.tm_year) != 7)
103 {
104 return UNDEFINED_TIME;
105 }
106 t.tm_isdst = 0;
107 t.tm_year -= 1900;
108 t.tm_mon = 12;
109
110 for (i = 0; i < countof(months); i++)
111 {
112 if (streq(mon, months[i]))
113 {
114 t.tm_mon = i;
115 break;
116 }
117 }
118 if (t.tm_mon == 12)
119 {
120 return UNDEFINED_TIME;
121 }
122
123 return mktime(&t) - timezone;
124 }
125
126 /**
127 * Process a package file and store updates in the database
128 */
129 static void process_packages(char *filename, char *product, bool update)
130 {
131 char *uri, line[12288], *pos;
132 int count = 0, errored = 0, vulnerable = 0, new_packages = 0;
133 int new_versions = 0, updated_versions = 0, deleted_versions = 0;
134 u_int32_t pid = 0;
135 enumerator_t *e;
136 database_t *db;
137 FILE *file;
138
139 /* opening package file */
140 printf("loading\"%s\"\n", filename);
141 file = fopen(filename, "r");
142 if (!file)
143 {
144 fprintf(stderr, "could not open \"%s\"\n", filename);
145 exit(EXIT_FAILURE);
146 }
147
148 /* connect package database */
149 uri = lib->settings->get_str(lib->settings, "pacman.database", NULL);
150 if (!uri)
151 {
152 fprintf(stderr, "database URI pacman.database not set\n");
153 fclose(file);
154 exit(EXIT_FAILURE);
155 }
156 db = lib->db->create(lib->db, uri);
157 if (!db)
158 {
159 fprintf(stderr, "could not connect to database '%s'\n", uri);
160 fclose(file);
161 exit(EXIT_FAILURE);
162 }
163
164 /* check if product is already in database */
165 e = db->query(db, "SELECT id FROM products WHERE name = ?",
166 DB_TEXT, product, DB_INT);
167 if (e)
168 {
169 if (!e->enumerate(e, &pid))
170 {
171 pid = 0;
172 }
173 e->destroy(e);
174 }
175 if (!pid)
176 {
177 if (db->execute(db, &pid, "INSERT INTO products (name) VALUES (?)",
178 DB_TEXT, product) != 1)
179 {
180 fprintf(stderr, "could not store product '%s' to database\n",
181 product);
182 fclose(file);
183 db->destroy(db);
184 exit(EXIT_FAILURE);
185 }
186 }
187
188 while (fgets(line, sizeof(line), file))
189 {
190 char *package, *version;
191 char *cur_version, *version_update = NULL, *version_delete = NULL;
192 bool security, add_version = TRUE;
193 int cur_security, security_update = 0, security_delete = 0;
194 u_int32_t gid = 0, vid = 0, vid_update = 0, vid_delete = 0;
195 time_t gen_time, cur_time;
196
197 count++;
198 if (count == 1)
199 {
200 printf("%s", line);
201 }
202 if (count == 3)
203 {
204 gen_time = extract_time(line);
205
206 if (gen_time == UNDEFINED_TIME)
207 {
208 fprintf(stderr, "could not extract generation time\n");
209 fclose(file);
210 db->destroy(db);
211 exit(EXIT_FAILURE);
212 }
213 printf("Generated: %T\n", &gen_time, TRUE);
214 }
215 if (count < 7)
216 {
217 continue;
218 }
219
220 /* look for the package name */
221 pos = strchr(line, ' ');
222 if (!pos)
223 {
224 fprintf(stderr, "could not extract package name from '%.*s'\n",
225 (int)(strlen(line)-1), line);
226 errored++;
227 continue;
228 }
229 *pos++ = '\0';
230 package = line;
231
232 /* look for version string in parentheses */
233 if (*pos == '(')
234 {
235 version = ++pos;
236 pos = strchr(pos, ')');
237 if (pos)
238 {
239 *pos++ = '\0';
240 }
241 else
242 {
243 fprintf(stderr, "could not extract package version from "
244 "'%.*s'\n", (int)(strlen(line)-1), line);
245 errored++;
246 continue;
247 }
248 }
249 else
250 {
251 /* no version information, skip entry */
252 continue;
253 }
254 security = (strstr(pos, "[security]") != NULL);
255 if (security)
256 {
257 vulnerable++;
258 }
259
260 /* handle non-security packages in update mode only */
261 if (!update && !security)
262 {
263 continue;
264 }
265
266 /* check if package is already in database */
267 e = db->query(db, "SELECT id FROM packages WHERE name = ?",
268 DB_TEXT, package, DB_INT);
269 if (e)
270 {
271 if (!e->enumerate(e, &gid))
272 {
273 gid = 0;
274 }
275 e->destroy(e);
276 }
277 if (!gid && security)
278 {
279 if (db->execute(db, &gid, "INSERT INTO packages (name) VALUES (?)",
280 DB_TEXT, package) != 1)
281 {
282 fprintf(stderr, "could not store package '%s' to database\n",
283 package);
284 fclose(file);
285 db->destroy(db);
286 exit(EXIT_FAILURE);
287 }
288 new_packages++;
289 }
290
291 /* check for package versions already in database */
292 e = db->query(db,
293 "SELECT id, release, security, time FROM versions "
294 "WHERE package = ? AND product = ?",
295 DB_INT, gid, DB_INT, pid, DB_INT, DB_TEXT, DB_INT, DB_INT);
296 if (!e)
297 {
298 break;
299 }
300 while (e->enumerate(e, &vid, &cur_version, &cur_security, &cur_time))
301 {
302 if (streq(version, cur_version))
303 {
304 /* already in data base */
305 add_version = FALSE;
306 break;
307 }
308 else if (gen_time > cur_time)
309 {
310 if (security)
311 {
312 if (cur_security)
313 {
314 vid_update = vid;
315 version_update = strdup(cur_version);
316 security_update = cur_security;
317 }
318 else
319 {
320 vid_delete = vid;
321 version_delete = strdup(cur_version);
322 security_delete = cur_security;
323 }
324 }
325 else
326 {
327 if (!cur_security)
328 {
329 vid_update = vid;
330 version_update = strdup(cur_version);
331 security_update = cur_security;
332 }
333 }
334 }
335 else
336 {
337 if (security == cur_security)
338 {
339 add_version = FALSE;
340 }
341 }
342 }
343 e->destroy(e);
344
345 if ((!vid && !security) || (vid && !add_version))
346 {
347 free(version_update);
348 free(version_delete);
349 continue;
350 }
351
352 if ((!vid && security) || (vid && !vid_update))
353 {
354 printf("%s (%s) %s\n", package, version, security ? "[s]" : "");
355
356 if (db->execute(db, &vid,
357 "INSERT INTO versions "
358 "(package, product, release, security, time) "
359 "VALUES (?, ?, ?, ?, ?)", DB_INT, gid, DB_INT, pid,
360 DB_TEXT, version, DB_INT, security, DB_INT, gen_time) != 1)
361 {
362 fprintf(stderr, "could not store version '%s' to database\n",
363 version);
364 free(version_update);
365 free(version_delete);
366 fclose(file);
367 db->destroy(db);
368 exit(EXIT_FAILURE);
369 }
370 new_versions++;
371 }
372 else
373 {
374 printf("%s (%s) %s updated by\n",
375 package, version_update, security_update ? "[s]" : "");
376 printf("%s (%s) %s\n", package, version, security ? "[s]" : "");
377
378 if (db->execute(db, NULL,
379 "UPDATE versions SET release = ?, time = ? WHERE id = ?",
380 DB_TEXT, version, DB_INT, gen_time, DB_INT, vid_update) <= 0)
381 {
382 fprintf(stderr, "could not update version '%s' to database\n",
383 version);
384 free(version_update);
385 free(version_delete);
386 fclose(file);
387 db->destroy(db);
388 exit(EXIT_FAILURE);
389 }
390 updated_versions++;
391 }
392
393 if (vid_delete)
394 {
395 printf("%s (%s) %s deleted\n",
396 package, version_delete, security_delete ? "[s]" : "");
397
398 if (db->execute(db, NULL,
399 "DELETE FROM versions WHERE id = ?",
400 DB_INT, vid_delete) <= 0)
401 {
402 fprintf(stderr, "could not delete version '%s' from database\n",
403 version_delete);
404 free(version_update);
405 free(version_delete);
406 fclose(file);
407 db->destroy(db);
408 exit(EXIT_FAILURE);
409 }
410 deleted_versions++;
411 }
412 free(version_update);
413 free(version_delete);
414 }
415 fclose(file);
416 db->destroy(db);
417
418 printf("processed %d packages, %d security, %d new packages, "
419 "%d new versions, %d updated versions, %d deleted versions, "
420 "%d errored\n", count - 6, vulnerable, new_packages, new_versions,
421 updated_versions, deleted_versions, errored);
422 }
423
424 static void do_args(int argc, char *argv[])
425 {
426 char *filename = NULL, *product = NULL;
427 bool update = FALSE;
428
429 /* reinit getopt state */
430 optind = 0;
431
432 while (TRUE)
433 {
434 int c;
435
436 struct option long_opts[] = {
437 { "help", no_argument, NULL, 'h' },
438 { "file", required_argument, NULL, 'f' },
439 { "product", required_argument, NULL, 'p' },
440 { "update", no_argument, NULL, 'u' },
441 { 0,0,0,0 }
442 };
443
444 c = getopt_long(argc, argv, "", long_opts, NULL);
445 switch (c)
446 {
447 case EOF:
448 break;
449 case 'h':
450 usage();
451 exit(EXIT_SUCCESS);
452 case 'f':
453 filename = optarg;
454 continue;
455 case 'p':
456 product = optarg;
457 continue;
458 case 'u':
459 update = TRUE;
460 continue;
461 }
462 break;
463 }
464
465 if (filename && product)
466 {
467 process_packages(filename, product, update);
468 }
469 else
470 {
471 usage();
472 exit(EXIT_FAILURE);
473 }
474 }
475
476 int main(int argc, char *argv[])
477 {
478 /* enable attest debugging hook */
479 dbg = pacman_dbg;
480 openlog("pacman", 0, LOG_DEBUG);
481
482 atexit(cleanup);
483
484 /* initialize library */
485 if (!library_init(NULL))
486 {
487 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
488 }
489 if (!lib->plugins->load(lib->plugins, NULL,
490 lib->settings->get_str(lib->settings, "attest.load", "sqlite")))
491 {
492 exit(SS_RC_INITIALIZATION_FAILED);
493 }
494 do_args(argc, argv);
495
496 exit(EXIT_SUCCESS);
497 }
498