2 * Copyright (C) 2017 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
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"
32 #include <utils/debug.h>
33 #include <utils/lexparser.h>
35 #include <swid_gen/swid_gen.h>
38 * global debug output variables
40 static int debug_level
= 2;
41 static bool stderr_quiet
= FALSE
;
44 typedef enum collector_op_t collector_op_t
;
49 COLLECTOR_OP_UNREGISTERED
,
50 COLLECTOR_OP_GENERATE
,
55 * sw_collector dbg function
57 static void sw_collector_dbg(debug_t group
, level_t level
, char *fmt
, ...)
61 if (level
<= debug_level
)
66 vfprintf(stderr
, fmt
, args
);
67 fprintf(stderr
, "\n");
73 int priority
= LOG_INFO
;
75 char *current
= buffer
, *next
;
77 /* write in memory buffer first */
79 vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
82 /* do a syslog with every line */
85 next
= strchr(current
, '\n');
90 syslog(priority
, "%s\n", current
);
94 #endif /* HAVE_SYSLOG */
101 static void cleanup(void)
110 * Display usage of sw-collector command
112 static void usage(void)
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");
126 * Parse command line options
128 static collector_op_t
do_args(int argc
, char *argv
[], bool *full_tags
,
129 sw_collector_db_query_t
*query_type
)
131 collector_op_t op
= COLLECTOR_OP_EXTRACT
;
132 bool installed
= FALSE
, removed
= FALSE
, full
= FALSE
;
134 /* reinit getopt state */
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' },
156 c
= getopt_long(argc
, argv
, "hc:d:fgilmqru", long_opts
, NULL
);
166 count
= atoi(optarg
);
169 debug_level
= atoi(optarg
);
175 op
= COLLECTOR_OP_GENERATE
;
181 op
= COLLECTOR_OP_LIST
;
184 op
= COLLECTOR_OP_MIGRATE
;
193 op
= COLLECTOR_OP_UNREGISTERED
;
202 if ((!installed
&& !removed
) || (installed
&& removed
))
204 *query_type
= SW_QUERY_ALL
;
208 *query_type
= SW_QUERY_INSTALLED
;
212 *query_type
= SW_QUERY_REMOVED
;
220 * Extract software events from apt history log files
222 static int extract_history(sw_collector_db_t
*db
)
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
;
231 /* open history file for reading */
232 history_path
= lib
->settings
->get_str(lib
->settings
, "%s.history", NULL
,
236 fprintf(stderr
, "sw-collector.history path not set.\n");
239 h
= chunk_map(history_path
, FALSE
);
242 fprintf(stderr
, "opening '%s' failed: %s", history_path
,
248 /* Instantiate history extractor */
249 history
= sw_collector_history_create(db
, 1);
255 /* retrieve last event in database */
256 if (!db
->get_last_event(db
, &last_eid
, &epoch
, &last_time
) || !last_eid
)
260 DBG0(DBG_IMC
, "Last-Event: %s, eid = %u, epoch = %u",
261 last_time
, last_eid
, epoch
);
263 /* parse history file */
264 while (fetchline(&history_chunk
, &line
))
270 if (!extract_token(&cmd
, ':', &line
))
272 fprintf(stderr
, "terminator symbol ':' not found.\n");
275 if (match("Start-Date", &cmd
))
277 if (!history
->extract_timestamp(history
, line
, rfc_time
))
282 /* have we reached new history entries? */
283 if (skip
&& strcmp(rfc_time
, last_time
) > 0)
292 /* insert new event into database */
293 eid
= db
->add_event(db
, rfc_time
);
298 DBG1(DBG_IMC
, "Start-Date: %s, eid = %u, epoch = %u",
299 rfc_time
, eid
, epoch
);
303 /* skip old history entries which have already been processed */
306 else if (match("Install", &cmd
))
308 DBG1(DBG_IMC
, " Install:");
309 if (!history
->extract_packages(history
, line
, eid
, SW_OP_INSTALL
))
314 else if (match("Upgrade", &cmd
))
316 DBG1(DBG_IMC
, " Upgrade:");
317 if (!history
->extract_packages(history
, line
, eid
, SW_OP_UPGRADE
))
322 else if (match("Remove", &cmd
))
324 DBG1(DBG_IMC
, " Remove:");
325 if (!history
->extract_packages(history
, line
, eid
, SW_OP_REMOVE
))
330 else if (match("Purge", &cmd
))
332 DBG1(DBG_IMC
, " Purge:");
333 if (!history
->extract_packages(history
, line
, eid
, SW_OP_REMOVE
))
338 else if (match("End-Date", &cmd
))
340 /* Process 'count' events at a time */
341 if (count
> 0 && eid
- last_eid
== count
)
343 fprintf(stderr
, "added %d events\n", count
);
349 if (history
->merge_installed_packages(history
))
351 status
= EXIT_SUCCESS
;
356 history
->destroy(history
);
363 * List all endpoint software identifiers stored in local collector database
365 static int list_identifiers(sw_collector_db_t
*db
, sw_collector_db_query_t type
)
368 char *name
, *package
, *version
;
369 uint32_t sw_id
, count
= 0, installed_count
= 0, removed_count
, installed
;
371 e
= db
->create_sw_enumerator(db
, type
, NULL
);
376 while (e
->enumerate(e
, &sw_id
, &name
, &package
, &version
, &installed
))
378 printf("%s,%s,%s,%d\n", name
, package
, version
, installed
);
385 removed_count
= count
- installed_count
;
391 DBG1(DBG_IMC
, "retrieved %u software identities with %u installed "
392 "and %u removed", count
, installed_count
, removed_count
);
394 case SW_QUERY_INSTALLED
:
395 DBG1(DBG_IMC
, "retrieved %u installed software identities", count
);
397 case SW_QUERY_REMOVED
:
398 DBG1(DBG_IMC
, "retrieved %u removed software identities", count
);
405 static bool query_registry(sw_collector_rest_api_t
*rest_api
, bool installed
)
407 sw_collector_db_query_t type
;
408 enumerator_t
*enumerator
;
412 type
= installed
? SW_QUERY_INSTALLED
: SW_QUERY_REMOVED
;
413 enumerator
= rest_api
->create_sw_enumerator(rest_api
, type
);
418 while (enumerator
->enumerate(enumerator
, &sw_id
))
420 printf("%s,%s\n", sw_id
, installed
? "1" : "0");
423 enumerator
->destroy(enumerator
);
424 DBG1(DBG_IMC
, "%d %s software identifiers not registered", count
,
425 installed
? "installed" : "removed");
431 * List all endpoint software identifiers stored in local collector database
432 * that are not registered yet in central collelector database
434 static int unregistered_identifiers(sw_collector_db_t
*db
,
435 sw_collector_db_query_t type
)
437 sw_collector_rest_api_t
*rest_api
;
438 int status
= EXIT_SUCCESS
;
440 rest_api
= sw_collector_rest_api_create(db
);
446 /* List installed software identifiers not registered centrally */
447 if (type
!= SW_QUERY_REMOVED
&& !query_registry(rest_api
, TRUE
))
449 status
= EXIT_FAILURE
;
452 /* List removed software identifiers not registered centrally */
453 if (type
!= SW_QUERY_INSTALLED
&& !query_registry(rest_api
, FALSE
))
455 status
= EXIT_FAILURE
;
457 rest_api
->destroy(rest_api
);
463 * Generate ISO 19770-2:2015 SWID tags for [installed|removed|all]
464 * SW identifiers that are not registered centrally
466 static int generate_tags(sw_collector_db_t
*db
, bool full_tags
,
467 sw_collector_db_query_t type
)
469 swid_gen_t
* swid_gen
;
470 sw_collector_rest_api_t
*rest_api
;
471 char *name
, *package
, *version
, *tag
;
472 enumerator_t
*enumerator
;
475 int count
= 0, installed_count
= 0, status
= EXIT_FAILURE
;
477 swid_gen
= swid_gen_create();
478 rest_api
= sw_collector_rest_api_create(db
);
484 enumerator
= rest_api
->create_sw_enumerator(rest_api
, type
);
489 while (enumerator
->enumerate(enumerator
, &name
))
491 sw_id
= db
->get_sw_id(db
, name
, &package
, &version
, NULL
, &installed
);
494 tag
= swid_gen
->generate_tag(swid_gen
, name
, package
, version
,
495 full_tags
&& installed
, FALSE
);
498 DBG2(DBG_IMC
, " creating %s", name
);
511 enumerator
->destroy(enumerator
);
512 status
= EXIT_SUCCESS
;
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
);
521 case SW_QUERY_INSTALLED
:
522 DBG1(DBG_IMC
, "created %d tags for unregistered installed software "
523 "identifiers", count
);
525 case SW_QUERY_REMOVED
:
526 DBG1(DBG_IMC
, "created %d tags for unregistered removed software "
527 "identifiers", count
);
532 swid_gen
->destroy(swid_gen
);
533 DESTROY_IF(rest_api
);
539 * Append missing architecture suffix to package entries in the database
541 static int migrate(sw_collector_db_t
*db
)
543 sw_collector_dpkg_t
*dpkg
;
545 char *package
, *arch
, *version
;
546 char package_filter
[BUF_LEN
];
548 int status
= EXIT_SUCCESS
;
549 enumerator_t
*enumerator
;
551 dpkg
= sw_collector_dpkg_create();
557 enumerator
= dpkg
->create_sw_enumerator(dpkg
);
558 while (enumerator
->enumerate(enumerator
, &package
, &arch
, &version
))
561 /* Look for package names with architecture suffix */
562 snprintf(package_filter
, BUF_LEN
, "%s:%%", package
);
564 res
= db
->update_package(db
, package_filter
, package
);
567 status
= EXIT_FAILURE
;
573 DBG2(DBG_IMC
, "%s: removed arch suffix %d times", package
, res
);
576 enumerator
->destroy(enumerator
);
579 DBG1(DBG_IMC
, "migrated %d sw identifier records", count
);
585 int main(int argc
, char *argv
[])
587 sw_collector_db_t
*db
= NULL
;
588 sw_collector_db_query_t query_type
;
592 int status
= EXIT_FAILURE
;
594 op
= do_args(argc
, argv
, &full_tags
, &query_type
);
596 /* enable sw_collector debugging hook */
597 dbg
= sw_collector_dbg
;
599 openlog("sw-collector", 0, LOG_DEBUG
);
604 /* initialize library */
605 if (!library_init(NULL
, "sw-collector"))
607 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY
);
610 /* load sw-collector plugins */
611 if (!lib
->plugins
->load(lib
->plugins
,
612 lib
->settings
->get_str(lib
->settings
, "%s.load", PLUGINS
, lib
->ns
)))
614 exit(SS_RC_INITIALIZATION_FAILED
);
617 /* connect to sw-collector database */
618 uri
= lib
->settings
->get_str(lib
->settings
, "%s.database", NULL
, lib
->ns
);
621 fprintf(stderr
, "sw-collector.database URI not set.\n");
624 db
= sw_collector_db_create(uri
);
627 fprintf(stderr
, "connection to sw-collector database failed.\n");
633 case COLLECTOR_OP_EXTRACT
:
634 status
= extract_history(db
);
636 case COLLECTOR_OP_LIST
:
637 status
= list_identifiers(db
, query_type
);
639 case COLLECTOR_OP_UNREGISTERED
:
640 status
= unregistered_identifiers(db
, query_type
);
642 case COLLECTOR_OP_GENERATE
:
643 status
= generate_tags(db
, full_tags
, query_type
);
645 case COLLECTOR_OP_MIGRATE
:
646 status
= migrate(db
);