]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/sw-collector/sw-collector.c
sw-collector: Moved info class to libimcv
[thirdparty/strongswan.git] / src / sw-collector / sw-collector.c
1 /*
2 * Copyright (C) 2017 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 <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <getopt.h>
21 #include <unistd.h>
22 #ifdef HAVE_SYSLOG
23 # include <syslog.h>
24 #endif
25
26 #include "sw_collector_db.h"
27 #include "sw_collector_history.h"
28 #include "sw_collector_rest_api.h"
29 #include "sw_collector_dpkg.h"
30 #
31 #include <library.h>
32 #include <utils/debug.h>
33 #include <utils/lexparser.h>
34
35 #include <swid_gen/swid_gen.h>
36
37 /**
38 * global debug output variables
39 */
40 static int debug_level = 2;
41 static bool stderr_quiet = FALSE;
42 static int count = 0;
43
44 typedef enum collector_op_t collector_op_t;
45
46 enum collector_op_t {
47 COLLECTOR_OP_EXTRACT,
48 COLLECTOR_OP_LIST,
49 COLLECTOR_OP_UNREGISTERED,
50 COLLECTOR_OP_GENERATE,
51 COLLECTOR_OP_MIGRATE
52 };
53
54 /**
55 * sw_collector dbg function
56 */
57 static void sw_collector_dbg(debug_t group, level_t level, char *fmt, ...)
58 {
59 va_list args;
60
61 if (level <= debug_level)
62 {
63 if (!stderr_quiet)
64 {
65 va_start(args, fmt);
66 vfprintf(stderr, fmt, args);
67 fprintf(stderr, "\n");
68 va_end(args);
69 }
70
71 #ifdef HAVE_SYSLOG
72 {
73 int priority = LOG_INFO;
74 char buffer[8192];
75 char *current = buffer, *next;
76
77 /* write in memory buffer first */
78 va_start(args, fmt);
79 vsnprintf(buffer, sizeof(buffer), fmt, args);
80 va_end(args);
81
82 /* do a syslog with every line */
83 while (current)
84 {
85 next = strchr(current, '\n');
86 if (next)
87 {
88 *(next++) = '\0';
89 }
90 syslog(priority, "%s\n", current);
91 current = next;
92 }
93 }
94 #endif /* HAVE_SYSLOG */
95 }
96 }
97
98 /**
99 * atexit handler
100 */
101 static void cleanup(void)
102 {
103 library_deinit();
104 #ifdef HAVE_SYSLOG
105 closelog();
106 #endif
107 }
108
109 /**
110 * Display usage of sw-collector command
111 */
112 static void usage(void)
113 {
114 printf("\
115 Usage:\n\
116 sw-collector --help\n\
117 sw-collector [--debug <level>] [--quiet] [--count <event count>]\n\
118 sw-collector [--debug <level>] [--quiet] [--installed|--removed] \
119 --list|-unregistered\n\
120 sw-collector [--debug <level>] [--quiet] [--installed|--removed] \
121 [--full] --generate\n\
122 sw-collector [--debug <level>] [--quiet] --migrate\n");
123 }
124
125 /**
126 * Parse command line options
127 */
128 static collector_op_t do_args(int argc, char *argv[], bool *full_tags,
129 sw_collector_db_query_t *query_type)
130 {
131 collector_op_t op = COLLECTOR_OP_EXTRACT;
132 bool installed = FALSE, removed = FALSE, full = FALSE;
133
134 /* reinit getopt state */
135 optind = 0;
136
137 while (TRUE)
138 {
139 int c;
140
141 struct option long_opts[] = {
142 { "help", no_argument, NULL, 'h' },
143 { "count", required_argument, NULL, 'c' },
144 { "debug", required_argument, NULL, 'd' },
145 { "full", no_argument, NULL, 'f' },
146 { "generate", no_argument, NULL, 'g' },
147 { "installed", no_argument, NULL, 'i' },
148 { "list", no_argument, NULL, 'l' },
149 { "migrate", no_argument, NULL, 'm' },
150 { "quiet", no_argument, NULL, 'q' },
151 { "removed", no_argument, NULL, 'r' },
152 { "unregistered", no_argument, NULL, 'u' },
153 { 0,0,0,0 }
154 };
155
156 c = getopt_long(argc, argv, "hc:d:fgilmqru", long_opts, NULL);
157 switch (c)
158 {
159 case EOF:
160 break;
161 case 'h':
162 usage();
163 exit(SUCCESS);
164 break;
165 case 'c':
166 count = atoi(optarg);
167 continue;
168 case 'd':
169 debug_level = atoi(optarg);
170 continue;
171 case 'f':
172 full = TRUE;
173 continue;
174 case 'g':
175 op = COLLECTOR_OP_GENERATE;
176 continue;
177 case 'i':
178 installed = TRUE;
179 continue;
180 case 'l':
181 op = COLLECTOR_OP_LIST;
182 continue;
183 case 'm':
184 op = COLLECTOR_OP_MIGRATE;
185 continue;
186 case 'q':
187 stderr_quiet = TRUE;
188 continue;
189 case 'r':
190 removed = TRUE;
191 continue;
192 case 'u':
193 op = COLLECTOR_OP_UNREGISTERED;
194 continue;
195 default:
196 usage();
197 exit(EXIT_FAILURE);
198 }
199 break;
200 }
201
202 if ((!installed && !removed) || (installed && removed))
203 {
204 *query_type = SW_QUERY_ALL;
205 }
206 else if (installed)
207 {
208 *query_type = SW_QUERY_INSTALLED;
209 }
210 else
211 {
212 *query_type = SW_QUERY_REMOVED;
213 }
214 *full_tags = full;
215
216 return op;
217 }
218
219 /**
220 * Extract software events from apt history log files
221 */
222 static int extract_history(sw_collector_db_t *db)
223 {
224 sw_collector_history_t *history = NULL;
225 uint32_t epoch, last_eid, eid = 0;
226 char *history_path, *last_time = NULL, rfc_time[21];
227 chunk_t *h, history_chunk, line, cmd;
228 int status = EXIT_FAILURE;
229 bool skip = TRUE;
230
231 /* open history file for reading */
232 history_path = lib->settings->get_str(lib->settings, "%s.history", NULL,
233 lib->ns);
234 if (!history_path)
235 {
236 fprintf(stderr, "sw-collector.history path not set.\n");
237 return EXIT_FAILURE;
238 }
239 h = chunk_map(history_path, FALSE);
240 if (!h)
241 {
242 fprintf(stderr, "opening '%s' failed: %s", history_path,
243 strerror(errno));
244 return EXIT_FAILURE;
245 }
246 history_chunk = *h;
247
248 /* Instantiate history extractor */
249 history = sw_collector_history_create(db, 1);
250 if (!history)
251 {
252 return EXIT_FAILURE;
253 }
254
255 /* retrieve last event in database */
256 if (!db->get_last_event(db, &last_eid, &epoch, &last_time) || !last_eid)
257 {
258 goto end;
259 }
260 DBG0(DBG_IMC, "Last-Event: %s, eid = %u, epoch = %u",
261 last_time, last_eid, epoch);
262
263 /* parse history file */
264 while (fetchline(&history_chunk, &line))
265 {
266 if (line.len == 0)
267 {
268 continue;
269 }
270 if (!extract_token(&cmd, ':', &line))
271 {
272 fprintf(stderr, "terminator symbol ':' not found.\n");
273 goto end;
274 }
275 if (match("Start-Date", &cmd))
276 {
277 if (!history->extract_timestamp(history, line, rfc_time))
278 {
279 goto end;
280 }
281
282 /* have we reached new history entries? */
283 if (skip && strcmp(rfc_time, last_time) > 0)
284 {
285 skip = FALSE;
286 }
287 if (skip)
288 {
289 continue;
290 }
291
292 /* insert new event into database */
293 eid = db->add_event(db, rfc_time);
294 if (!eid)
295 {
296 goto end;
297 }
298 DBG1(DBG_IMC, "Start-Date: %s, eid = %u, epoch = %u",
299 rfc_time, eid, epoch);
300 }
301 else if (skip)
302 {
303 /* skip old history entries which have already been processed */
304 continue;
305 }
306 else if (match("Install", &cmd))
307 {
308 DBG1(DBG_IMC, " Install:");
309 if (!history->extract_packages(history, line, eid, SW_OP_INSTALL))
310 {
311 goto end;
312 }
313 }
314 else if (match("Upgrade", &cmd))
315 {
316 DBG1(DBG_IMC, " Upgrade:");
317 if (!history->extract_packages(history, line, eid, SW_OP_UPGRADE))
318 {
319 goto end;
320 }
321 }
322 else if (match("Remove", &cmd))
323 {
324 DBG1(DBG_IMC, " Remove:");
325 if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
326 {
327 goto end;
328 }
329 }
330 else if (match("Purge", &cmd))
331 {
332 DBG1(DBG_IMC, " Purge:");
333 if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
334 {
335 goto end;
336 }
337 }
338 else if (match("End-Date", &cmd))
339 {
340 /* Process 'count' events at a time */
341 if (count > 0 && eid - last_eid == count)
342 {
343 fprintf(stderr, "added %d events\n", count);
344 goto end;
345 }
346 }
347 }
348
349 if (history->merge_installed_packages(history))
350 {
351 status = EXIT_SUCCESS;
352 }
353
354 end:
355 free(last_time);
356 history->destroy(history);
357 chunk_unmap(h);
358
359 return status;
360 }
361
362 /**
363 * List all endpoint software identifiers stored in local collector database
364 */
365 static int list_identifiers(sw_collector_db_t *db, sw_collector_db_query_t type)
366 {
367 enumerator_t *e;
368 char *name, *package, *version;
369 uint32_t sw_id, count = 0, installed_count = 0, removed_count, installed;
370
371 e = db->create_sw_enumerator(db, type, NULL);
372 if (!e)
373 {
374 return EXIT_FAILURE;
375 }
376 while (e->enumerate(e, &sw_id, &name, &package, &version, &installed))
377 {
378 printf("%s,%s,%s,%d\n", name, package, version, installed);
379 if (installed)
380 {
381 installed_count++;
382 }
383 count++;
384 }
385 removed_count = count - installed_count;
386 e->destroy(e);
387
388 switch (type)
389 {
390 case SW_QUERY_ALL:
391 DBG1(DBG_IMC, "retrieved %u software identities with %u installed "
392 "and %u removed", count, installed_count, removed_count);
393 break;
394 case SW_QUERY_INSTALLED:
395 DBG1(DBG_IMC, "retrieved %u installed software identities", count);
396 break;
397 case SW_QUERY_REMOVED:
398 DBG1(DBG_IMC, "retrieved %u removed software identities", count);
399 break;
400 }
401
402 return EXIT_SUCCESS;
403 }
404
405 static bool query_registry(sw_collector_rest_api_t *rest_api, bool installed)
406 {
407 sw_collector_db_query_t type;
408 enumerator_t *enumerator;
409 char *sw_id;
410 int count = 0;
411
412 type = installed ? SW_QUERY_INSTALLED : SW_QUERY_REMOVED;
413 enumerator = rest_api->create_sw_enumerator(rest_api, type);
414 if (!enumerator)
415 {
416 return FALSE;
417 }
418 while (enumerator->enumerate(enumerator, &sw_id))
419 {
420 printf("%s,%s\n", sw_id, installed ? "1" : "0");
421 count++;
422 }
423 enumerator->destroy(enumerator);
424 DBG1(DBG_IMC, "%d %s software identifiers not registered", count,
425 installed ? "installed" : "removed");
426 return TRUE;
427 }
428
429
430 /**
431 * List all endpoint software identifiers stored in local collector database
432 * that are not registered yet in central collelector database
433 */
434 static int unregistered_identifiers(sw_collector_db_t *db,
435 sw_collector_db_query_t type)
436 {
437 sw_collector_rest_api_t *rest_api;
438 int status = EXIT_SUCCESS;
439
440 rest_api = sw_collector_rest_api_create(db);
441 if (!rest_api)
442 {
443 return EXIT_FAILURE;
444 }
445
446 /* List installed software identifiers not registered centrally */
447 if (type != SW_QUERY_REMOVED && !query_registry(rest_api, TRUE))
448 {
449 status = EXIT_FAILURE;
450 }
451
452 /* List removed software identifiers not registered centrally */
453 if (type != SW_QUERY_INSTALLED && !query_registry(rest_api, FALSE))
454 {
455 status = EXIT_FAILURE;
456 }
457 rest_api->destroy(rest_api);
458
459 return status;
460 }
461
462 /**
463 * Generate ISO 19770-2:2015 SWID tags for [installed|removed|all]
464 * SW identifiers that are not registered centrally
465 */
466 static int generate_tags(sw_collector_db_t *db, bool full_tags,
467 sw_collector_db_query_t type)
468 {
469 swid_gen_t * swid_gen;
470 sw_collector_rest_api_t *rest_api;
471 char *name, *package, *version, *tag;
472 enumerator_t *enumerator;
473 uint32_t sw_id;
474 bool installed;
475 int count = 0, installed_count = 0, status = EXIT_FAILURE;
476
477 swid_gen = swid_gen_create();
478 rest_api = sw_collector_rest_api_create(db);
479 if (!rest_api)
480 {
481 goto end;
482 }
483
484 enumerator = rest_api->create_sw_enumerator(rest_api, type);
485 if (!enumerator)
486 {
487 goto end;
488 }
489 while (enumerator->enumerate(enumerator, &name))
490 {
491 sw_id = db->get_sw_id(db, name, &package, &version, NULL, &installed);
492 if (sw_id)
493 {
494 tag = swid_gen->generate_tag(swid_gen, name, package, version,
495 full_tags && installed, FALSE);
496 if (tag)
497 {
498 DBG2(DBG_IMC, " creating %s", name);
499 printf("%s\n", tag);
500 free(tag);
501 count++;
502 if (installed)
503 {
504 installed_count++;
505 }
506 }
507 free(package);
508 free(version);
509 }
510 }
511 enumerator->destroy(enumerator);
512 status = EXIT_SUCCESS;
513
514 switch (type)
515 {
516 case SW_QUERY_ALL:
517 DBG1(DBG_IMC, "created %d tags for unregistered software "
518 "identifiers with %d installed and %d removed", count,
519 installed_count, count - installed_count);
520 break;
521 case SW_QUERY_INSTALLED:
522 DBG1(DBG_IMC, "created %d tags for unregistered installed software "
523 "identifiers", count);
524 break;
525 case SW_QUERY_REMOVED:
526 DBG1(DBG_IMC, "created %d tags for unregistered removed software "
527 "identifiers", count);
528 break;
529 }
530
531 end:
532 swid_gen->destroy(swid_gen);
533 DESTROY_IF(rest_api);
534
535 return status;
536 }
537
538 /**
539 * Append missing architecture suffix to package entries in the database
540 */
541 static int migrate(sw_collector_db_t *db)
542 {
543 sw_collector_dpkg_t *dpkg;
544
545 char *package, *arch, *version;
546 char package_filter[BUF_LEN];
547 int res, count = 0;
548 int status = EXIT_SUCCESS;
549 enumerator_t *enumerator;
550
551 dpkg = sw_collector_dpkg_create();
552 if (!dpkg)
553 {
554 return FAILED;
555 }
556
557 enumerator = dpkg->create_sw_enumerator(dpkg);
558 while (enumerator->enumerate(enumerator, &package, &arch, &version))
559 {
560
561 /* Look for package names with architecture suffix */
562 snprintf(package_filter, BUF_LEN, "%s:%%", package);
563
564 res = db->update_package(db, package_filter, package);
565 if (res < 0)
566 {
567 status = EXIT_FAILURE;
568 break;
569 }
570 else if (res > 0)
571 {
572 count += res;
573 DBG2(DBG_IMC, "%s: removed arch suffix %d times", package, res);
574 }
575 }
576 enumerator->destroy(enumerator);
577 dpkg->destroy(dpkg);
578
579 DBG1(DBG_IMC, "migrated %d sw identifier records", count);
580
581 return status;
582 }
583
584
585 int main(int argc, char *argv[])
586 {
587 sw_collector_db_t *db = NULL;
588 sw_collector_db_query_t query_type;
589 collector_op_t op;
590 bool full_tags;
591 char *uri;
592 int status = EXIT_FAILURE;
593
594 op = do_args(argc, argv, &full_tags, &query_type);
595
596 /* enable sw_collector debugging hook */
597 dbg = sw_collector_dbg;
598 #ifdef HAVE_SYSLOG
599 openlog("sw-collector", 0, LOG_DEBUG);
600 #endif
601
602 atexit(cleanup);
603
604 /* initialize library */
605 if (!library_init(NULL, "sw-collector"))
606 {
607 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
608 }
609
610 /* load sw-collector plugins */
611 if (!lib->plugins->load(lib->plugins,
612 lib->settings->get_str(lib->settings, "%s.load", PLUGINS, lib->ns)))
613 {
614 exit(SS_RC_INITIALIZATION_FAILED);
615 }
616
617 /* connect to sw-collector database */
618 uri = lib->settings->get_str(lib->settings, "%s.database", NULL, lib->ns);
619 if (!uri)
620 {
621 fprintf(stderr, "sw-collector.database URI not set.\n");
622 exit(EXIT_FAILURE);
623 }
624 db = sw_collector_db_create(uri);
625 if (!db)
626 {
627 fprintf(stderr, "connection to sw-collector database failed.\n");
628 exit(EXIT_FAILURE);
629 }
630
631 switch (op)
632 {
633 case COLLECTOR_OP_EXTRACT:
634 status = extract_history(db);
635 break;
636 case COLLECTOR_OP_LIST:
637 status = list_identifiers(db, query_type);
638 break;
639 case COLLECTOR_OP_UNREGISTERED:
640 status = unregistered_identifiers(db, query_type);
641 break;
642 case COLLECTOR_OP_GENERATE:
643 status = generate_tags(db, full_tags, query_type);
644 break;
645 case COLLECTOR_OP_MIGRATE:
646 status = migrate(db);
647 break;
648 }
649 db->destroy(db);
650
651 exit(status);
652 }