sw-collector: Moved to its own directory and added man page
authorAndreas Steffen <andreas.steffen@strongswan.org>
Sun, 16 Jul 2017 07:52:52 +0000 (09:52 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 18 Jul 2017 05:25:45 +0000 (07:25 +0200)
30 files changed:
configure.ac
src/Makefile.am
src/libimcv/plugins/imc_swima/Makefile.am
src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.c [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.h [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.c [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.h [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.h [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.c [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.h [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.c [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.h [deleted file]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_tables.sql [deleted file]
src/sw-collector/.gitignore [new file with mode: 0644]
src/sw-collector/Makefile.am [new file with mode: 0644]
src/sw-collector/sw-collector.8.in [new file with mode: 0644]
src/sw-collector/sw-collector.c [new file with mode: 0644]
src/sw-collector/sw_collector_db.c [new file with mode: 0644]
src/sw-collector/sw_collector_db.h [new file with mode: 0644]
src/sw-collector/sw_collector_dpkg.c [new file with mode: 0644]
src/sw-collector/sw_collector_dpkg.h [new file with mode: 0644]
src/sw-collector/sw_collector_history.c [new file with mode: 0644]
src/sw-collector/sw_collector_history.h [new file with mode: 0644]
src/sw-collector/sw_collector_info.c [new file with mode: 0644]
src/sw-collector/sw_collector_info.h [new file with mode: 0644]
src/sw-collector/sw_collector_rest_api.c [new file with mode: 0644]
src/sw-collector/sw_collector_rest_api.h [new file with mode: 0644]
src/sw-collector/sw_collector_tables.sql [new file with mode: 0644]

index 320c50b..c00cb80 100644 (file)
@@ -1956,6 +1956,7 @@ AC_CONFIG_FILES([
        src/checksum/Makefile
        src/conftest/Makefile
        src/pt-tls-client/Makefile
+       src/sw-collector/Makefile
        src/swanctl/Makefile
        scripts/Makefile
        testing/Makefile
@@ -1988,6 +1989,8 @@ AC_CONFIG_FILES([
        src/swanctl/swanctl.8
        src/swanctl/swanctl.conf.5.head
        src/swanctl/swanctl.conf.5.tail
+       src/pt-tls-client/pt-tls-client.1
+       src/sw-collector/sw-collector.8
 ])
 
 AC_OUTPUT
index df171b2..193b77c 100644 (file)
@@ -135,3 +135,7 @@ endif
 if USE_AIKGEN
   SUBDIRS += aikgen
 endif
+
+if USE_IMC_SWIMA
+  SUBDIRS += sw-collector
+endif
index e14556c..4a29e79 100644 (file)
@@ -19,8 +19,7 @@ $(swid_tag) : $(regid)__strongSwan.swidtag.in
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/libstrongswan \
        -I$(top_srcdir)/src/libtncif \
-       -I$(top_srcdir)/src/libimcv \
-       -DPLUGINS=\""random openssl sqlite curl"\"
+       -I$(top_srcdir)/src/libimcv
 
 AM_CFLAGS = \
        $(PLUGIN_CFLAGS) $(json_CFLAGS)
@@ -32,23 +31,3 @@ imc_swima_la_LIBADD = \
        $(top_builddir)/src/libstrongswan/libstrongswan.la
 imc_swima_la_SOURCES = imc_swima.c imc_swima_state.h imc_swima_state.c
 imc_swima_la_LDFLAGS = -module -avoid-version -no-undefined
-
-ipsec_PROGRAMS = sw-collector
-sw_collector_SOURCES = \
-       sw_collector/sw-collector.c \
-       sw_collector/sw_collector_db.h sw_collector/sw_collector_db.c \
-       sw_collector/sw_collector_dpkg.h sw_collector/sw_collector_dpkg.c \
-       sw_collector/sw_collector_history.h sw_collector/sw_collector_history.c \
-       sw_collector/sw_collector_info.h sw_collector/sw_collector_info.c \
-       sw_collector/sw_collector_rest_api.h sw_collector/sw_collector_rest_api.c
-
-sw_collector_LDADD = \
-       $(top_builddir)/src/libstrongswan/libstrongswan.la \
-       $(top_builddir)/src/libimcv/libimcv.la \
-       $(json_LIBS)
-
-sw-collector.o : $(top_builddir)/config.status
-
-templatesdir = $(pkgdatadir)/templates/database/sw-collector
-dist_templates_DATA = sw_collector/sw_collector_tables.sql
-
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c b/src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c
deleted file mode 100644 (file)
index d76f751..0000000
+++ /dev/null
@@ -1,632 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <getopt.h>
-#include <unistd.h>
-#ifdef HAVE_SYSLOG
-# include <syslog.h>
-#endif
-
-#include "sw_collector_info.h"
-#include "sw_collector_db.h"
-#include "sw_collector_history.h"
-#include "sw_collector_rest_api.h"
-#include "sw_collector_dpkg.h"
-#
-#include <library.h>
-#include <utils/debug.h>
-#include <utils/lexparser.h>
-
-#include <imv/imv_os_info.h>
-
-/**
- * global debug output variables
- */
-static int debug_level = 2;
-static bool stderr_quiet = FALSE;
-static int count = 0;
-
-typedef enum collector_op_t collector_op_t;
-
-enum collector_op_t {
-       COLLECTOR_OP_EXTRACT,
-       COLLECTOR_OP_LIST,
-       COLLECTOR_OP_UNREGISTERED,
-       COLLECTOR_OP_GENERATE,
-       COLLECTOR_OP_MIGRATE
-};
-
-/**
- * sw_collector dbg function
- */
-static void sw_collector_dbg(debug_t group, level_t level, char *fmt, ...)
-{
-       va_list args;
-
-       if (level <= debug_level)
-       {
-               if (!stderr_quiet)
-               {
-                       va_start(args, fmt);
-                       vfprintf(stderr, fmt, args);
-                       fprintf(stderr, "\n");
-                       va_end(args);
-               }
-
-#ifdef HAVE_SYSLOG
-               {
-                       int priority = LOG_INFO;
-                       char buffer[8192];
-                       char *current = buffer, *next;
-
-                       /* write in memory buffer first */
-                       va_start(args, fmt);
-                       vsnprintf(buffer, sizeof(buffer), fmt, args);
-                       va_end(args);
-
-                       /* do a syslog with every line */
-                       while (current)
-                       {
-                               next = strchr(current, '\n');
-                               if (next)
-                               {
-                                       *(next++) = '\0';
-                               }
-                               syslog(priority, "%s\n", current);
-                               current = next;
-                       }
-               }
-#endif /* HAVE_SYSLOG */
-       }
-}
-
-/**
- * atexit handler
- */
-static void cleanup(void)
-{
-       library_deinit();
-#ifdef HAVE_SYSLOG
-       closelog();
-#endif
-}
-
-/**
- * Display usage of sw-collector command
- */
-static void usage(void)
-{
-       printf("\
-Usage:\n\
-  sw-collector --help\n\
-  sw-collector [--debug <level>] [--quiet] [--count <event count>]\n\
-  sw-collector [--debug <level>] [--quiet] --list|-unregistered\n\
-  sw-collector [--debug <level>] [--quiet] ---generate|--migrate\n");
-}
-
-/**
- * Parse command line options
- */
-static collector_op_t do_args(int argc, char *argv[])
-{
-       collector_op_t op = COLLECTOR_OP_EXTRACT;
-
-       /* reinit getopt state */
-       optind = 0;
-
-       while (TRUE)
-       {
-               int c;
-
-               struct option long_opts[] = {
-                       { "help", no_argument, NULL, 'h' },
-                       { "count", required_argument, NULL, 'c' },
-                       { "debug", required_argument, NULL, 'd' },
-                       { "generate", no_argument, NULL, 'g' },
-                       { "list", no_argument, NULL, 'l' },
-                       { "migrate", no_argument, NULL, 'm' },
-                       { "quiet", no_argument, NULL, 'q' },
-                       { "unregistered", no_argument, NULL, 'u' },
-                       { 0,0,0,0 }
-               };
-
-               c = getopt_long(argc, argv, "hc:d:lmqu", long_opts, NULL);
-               switch (c)
-               {
-                       case EOF:
-                               break;
-                       case 'h':
-                               usage();
-                               exit(SUCCESS);
-                               break;
-                       case 'c':
-                               count = atoi(optarg);
-                               continue;
-                       case 'd':
-                               debug_level = atoi(optarg);
-                               continue;
-                       case 'g':
-                               op = COLLECTOR_OP_GENERATE;
-                               continue;
-                       case 'l':
-                               op = COLLECTOR_OP_LIST;
-                               continue;
-                       case 'm':
-                               op = COLLECTOR_OP_MIGRATE;
-                               continue;
-                       case 'q':
-                               stderr_quiet = TRUE;
-                               continue;
-                       case 'u':
-                               op = COLLECTOR_OP_UNREGISTERED;
-                               continue;
-                       default:
-                               usage();
-                               exit(EXIT_FAILURE);
-               }
-               break;
-       }
-       return op;
-}
-
-/**
- * Extract software events from apt history log files
- */
-static int extract_history(sw_collector_info_t *info, sw_collector_db_t *db)
-{
-       sw_collector_history_t *history = NULL;
-       uint32_t epoch, last_eid, eid = 0;
-       char *history_path, *os, *last_time = NULL, rfc_time[21];
-       chunk_t *h, history_chunk, line, cmd;
-       os_type_t os_type;
-       int status = EXIT_FAILURE;
-       bool skip = TRUE;
-
-       /* check if OS supports apg/dpkg history logs */
-       info->get_os(info, &os);
-       os_type = info->get_os_type(info);
-
-       if (os_type !=  OS_TYPE_DEBIAN && os_type != OS_TYPE_UBUNTU)
-       {
-               DBG1(DBG_IMC, "%.*s not supported", os);
-               return EXIT_FAILURE;
-       }
-
-       /* open history file for reading */
-       history_path= lib->settings->get_str(lib->settings, "%s.history", NULL,
-                                                                                lib->ns);
-       if (!history_path)
-       {
-               fprintf(stderr, "sw-collector.history path not set.\n");
-               return EXIT_FAILURE;
-       }
-       h = chunk_map(history_path, FALSE);
-       if (!h)
-       {
-               fprintf(stderr, "opening '%s' failed: %s", history, strerror(errno));
-               return EXIT_FAILURE;
-       }
-       history_chunk = *h;
-
-       /* Instantiate history extractor */
-       history = sw_collector_history_create(info, db, 1);
-
-       /* retrieve last event in database */
-       if (!db->get_last_event(db, &last_eid, &epoch, &last_time) || !last_eid)
-       {
-               goto end;
-       }
-       DBG0(DBG_IMC, "Last-Event: %s, eid = %u, epoch = %u",
-                                  last_time, last_eid, epoch);
-
-       /* parse history file */
-       while (fetchline(&history_chunk, &line))
-       {
-               if (line.len == 0)
-               {
-                       continue;
-               }
-               if (!extract_token(&cmd, ':', &line))
-               {
-                       fprintf(stderr, "terminator symbol ':' not found.\n");
-                       goto end;
-               }
-               if (match("Start-Date", &cmd))
-               {
-                       if (!history->extract_timestamp(history, line, rfc_time))
-                       {
-                               goto end;
-                       }
-
-                       /* have we reached new history entries? */
-                       if (skip && strcmp(rfc_time, last_time) > 0)
-                       {
-                               skip = FALSE;
-                       }
-                       if (skip)
-                       {
-                               continue;
-                       }
-
-                       /* insert new event into database */
-                       eid = db->add_event(db, rfc_time);
-                       if (!eid)
-                       {
-                               goto end;
-                       }
-                       DBG1(DBG_IMC, "Start-Date: %s, eid = %u, epoch = %u",
-                                                  rfc_time, eid, epoch);
-               }
-               else if (skip)
-               {
-                       /* skip old history entries which have already been processed */
-                       continue;
-               }
-               else if (match("Install", &cmd))
-               {
-                       DBG1(DBG_IMC, "  Install:");
-                       if (!history->extract_packages(history, line, eid, SW_OP_INSTALL))
-                       {
-                               goto end;
-                       }
-               }
-               else if (match("Upgrade", &cmd))
-               {
-                       DBG1(DBG_IMC, "  Upgrade:");
-                       if (!history->extract_packages(history, line, eid, SW_OP_UPGRADE))
-                       {
-                               goto end;
-                       }
-               }
-               else if (match("Remove", &cmd))
-               {
-                       DBG1(DBG_IMC, "  Remove:");
-                       if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
-                       {
-                               goto end;
-                       }
-               }
-               else if (match("Purge", &cmd))
-               {
-                       DBG1(DBG_IMC, "  Purge:");
-                       if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
-                       {
-                               goto end;
-                       }
-               }
-               else if (match("End-Date", &cmd))
-               {
-                       /* Process 'count' events at a time */
-                       if (count > 0 && eid - last_eid == count)
-                       {
-                               fprintf(stderr, "added %d events\n", count);
-                               goto end;
-                       }
-               }
-       }
-
-       if (history->merge_installed_packages(history))
-       {
-               status = EXIT_SUCCESS;
-       }
-
-end:
-       free(last_time);
-       history->destroy(history);
-       chunk_unmap(h);
-
-       return status;
-}
-
-/**
- * List all endpoint software identifiers stored in local collector database
- */
-static int list_identifiers(sw_collector_db_t *db)
-{
-       enumerator_t *e;
-       char *name, *package, *version;
-       uint32_t sw_id, count = 0, installed_count = 0, installed;
-
-       e = db->create_sw_enumerator(db, SW_QUERY_ALL, NULL);
-       if (!e)
-       {
-               return EXIT_FAILURE;
-       }
-       while (e->enumerate(e, &sw_id, &name, &package, &version, &installed))
-       {
-               printf("%s,%s,%s,%d\n", name, package, version, installed);
-               if (installed)
-               {
-                       installed_count++;
-               }
-               count++;
-       }
-       e->destroy(e);
-       DBG1(DBG_IMC, "retrieved %u software identities with %u installed and %u "
-                                 "deleted", count, installed_count, count - installed_count);
-
-       return EXIT_SUCCESS;
-}
-
-static bool query_registry(sw_collector_rest_api_t *rest_api, bool installed)
-{
-       sw_collector_db_query_t type;
-       enumerator_t *enumerator;
-       char *sw_id;
-       int count = 0;
-
-       type = installed ? SW_QUERY_INSTALLED : SW_QUERY_DELETED;
-       enumerator = rest_api->create_sw_enumerator(rest_api, type);
-       if (!enumerator)
-       {
-               return FALSE;
-       }
-       while (enumerator->enumerate(enumerator, &sw_id))
-       {
-               printf("%s,%s\n", sw_id, installed ? "1" : "0");
-               count++;
-       }
-       enumerator->destroy(enumerator);
-       DBG1(DBG_IMC, "%d %s software identifiers not registered", count,
-                                  installed ? "installed" : "deleted");
-       return TRUE;
-}
-
-
-/**
- * List all endpoint software identifiers stored in local collector database
- * that are not registered yet in central collelector database
- */
-static int unregistered_identifiers(sw_collector_db_t *db)
-{
-       sw_collector_rest_api_t *rest_api;
-       int status = EXIT_SUCCESS;
-
-       rest_api = sw_collector_rest_api_create(db);
-       if (!rest_api)
-       {
-               return EXIT_FAILURE;
-       }
-
-       /* List installed software identifiers not registered centrally */
-       if (!query_registry(rest_api, TRUE))
-       {
-               status = EXIT_FAILURE;
-       }
-
-       /* List deleted software identifiers not registered centrally */
-       if (!query_registry(rest_api, FALSE))
-       {
-               status = EXIT_FAILURE;
-       }
-       rest_api->destroy(rest_api);
-
-       return status;
-}
-
-/**
- * Generate a minimalistic ISO 19770-2:2015 SWID tag
- */
-static char* generate_tag(char *name, char *package, char *version,
-                                                 char* entity, char *regid, char *product)
-{
-       char *tag_id, *tag;
-       int res;
-
-       tag_id = strstr(name, "__");
-       if (!tag_id)
-       {
-               return NULL;
-       }
-       tag_id += 2;
-
-       res = asprintf(&tag, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-                       "<SoftwareIdentity name=\"%s\" tagId=\"%s\" version=\"%s\" "
-                       "versionScheme=\"alphanumeric\" "
-                       "xmlns=\"http://standards.iso.org/iso/19770/-2/2015/schema.xsd\">"
-                       "<Entity name=\"%s\" regid=\"%s\" role=\"tagCreator\"/>"
-                       "<Meta product=\"%s\"/>"
-                       "</SoftwareIdentity>",
-                        package, tag_id, version, entity, regid, product);
-
-       return (res == -1) ? NULL : tag;
-}
-
-/**
- * Generate a minimalistic ISO 19770-2:2015 SWID tag for
- * all deleted SW identifiers that are not registered centrally
- */
-static int generate_tags(sw_collector_info_t *info, sw_collector_db_t *db)
-{
-       sw_collector_rest_api_t *rest_api;
-       char *pos, *name, *package, *version, *entity, *regid, *product, *tag;
-       enumerator_t *enumerator;
-       uint32_t sw_id;
-       int status = EXIT_FAILURE;
-
-       entity = lib->settings->get_str(lib->settings, "%s.tag_creator.name",
-                                                                       "strongSwan Project", lib->ns);
-       regid  = lib->settings->get_str(lib->settings, "%s.tag_creator.regid",
-                                                                       "strongswan.org", lib->ns);
-       info->get_os(info, &product);
-
-       rest_api = sw_collector_rest_api_create(db);
-       if (!rest_api)
-       {
-               goto end;
-       }
-
-       enumerator = rest_api->create_sw_enumerator(rest_api, SW_QUERY_DELETED);
-       if (!enumerator)
-       {
-               goto end;
-       }
-       while (enumerator->enumerate(enumerator, &name))
-       {
-               sw_id = db->get_sw_id(db, name, &package, &version, NULL, NULL);
-               if (sw_id)
-               {
-                       /* Remove architecture from package name */
-                       pos = strchr(package, ':');
-                       if (pos)
-                       {
-                               *pos = '\0';
-                       }
-                       tag = generate_tag(name, package, version, entity, regid, product);
-                       if (tag)
-                       {
-                               printf("%s\n", tag);
-                               free(tag);
-                               count++;
-                       }
-                       free(package);
-                       free(version);
-               }
-       }
-       enumerator->destroy(enumerator);
-       status = EXIT_SUCCESS;
-       DBG1(DBG_IMC, "%d tags for deleted unregistered software identifiers",
-                                  count);
-
-end:
-       DESTROY_IF(rest_api);
-
-       return status;
-}
-
-/**
- * Append missing architecture suffix to package entries in the database
- */
-static int migrate(sw_collector_info_t *info, sw_collector_db_t *db)
-{
-       sw_collector_dpkg_t *dpkg;
-
-       char *package, *arch, *version;
-       char package_filter[BUF_LEN];
-       int res, count = 0;
-       int status = EXIT_SUCCESS;
-       enumerator_t *enumerator;
-
-       dpkg = sw_collector_dpkg_create();
-       if (!dpkg)
-       {
-               return FAILED;
-       }
-
-       enumerator = dpkg->create_sw_enumerator(dpkg);
-       while (enumerator->enumerate(enumerator, &package, &arch, &version))
-       {
-
-               /* Look for package names with architecture suffix */
-               snprintf(package_filter, BUF_LEN, "%s:%%", package);
-
-               res = db->update_package(db, package_filter, package);
-               if (res < 0)
-               {
-                               status = EXIT_FAILURE;
-                               break;
-               }
-               else if (res > 0)
-               {
-                       count += res;
-                       DBG2(DBG_IMC, "%s: removed arch suffix %d times", package, res);
-               }
-       }
-       enumerator->destroy(enumerator);
-       dpkg->destroy(dpkg);
-
-       DBG1(DBG_IMC, "migrated %d sw identifier records", count);
-       return status;
-}
-
-
-int main(int argc, char *argv[])
-{
-       sw_collector_db_t *db = NULL;
-       sw_collector_info_t *info;
-       collector_op_t op;
-       char *uri, *tag_creator;
-       int status = EXIT_FAILURE;
-
-       op = do_args(argc, argv);
-
-       /* enable sw_collector debugging hook */
-       dbg = sw_collector_dbg;
-#ifdef HAVE_SYSLOG
-       openlog("sw-collector", 0, LOG_DEBUG);
-#endif
-
-       atexit(cleanup);
-
-       /* initialize library */
-       if (!library_init(NULL, "sw-collector"))
-       {
-               exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
-       }
-
-       /* load sw-collector plugins */
-       if (!lib->plugins->load(lib->plugins,
-                       lib->settings->get_str(lib->settings, "%s.load", PLUGINS, lib->ns)))
-       {
-               exit(SS_RC_INITIALIZATION_FAILED);
-       }
-
-       /* connect to sw-collector database */
-       uri = lib->settings->get_str(lib->settings, "%s.database", NULL, lib->ns);
-       if (!uri)
-       {
-               fprintf(stderr, "sw-collector.database URI not set.\n");
-               exit(EXIT_FAILURE);
-       }
-       db = sw_collector_db_create(uri);
-       if (!db)
-       {
-               fprintf(stderr, "connection to sw-collector database failed.\n");
-               exit(EXIT_FAILURE);
-       }
-
-       /* Attach OS info */
-       tag_creator = lib->settings->get_str(lib->settings, "%s.tag_creator.regid",
-                                                                                "strongswan.org", lib->ns);
-       info = sw_collector_info_create(tag_creator);
-
-       switch (op)
-       {
-               case COLLECTOR_OP_EXTRACT:
-                       status = extract_history(info, db);
-                       break;
-               case COLLECTOR_OP_LIST:
-                       status = list_identifiers(db);
-                       break;
-               case COLLECTOR_OP_UNREGISTERED:
-                       status = unregistered_identifiers(db);
-                       break;
-               case COLLECTOR_OP_GENERATE:
-                       status = generate_tags(info, db);
-                       break;
-               case COLLECTOR_OP_MIGRATE:
-                       status = migrate(info, db);
-                       break;
-       }
-       db->destroy(db);
-       info->destroy(info);
-
-       exit(status);
-}
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.c b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.c
deleted file mode 100644 (file)
index 6f14818..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include "sw_collector_db.h"
-
-#include "swima/swima_event.h"
-
-typedef struct private_sw_collector_db_t private_sw_collector_db_t;
-
-/**
- * Private data of an sw_collector_db_t object.
- */
-struct private_sw_collector_db_t {
-
-       /**
-        * Public members of sw_collector_db_state_t
-        */
-       sw_collector_db_t public;
-
-       /**
-        * Epoch
-        */
-       uint32_t epoch;
-
-       /**
-        * Event ID of last event stored in database
-        */
-       uint32_t last_eid;
-
-       /**
-        * Software collector database
-        */
-       database_t *db;
-
-};
-
-METHOD(sw_collector_db_t, add_event, uint32_t,
-       private_sw_collector_db_t *this, char *timestamp)
-{
-       uint32_t eid = 0;
-
-       if (this->db->execute(this->db, &eid,
-                       "INSERT INTO events (epoch, timestamp) VALUES (?, ?)",
-                        DB_UINT, this->epoch, DB_TEXT, timestamp) != 1)
-       {
-               DBG1(DBG_IMC, "unable to insert event into database");
-               return 0;
-       }
-
-       return eid;
-}
-
-METHOD(sw_collector_db_t, get_last_event, bool,
-       private_sw_collector_db_t *this, uint32_t *eid, uint32_t *epoch,
-       char **last_time)
-{
-       char *timestamp;
-       enumerator_t *e;
-
-       e = this->db->query(this->db,
-                       "SELECT id, epoch, timestamp FROM events ORDER BY timestamp DESC",
-                       DB_UINT, DB_UINT, DB_TEXT);
-       if (!e)
-       {
-               DBG1(DBG_IMC, "database query for event failed");
-               return FALSE;
-       }
-       if (e->enumerate(e, eid, epoch, &timestamp))
-       {
-               if (last_time)
-               {
-                       *last_time = strdup(timestamp);
-               }
-       }
-       else
-       {
-               *eid = 0;
-       }
-       e->destroy(e);
-
-       return TRUE;
-}
-
-METHOD(sw_collector_db_t, add_sw_event, bool,
-       private_sw_collector_db_t *this, uint32_t eid, uint32_t sw_id,
-       uint8_t action)
-{
-       if (this->db->execute(this->db, NULL,
-                       "INSERT INTO sw_events (eid, sw_id, action) VALUES (?, ?, ?)",
-                        DB_UINT, eid, DB_UINT, sw_id, DB_UINT, action) != 1)
-       {
-               DBG1(DBG_IMC, "unable to insert sw_event into database");
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-METHOD(sw_collector_db_t, set_sw_id, uint32_t,
-       private_sw_collector_db_t *this, char *name,  char *package, char *version,
-       uint8_t source, bool installed)
-{
-       uint32_t sw_id;
-
-       if (this->db->execute(this->db, &sw_id,
-                       "INSERT INTO sw_identifiers "
-                       "(name, package, version, source, installed) VALUES (?, ?, ?, ?, ?)",
-                        DB_TEXT, name, DB_TEXT, package, DB_TEXT, version, DB_UINT, source,
-                        DB_UINT, installed) != 1)
-       {
-               DBG1(DBG_IMC, "unable to insert sw_id into database");
-               return 0;
-       }
-
-       return sw_id;
-}
-
-METHOD(sw_collector_db_t, get_sw_id, uint32_t,
-       private_sw_collector_db_t *this, char *name, char **package, char **version,
-       uint8_t *source, bool *installed)
-{
-       char *sw_package, *sw_version;
-       uint32_t sw_id = 0, sw_source, sw_installed;
-       enumerator_t *e;
-
-       /* Does software identifier already exist in database? */
-       e = this->db->query(this->db,
-                       "SELECT id, package, version, source, installed "
-                       "FROM sw_identifiers WHERE name = ?",
-                       DB_TEXT, name, DB_UINT, DB_TEXT, DB_TEXT, DB_UINT, DB_UINT);
-       if (!e)
-       {
-               DBG1(DBG_IMC, "database query for sw_identifier failed");
-               return 0;
-       }
-       if (e->enumerate(e, &sw_id, &sw_package, &sw_version, &sw_source,
-                                               &sw_installed))
-       {
-               if (package)
-               {
-                       *package = strdup(sw_package);
-               }
-               if (version)
-               {
-                       *version = strdup(sw_version);
-               }
-               if (source)
-               {
-                       *source = sw_source;
-               }
-               if (installed)
-               {
-                       *installed = sw_installed;
-               }
-       }
-       e->destroy(e);
-
-       return sw_id;
-}
-
-METHOD(sw_collector_db_t, get_sw_id_count, uint32_t,
-       private_sw_collector_db_t *this, sw_collector_db_query_t type)
-{
-       uint32_t count, installed;
-       enumerator_t *e;
-
-       if (type == SW_QUERY_ALL)
-       {
-               e = this->db->query(this->db,
-                       "SELECT COUNT(installed) FROM sw_identifiers", DB_UINT);
-       }
-       else
-       {
-               installed = (type == SW_QUERY_INSTALLED);
-               e = this->db->query(this->db,
-                       "SELECT COUNT(installed) FROM sw_identifiers WHERE installed = ?",
-                        DB_UINT, installed, DB_UINT);
-       }
-
-       if (!e)
-       {
-               DBG1(DBG_IMC, "database query for sw_identifier count failed");
-               return 0;
-       }
-       if (!e->enumerate(e, &count))
-       {
-               count = 0;
-       }
-       e->destroy(e);
-
-       return count;
-}
-
-METHOD(sw_collector_db_t, update_sw_id, bool,
-       private_sw_collector_db_t *this, uint32_t sw_id, char *name, char *version,
-       bool installed)
-{
-       int res;
-
-       if (name && version)
-       {
-               res = this->db->execute(this->db, NULL,
-                       "UPDATE sw_identifiers SET name = ?, version = ?, installed = ? "
-                       "WHERE id = ?", DB_TEXT, name, DB_TEXT, version, DB_UINT, installed,
-                        DB_UINT, sw_id);
-       }
-       else
-       {
-               res = this->db->execute(this->db, NULL,
-                       "UPDATE sw_identifiers SET installed = ? WHERE id = ?",
-                        DB_UINT, installed, DB_UINT, sw_id);
-       }
-       if (res != 1)
-       {
-               DBG1(DBG_IMC, "unable to update software identifier in database");
-               return FALSE;
-       }
-       return TRUE;
-}
-
-METHOD(sw_collector_db_t, update_package, int,
-       private_sw_collector_db_t *this, char *package_filter, char *package)
-{
-       int count;
-
-       count = this->db->execute(this->db, NULL,
-                       "UPDATE sw_identifiers SET package = ? WHERE package LIKE ?",
-                        DB_TEXT, package, DB_TEXT, package_filter);
-       if (count < 0)
-       {
-               DBG1(DBG_IMC, "unable to update package name in database");
-       }
-
-       return count;
-}
-
-METHOD(sw_collector_db_t, create_sw_enumerator, enumerator_t*,
-       private_sw_collector_db_t *this, sw_collector_db_query_t type, char *package)
-{
-       enumerator_t *e;
-       u_int installed;
-
-       if (type == SW_QUERY_ALL)
-       {
-               if (package)
-               {
-                       e = this->db->query(this->db,
-                               "SELECT id, name, package, version, installed "
-                               "FROM sw_identifiers WHERE package = ? ORDER BY name ASC",
-                                DB_TEXT, package, DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
-               }
-               else
-               {
-                       e = this->db->query(this->db,
-                               "SELECT id, name, package, version, installed "
-                               "FROM sw_identifiers ORDER BY name ASC",
-                                DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
-               }
-       }
-       else
-       {
-               installed = (type == SW_QUERY_INSTALLED);
-
-               if (package)
-               {
-                       e = this->db->query(this->db,
-                               "SELECT id, name, package, version, installed "
-                               "FROM sw_identifiers WHERE package = ? AND installed = ? "
-                               "ORDER BY name ASC", DB_TEXT, package, DB_UINT, installed,
-                                DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
-               }
-               else
-               {
-                       e = this->db->query(this->db,
-                               "SELECT id, name, package, version, installed "
-                               "FROM sw_identifiers WHERE installed = ? ORDER BY name ASC",
-                                DB_UINT, installed, DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
-               }
-       }
-       if (!e)
-       {
-               DBG1(DBG_IMC, "database query for sw_identifier count failed");
-               return NULL;
-       }
-
-       return e;
-}
-
-METHOD(sw_collector_db_t, destroy, void,
-       private_sw_collector_db_t *this)
-{
-       this->db->destroy(this->db);
-       free(this);
-}
-
-/**
- * Described in header.
- */
-sw_collector_db_t *sw_collector_db_create(char *uri)
-{
-       private_sw_collector_db_t *this;
-       uint32_t first_eid, last_eid;
-       char *first_time;
-
-       INIT(this,
-               .public = {
-                       .add_event = _add_event,
-                       .get_last_event = _get_last_event,
-                       .add_sw_event = _add_sw_event,
-                       .set_sw_id = _set_sw_id,
-                       .get_sw_id = _get_sw_id,
-                       .get_sw_id_count = _get_sw_id_count,
-                       .update_sw_id = _update_sw_id,
-                       .update_package = _update_package,
-                       .create_sw_enumerator = _create_sw_enumerator,
-                       .destroy = _destroy,
-               },
-               .db = lib->db->create(lib->db, uri),
-       );
-
-       if (!this->db)
-       {
-               DBG1(DBG_IMC, "opening database URI '%s' failed", uri);
-               return NULL;
-       }
-
-       /* Retrieve last event in database */
-       if (!get_last_event(this, &last_eid, &this->epoch, NULL))
-       {
-               destroy(this);
-               return NULL;
-       }
-
-       /* Create random epoch and first event if no events exist yet */
-       if (!last_eid)
-       {
-               rng_t *rng;
-
-               rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
-               if (!rng ||
-                       !rng->get_bytes(rng, sizeof(uint32_t), (uint8_t*)&this->epoch))
-               {
-                       DESTROY_IF(rng);
-                       destroy(this);
-                       DBG1(DBG_IMC, "generating random epoch value failed");
-                       return NULL;
-               }
-               rng->destroy(rng);
-
-               /* strongTNC workaround - limit epoch to 31 bit unsigned integer */
-               this->epoch &= 0x7fffffff;
-
-               /* Create first event when the OS was installed */
-               first_time = lib->settings->get_str(lib->settings,
-                                               "sw-collector.first_time", "0000-00-00T00:00:00Z");
-               first_eid = add_event(this, first_time);
-               if (!first_eid)
-               {
-                       destroy(this);
-                       return NULL;
-               }
-               DBG0(DBG_IMC, "First-Date: %s, eid = %u, epoch = %u",
-                                          first_time, first_eid, this->epoch);
-       }
-
-       return &this->public;
-}
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.h b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.h
deleted file mode 100644 (file)
index 7aa41b0..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup sw_collector_db_t sw_collector_db
- * @{ @ingroup imc_swima
- */
-
-#ifndef SW_COLLECTOR_DB_H_
-#define SW_COLLECTOR_DB_H_
-
-#include <library.h>
-
-typedef struct sw_collector_db_t sw_collector_db_t;
-typedef enum sw_collector_db_query_t sw_collector_db_query_t;
-
-/**
- * Type of software identifier queries
- */
-enum sw_collector_db_query_t {
-       SW_QUERY_ALL,
-       SW_QUERY_INSTALLED,
-       SW_QUERY_DELETED
-};
-
-/**
- * Software collector database object
- */
-struct sw_collector_db_t {
-
-       /**
-        * bAdd event to database
-        *
-        * @param timestamp             Timestamp in 20 octet RFC 3339 format
-        * @return                              Primary key pointing to event ID or 0 if failed
-        */
-       uint32_t (*add_event)(sw_collector_db_t *this, char *timestamp);
-
-       /**
-        * Get last event, zero EID if none exists
-        *
-        * @param eid                   Primary key pointing to last event
-        * @param epoch                 Epoch
-        * @param last_time             Timestamp in 20 octet RFC 3339 format of last event
-        * @return                              
-        */
-       bool (*get_last_event)(sw_collector_db_t *this, uint32_t *eid,
-                                                          uint32_t *epoch, char **last_time);
-
-       /**
-        * Add software identifier event to database
-        *
-        * @param eid                   Foreign key pointing to an event ID
-        * @param sw_id                 Foreign key pointing to a software identifier
-        * @param action                1 for CREATION, 2 for deletion
-        * @return                              TRUE if successful
-        */
-       bool (*add_sw_event)(sw_collector_db_t *this, uint32_t eid, uint32_t sw_id,
-                                                uint8_t action);
-
-       /**
-        * Set software_identifier, checking if the identifier already exists
-        *
-        * @param name                  Software identifier
-        * @param package               Software package
-        * @param version               Version of software package
-        * @param source                Source ID of the software collector
-        * @param installed             Installation status to be set, TRUE if installed
-        * @return                              Primary key pointing to SW ID or 0 if failed
-        */
-       uint32_t (*set_sw_id)(sw_collector_db_t *this, char *name, char *package,
-                                                 char *version, uint8_t source, bool installed);
-
-       /**
-        * Get software_identifier record
-        *
-        * @param name                  Software identifier
-        * @param package               Software package
-        * @param version               Version of software package
-        * @param source                Source ID of the software collector
-        * @param installed             Installation status
-        * @return                              Primary key pointing to SW ID or 0 if failed
-        */
-       uint32_t (*get_sw_id)(sw_collector_db_t *this, char *name, char **package,
-                                                 char **version, uint8_t *source, bool *installed);
-
-       /**
-        * Get number of installed or deleted software identifiers
-        *
-        * @param type                  Query type (ALL, INSTALLED, DELETED)
-        * @return                              Count
-        */
-       uint32_t (*get_sw_id_count)(sw_collector_db_t *this,
-                                                               sw_collector_db_query_t type);
-
-       /**
-        * Update the software identifier version
-        *
-        * @param sw_id                 Primary key of software identifier
-        * @param name                  Software identifier
-        * @param version               Package version
-        * @param installed             Installation status
-        * @return                              TRUE if update successful
-        */
-       bool (*update_sw_id)(sw_collector_db_t *this, uint32_t sw_id, char *name,
-                                                char *version, bool installed);
-
-       /**
-        * Update the package name
-        *
-        * @param package_filter        Package name[s] to be changed
-        * @param package                       New package name
-        * @return                                      TRUE if update successful
-        */
-       int (*update_package)(sw_collector_db_t *this, char *package_filter,
-                                                                                                  char *package);
-
-       /**
-        * Enumerate over all collected [installed] software identities
-        *
-        * @param type                  Query type (ALL, INSTALLED, DELETED)
-        * @param package               If not NULL enumerate over all package versions
-        * @return                              Enumerator
-        */
-       enumerator_t* (*create_sw_enumerator)(sw_collector_db_t *this,
-                                                                                 sw_collector_db_query_t type,
-                                                                                 char *package);
-
-       /**
-        * Destroy sw_collector_db_t object
-        */
-       void (*destroy)(sw_collector_db_t *this);
-
-};
-
-/**
- * Create an sw_collector_db_t instance
- *
- * @param uri                          database URI
- */
-sw_collector_db_t* sw_collector_db_create(char *uri);
-
-#endif /** SW_COLLECTOR_DB_H_ @}*/
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.c b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.c
deleted file mode 100644 (file)
index b5a8582..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-
-#include "sw_collector_dpkg.h"
-
-typedef struct private_sw_collector_dpkg_t private_sw_collector_dpkg_t;
-
-/**
- * Private data of an sw_collector_dpkg_t object.
- */
-struct private_sw_collector_dpkg_t {
-
-       /**
-        * Public members of sw_collector_dpkg_state_t
-        */
-       sw_collector_dpkg_t public;
-
-};
-
-typedef struct {
-       /** public enumerator interface */
-       enumerator_t public;
-       /** dpkg output stream */
-       FILE *file;
-       /** current dpkg output line */
-       char line[BUF_LEN];
-} dpkg_enumerator_t;
-
-METHOD(enumerator_t, enumerate, bool,
-       dpkg_enumerator_t *this, va_list args)
-{
-       char **package, **arch, **version, *state, *pos;
-
-       VA_ARGS_VGET(args, package, arch, version);
-
-       while (TRUE)
-       {
-               if (!fgets(this->line, BUF_LEN, this->file))
-               {
-                       return FALSE;
-               }
-
-               *package = this->line;
-               pos = strchr(this->line, '\t');
-               if (!pos)
-               {
-                       return FALSE;
-               }
-               *pos = '\0';
-
-               *arch = ++pos;
-               pos = strchr(pos, '\t');
-               if (!pos)
-               {
-                       return FALSE;
-               }
-               *pos = '\0';
-
-               *version = ++pos;
-               pos = strchr(pos, '\t');
-               if (!pos)
-               {
-                       return FALSE;
-               }
-               *pos = '\0';
-
-               state = ++pos;
-               pos = strchr(pos, '\n');
-               if (!pos)
-               {
-                       return FALSE;
-               }
-               *pos = '\0';
-
-               if (streq(state, "install ok installed"))
-               {
-                       return TRUE;
-               }
-       }
-}
-
-METHOD(enumerator_t, enumerator_destroy, void,
-       dpkg_enumerator_t *this)
-{
-       pclose(this->file);
-       free(this);     
-}
-
-METHOD(sw_collector_dpkg_t, create_sw_enumerator, enumerator_t*,
-       private_sw_collector_dpkg_t *this)
-{
-       dpkg_enumerator_t *enumerator;
-       char cmd[] = "dpkg-query -W -f="
-                                "\'${Package}\t${Architecture}\t${Version}\t${Status}\n\'";
-       FILE *file;
-
-       file = popen(cmd, "r");
-       if (!file)
-       {
-               DBG1(DBG_IMC, "failed to run dpgk-query command");
-               return NULL;
-       }
-
-       INIT(enumerator,
-               .public = {
-                       .enumerate = enumerator_enumerate_default,
-                       .venumerate = _enumerate,
-                       .destroy = _enumerator_destroy,
-               },
-               .file = file,
-       );
-
-       return &enumerator->public;
-}
-
-METHOD(sw_collector_dpkg_t, destroy, void,
-       private_sw_collector_dpkg_t *this)
-{
-       free(this);
-}
-
-/**
- * Described in header.
- */
-sw_collector_dpkg_t *sw_collector_dpkg_create(void)
-{
-       private_sw_collector_dpkg_t *this;
-
-       INIT(this,
-               .public = {
-                       .create_sw_enumerator = _create_sw_enumerator,
-                       .destroy = _destroy,
-               },
-       );
-
-       return &this->public;
-}
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.h b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.h
deleted file mode 100644 (file)
index eab792e..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup sw_collector_dpkg_t sw_collector_dpkg
- * @{ @ingroup sw_collector
- */
-
-#ifndef SW_COLLECTOR_DPKG_H_
-#define SW_COLLECTOR_DPKG_H_
-
-#include <library.h>
-
-typedef struct sw_collector_dpkg_t sw_collector_dpkg_t;
-
-/**
- * Software collector dpkg object
- */
-struct sw_collector_dpkg_t {
-
-       /**
-        * List of installed software identifiers managed by the
-        * Debian "dpkg" package manager
-        *
-        * @return                              Enumerator
-        */
-       enumerator_t* (*create_sw_enumerator)(sw_collector_dpkg_t *this);
-
-       /**
-        * Destroy sw_collector_dpkg_t object
-        */
-       void (*destroy)(sw_collector_dpkg_t *this);
-
-};
-
-/**
- * Create an sw_collector_dpkg_t instance
- */
-sw_collector_dpkg_t* sw_collector_dpkg_create(void);
-
-#endif /** SW_COLLECTOR_DPKG_H_ @}*/
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c
deleted file mode 100644 (file)
index 71f888c..0000000
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <time.h>
-
-#include "sw_collector_history.h"
-#include "sw_collector_dpkg.h"
-
-#include "swima/swima_event.h"
-
-typedef struct private_sw_collector_history_t private_sw_collector_history_t;
-
-/**
- * Private data of an sw_collector_history_t object.
- */
-struct private_sw_collector_history_t {
-
-       /**
-        * Public members of sw_collector_history_state_t
-        */
-       sw_collector_history_t public;
-
-       /**
-        * Software Event Source Number
-        */
-       uint8_t source;
-
-       /**
-        * Reference to OS info object
-        */
-       sw_collector_info_t *info;
-
-       /**
-        * Reference to collector database
-        */
-       sw_collector_db_t *db;
-
-};
-
-/**
- * Define auxiliary package_t list item object
- */
-typedef struct package_t package_t;
-
-struct package_t {
-       char *package;
-       char *version;
-       char *old_version;
-       char *sw_id;
-       char *old_sw_id;
-};
-
-/**
- * Create package_t list item object
- */
-static package_t* create_package(sw_collector_info_t *info, chunk_t package,
-                                                                chunk_t version, chunk_t old_version)
-{
-       package_t *this;
-
-       INIT(this,
-               .package = strndup(package.ptr, package.len),
-               .version = strndup(version.ptr, version.len),
-               .old_version = strndup(old_version.ptr, old_version.len),
-       )
-
-       this->sw_id = info->create_sw_id(info, this->package, this->version);
-       if (old_version.len)
-       {
-               this->old_sw_id = info->create_sw_id(info, this->package,
-                                                                                                  this->old_version);
-       }
-
-       return this;
-}
-
-/**
- * Free package_t list item object
- */
-static void free_package(package_t *this)
-{
-       if (this)
-       {
-               free(this->package);
-               free(this->version);
-               free(this->old_version);
-               free(this->sw_id);
-               free(this->old_sw_id);
-               free(this);
-       }
-}
-
-/**
- * Extract and parse a single package item
- */
-static package_t* extract_package(chunk_t item, sw_collector_info_t *info,
-                                                                                               sw_collector_history_op_t op)
-{
-       chunk_t package, package_stripped, version, old_version;
-       package_t *p;
-
-       /* extract package name */
-       if (!extract_token(&package, ' ', &item))
-       {
-               fprintf(stderr, "version not found.\n");
-               return NULL;
-       }
-       item = chunk_skip(item, 1);
-
-       /* strip architecture suffix if present */
-       if (extract_token(&package_stripped, ':', &package))
-       {
-               package = package_stripped;
-       }
-
-       /* extract versions */
-       version = old_version = chunk_empty;
-
-       if (item.len > 0)
-       {
-               if (extract_token(&version, ',', &item))
-               {
-                       eat_whitespace(&item);
-                       if (!match("automatic", &item))
-                       {
-                               old_version = version;
-                               version = item;
-                       }
-               }
-               else
-               {
-                       version = item;
-               }
-       }
-       p = create_package(info, package, version, old_version);
-
-       /* generate log entry */
-       if (op == SW_OP_UPGRADE)
-       {
-               DBG2(DBG_IMC, "    %s (%s, %s)", p->package, p->old_version, p->version);
-               DBG2(DBG_IMC, "      +%s", p->sw_id);
-               DBG2(DBG_IMC, "      -%s", p->old_sw_id);
-       }
-       else
-       {
-               DBG2(DBG_IMC, "    %s (%s)", p->package, p->version);
-               DBG2(DBG_IMC, "      %s%s", (op == SW_OP_INSTALL) ? "+" : "-", p->sw_id);
-       }
-
-       return p;
-}
-
-METHOD(sw_collector_history_t, extract_timestamp, bool,
-       private_sw_collector_history_t *this, chunk_t args, char *buf)
-{
-       struct tm loc, utc;
-       chunk_t t1, t2;
-       time_t t;
-
-       /* Break down local time with format t1 = yyyy-mm-dd and t2 = hh:mm:ss */
-       if (!eat_whitespace(&args) || !extract_token(&t1, ' ', &args) ||
-               !eat_whitespace(&args) || t1.len != 10 || args.len != 8)
-       {
-               DBG1(DBG_IMC, "unable to parse start-date");
-               return FALSE;
-       }
-       t2 = args;
-
-       if (sscanf(t1.ptr, "%4d-%2d-%2d",
-                                               &loc.tm_year, &loc.tm_mon, &loc.tm_mday) != 3)
-       {
-               DBG1(DBG_IMC, "unable to parse date format yyyy-mm-dd");
-               return FALSE;
-       }
-       loc.tm_year -= 1900;
-       loc.tm_mon  -= 1;
-       loc.tm_isdst = -1;
-
-       if (sscanf(t2.ptr, "%2d:%2d:%2d",
-                                               &loc.tm_hour, &loc.tm_min, &loc.tm_sec) != 3)
-       {
-               DBG1(DBG_IMC, "unable to parse time format hh:mm:ss");
-               return FALSE;
-       }
-
-       /* Convert from local time to UTC */
-       t = mktime(&loc);
-       gmtime_r(&t, &utc);
-       utc.tm_year += 1900;
-       utc.tm_mon  += 1;
-
-       /* Form timestamp according to RFC 3339 (20 characters) */
-       snprintf(buf, 21, "%4d-%02d-%02dT%02d:%02d:%02dZ",
-                        utc.tm_year, utc.tm_mon, utc.tm_mday,
-                        utc.tm_hour, utc.tm_min, utc.tm_sec);
-
-       return TRUE;
-}
-
-METHOD(sw_collector_history_t, extract_packages, bool,
-       private_sw_collector_history_t *this, chunk_t args, uint32_t eid,
-       sw_collector_history_op_t op)
-{
-       bool success = FALSE;
-       package_t *p = NULL;
-       chunk_t item;
-
-       eat_whitespace(&args);
-
-       while (extract_token(&item, ')', &args))
-       {
-               char *del_sw_id = NULL, *del_version = NULL;
-               char *nx, *px, *vx, *v1;
-               bool installed;
-               u_int sw_idx, ix;
-               uint32_t sw_id, sw_id_epoch_less = 0;
-               enumerator_t *e;
-
-               p = extract_package(item, this->info, op);
-               if (!p)
-               {
-                       goto end;
-               }
-
-               /* packages without version information cannot be handled */
-               if (strlen(p->version) == 0)
-               {
-                       free_package(p);
-                       continue;
-               }
-
-               switch (op)
-               {
-                       case SW_OP_REMOVE:
-                               /* prepare subsequent deletion sw event */
-                               del_sw_id = p->sw_id;
-                               del_version = p->version;
-                               break;
-                       case SW_OP_UPGRADE:
-                               /* prepare subsequent deletion sw event */
-                               del_sw_id = p->old_sw_id;
-                               del_version = p->old_version;
-                               /* fall through to next case */
-                       case SW_OP_INSTALL:
-                               sw_id = this->db->get_sw_id(this->db, p->sw_id, NULL, NULL,
-                                                                                       NULL, &installed);
-                               if (sw_id)
-                               {
-                                       /* sw identifier exists - update state to 'installed' */
-                                       if (installed)
-                                       {
-                                               /* this case should not occur */
-                                               DBG1(DBG_IMC, "  warning:  sw_id %d is already "
-                                                                         "installed", sw_id);
-                                       }
-                                       else if (!this->db->update_sw_id(this->db, sw_id, NULL,
-                                                                                                        NULL, TRUE))
-                                       {
-                                               goto end;
-                                       }
-                               }
-                               else
-                               {
-                                       /* new sw identifier - create with state 'installed' */
-                                       sw_id = this->db->set_sw_id(this->db, p->sw_id, p->package,
-                                                                                               p->version,     this->source, TRUE);
-                                       if (!sw_id)
-                                       {
-                                               goto end;
-                                       }
-                               }
-
-                               /* add creation sw event with current eid */
-                               if (!this->db->add_sw_event(this->db, eid, sw_id,
-                                                                       SWIMA_EVENT_ACTION_CREATION))
-                               {
-                                       goto end;
-                               }
-                               break;
-               }
-
-               if (op != SW_OP_INSTALL)
-               {
-                       sw_id = 0;
-
-                       /* look for existing installed package versions */
-                       e = this->db->create_sw_enumerator(this->db, SW_QUERY_INSTALLED,
-                                                                                          p->package);
-                       if (!e)
-                       {
-                               goto end;
-                       }
-
-                       while (e->enumerate(e, &sw_idx, &nx, &px, &vx, &ix))
-                       {
-                               if (streq(vx, del_version))
-                               {
-                                       /* full match with epoch */
-                                       sw_id = sw_idx;
-                                       break;
-                               }
-                               v1 = strchr(vx, ':');
-                               if (v1 && streq(++v1, del_version))
-                               {
-                                       /* match with stripped epoch */
-                                       sw_id_epoch_less = sw_idx;
-                               }
-                       }
-                       e->destroy(e);
-
-                       if (!sw_id && sw_id_epoch_less)
-                       {
-                               /* no full match - fall back to epoch-less match */
-                               sw_id = sw_id_epoch_less;
-                       }
-                       if (sw_id)
-                       {
-                               /* sw identifier exists - update state to 'deleted' */
-                               if (!this->db->update_sw_id(this->db, sw_id, NULL, NULL, FALSE))
-                               {
-                                       goto end;
-                               }
-                       }
-                       else
-                       {
-                               /* new sw identifier - create with state 'deleted' */
-                               sw_id = this->db->set_sw_id(this->db, del_sw_id, p->package,
-                                                                       del_version, this->source, FALSE);
-
-                               /* add creation sw event with eid = 1 */
-                               if (!sw_id || !this->db->add_sw_event(this->db, 1, sw_id,
-                                                                                       SWIMA_EVENT_ACTION_CREATION))
-                               {
-                                       goto end;
-                               }
-                       }
-
-                       /* add creation sw event with current eid */
-                       if (!this->db->add_sw_event(this->db, eid, sw_id,
-                                                               SWIMA_EVENT_ACTION_DELETION))
-                       {
-                               goto end;
-                       }
-               }
-               free_package(p);
-
-               if (args.len < 2)
-               {
-                       break;
-               }
-               args = chunk_skip(args, 2);
-       }
-       p = NULL;
-       success = TRUE;
-
-end:
-       free_package(p);
-
-       return success;
-}
-
-METHOD(sw_collector_history_t, merge_installed_packages, bool,
-       private_sw_collector_history_t *this)
-{
-       uint32_t sw_id, count = 0;
-       char *package, *arch, *version, *v1, *name, *n1;
-       bool installed, success = FALSE;
-       sw_collector_dpkg_t *dpkg;
-       enumerator_t *enumerator;
-
-       DBG1(DBG_IMC, "Merging:");
-
-       dpkg = sw_collector_dpkg_create();
-       if (!dpkg)
-       {
-               return FALSE;
-       }
-
-       enumerator = dpkg->create_sw_enumerator(dpkg);
-       while (enumerator->enumerate(enumerator, &package, &arch, &version))
-       {
-               name = this->info->create_sw_id(this->info, package, version);
-               DBG3(DBG_IMC, "  %s merged", name);
-
-               sw_id = this->db->get_sw_id(this->db, name, NULL, NULL, NULL,
-                                                                       &installed);
-               if (sw_id)
-               {
-                       if (!installed)
-                       {
-                               DBG1(DBG_IMC, "  warning: existing sw_id %u"
-                                                         " is not installed", sw_id);
-
-                               if (!this->db->update_sw_id(this->db, sw_id, name, version,
-                                                                                       TRUE))
-                               {
-                                       free(name);
-                                       goto end;
-                               }
-                       }
-               }
-               else
-               {
-                       /* check for a Debian epoch number */
-                       v1 = strchr(version, ':');
-                       if (v1)
-                       {
-                               /* check for existing and installed epoch-less version */
-                               n1 = this->info->create_sw_id(this->info, package, ++v1);
-                               sw_id = this->db->get_sw_id(this->db, n1, NULL, NULL, NULL,
-                                                                                       &installed);
-                               free(n1);
-
-                               if (sw_id && installed)
-                               {
-                                       /* add epoch to existing version */
-                                       if (!this->db->update_sw_id(this->db, sw_id, name, version,
-                                                                                               installed))
-                                       {
-                                               free(name);
-                                               goto end;
-                                       }
-                               }
-                               else
-                               {
-                                       sw_id = 0;
-                               }
-                       }
-               }
-
-               if (!sw_id)
-               {
-                       /* new sw identifier - create with state 'installed' */
-                       sw_id = this->db->set_sw_id(this->db, name, package, version,
-                                                                               this->source, TRUE);
-
-                       /* add creation sw event with eid = 1 */
-                       if (!sw_id || !this->db->add_sw_event(this->db, 1, sw_id,
-                                                                               SWIMA_EVENT_ACTION_CREATION))
-                       {
-                               free(name);
-                               goto end;
-                       }
-
-               }
-               free(name);
-               count++;
-       }
-       success = TRUE;
-
-       DBG1(DBG_IMC, "  merged %u installed packages, %u registered in database",
-                count, this->db->get_sw_id_count(this->db, SW_QUERY_INSTALLED));
-
-end:
-       enumerator->destroy(enumerator);
-       dpkg->destroy(dpkg);
-
-       return success;
-}
-
-METHOD(sw_collector_history_t, destroy, void,
-       private_sw_collector_history_t *this)
-{
-       free(this);
-}
-
-/**
- * Described in header.
- */
-sw_collector_history_t *sw_collector_history_create(sw_collector_info_t *info,
-                                                                                                       sw_collector_db_t *db,
-                                                                                                       uint8_t source)
-{
-       private_sw_collector_history_t *this;
-
-       INIT(this,
-               .public = {
-                       .extract_timestamp = _extract_timestamp,
-                       .extract_packages = _extract_packages,
-                       .merge_installed_packages = _merge_installed_packages,
-                       .destroy = _destroy,
-               },
-               .source = source,
-               .info = info,
-               .db = db,
-       );
-
-       return &this->public;
-}
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.h b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.h
deleted file mode 100644 (file)
index 857cc17..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup sw_collector_history_t sw_collector_history
- * @{ @ingroup sw_collector
- */
-
-#ifndef SW_COLLECTOR_HISTORY_H_
-#define SW_COLLECTOR_HISTORY_H_
-
-#include "sw_collector_history.h"
-#include "sw_collector_info.h"
-#include "sw_collector_db.h"
-
-#include <library.h>
-#include <utils/debug.h>
-#include <utils/lexparser.h>
-
-typedef struct sw_collector_history_t sw_collector_history_t;
-typedef enum sw_collector_history_op_t sw_collector_history_op_t;
-
-/**
- * Define major history event operations
- */
-enum sw_collector_history_op_t {
-       SW_OP_INSTALL,
-       SW_OP_UPGRADE,
-       SW_OP_REMOVE
-};
-
-/**
- * Software collector history object
- */
-struct sw_collector_history_t {
-
-       /**
-        * Extract timestamp from event in installation history
-        *
-        * @param args                  Arguments to be processed
-        * @param buf                   timestamp buffer for 21 byte RFC 3339 string
-        * @return                              TRUE if extraction succeeded
-        */
-       bool (*extract_timestamp)(sw_collector_history_t *this, chunk_t args,
-                                                         char *buf);
-
-       /**
-        * Extract packages from event in installation history 
-        *
-        * @param args                  Arguments to be processed
-        * @param eid                   Primary key pointing to current event
-        * @param op                    Extraction operation 
-        * @return                              TRUE if extraction succeeded
-        */
-       bool (*extract_packages)(sw_collector_history_t *this, chunk_t args,
-                                                        uint32_t eid, sw_collector_history_op_t op);
-
-       /**
-        * Merge packages from initial installation
-        *
-        * @return                              TRUE if merge succeeded
-        */
-       bool (*merge_installed_packages)(sw_collector_history_t *this);
-
-       /**
-        * Destroy sw_collector_history_t object
-        */
-       void (*destroy)(sw_collector_history_t *this);
-
-};
-
-/**
- * Create an sw_collector_history_t instance
- *
- * @param info                         Internal reference to collector info
- * @param db                           Internal reference to collector database
- * @param source                       Software event source number
- */
-sw_collector_history_t* sw_collector_history_create(sw_collector_info_t *info,
-                                                                                                       sw_collector_db_t *db,
-                                                                                                       uint8_t source);
-
-#endif /** SW_COLLECTOR_HISTORY_H_ @}*/
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.c b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.c
deleted file mode 100644 (file)
index b046450..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-
-#include "sw_collector_info.h"
-
-#include <library.h>
-#include <utils/lexparser.h>
-
-typedef struct private_sw_collector_info_t private_sw_collector_info_t;
-
-/**
- * Private data of an sw_collector_info_t object.
- */
-struct private_sw_collector_info_t {
-
-       /**
-        * Public members of sw_collector_info_state_t
-        */
-       sw_collector_info_t public;
-
-       /**
-        * tagCreator
-        */
-       char *tag_creator;
-
-       /**
-        * OS string 'Name_Version-Arch'
-        */
-       char *os;
-
-       /**
-        * Product string 'Name Version Arch'
-        */
-       char *product;
-
-       /**
-        * OS info about endpoint
-        */
-       imc_os_info_t *os_info;
-
-};
-
-/**
- * Replaces invalid character by a valid one
- */
-static void sanitize_uri(char *uri, char a, char b)
-{
-       char *pos = uri;
-
-       while (TRUE)
-       {
-               pos = strchr(pos, a);
-               if (!pos)
-               {
-                       break;
-               }
-               *pos = b;
-               pos++;
-       }
-}
-
-METHOD(sw_collector_info_t, get_os_type, os_type_t,
-       private_sw_collector_info_t *this)
-{
-       return this->os_info->get_type(this->os_info);
-}
-
-METHOD(sw_collector_info_t, get_os, char*,
-       private_sw_collector_info_t *this, char **product)
-{
-       if (product)
-       {
-               *product = this->product;
-       }
-       return this->os;
-}
-
-METHOD(sw_collector_info_t, create_sw_id, char*,
-       private_sw_collector_info_t *this, char *package, char *version)
-{
-       char *sw_id;
-
-       if (asprintf(&sw_id, "%s__%s-%s%s%s", this->tag_creator, this->os,
-                                package, strlen(version) ? "-" : "", version) == -1)
-       {
-               return NULL;
-       }
-       sanitize_uri(sw_id, ':', '~');
-       sanitize_uri(sw_id, '+', '~');
-
-       return sw_id;
-}
-
-METHOD(sw_collector_info_t, destroy, void,
-       private_sw_collector_info_t *this)
-{
-       this->os_info->destroy(this->os_info);
-       free(this->os);
-       free(this->product);
-       free(this->tag_creator);
-       free(this);
-}
-
-/**
- * Described in header.
- */
-sw_collector_info_t *sw_collector_info_create(char *tag_creator)
-{
-       private_sw_collector_info_t *this;
-       chunk_t os_name, os_version, os_arch;
-
-       INIT(this,
-               .public = {
-                       .get_os_type = _get_os_type,
-                       .get_os = _get_os,
-                       .create_sw_id = _create_sw_id,
-                       .destroy = _destroy,
-               },
-               .os_info = imc_os_info_create(),
-               .tag_creator = strdup(tag_creator),
-       );
-
-       os_name = this->os_info->get_name(this->os_info);
-       os_arch = this->os_info->get_version(this->os_info);
-
-       /* get_version() returns version followed by arch */ 
-       if (!extract_token(&os_version, ' ', &os_arch))
-       {
-               DBG1(DBG_IMC, "separation of OS version from arch failed");
-               destroy(this);
-               return NULL;
-       }
-
-       /* construct OS string */
-       if (asprintf(&this->os, "%.*s_%.*s-%.*s", os_name.len, os_name.ptr,
-                                                                                         os_version.len, os_version.ptr,
-                                                                                         os_arch.len, os_arch.ptr) == -1)
-       {
-               DBG1(DBG_IMC, "constructon of OS string failed");
-               destroy(this);
-               return NULL;
-       }
-
-       /* construct product string */
-       if (asprintf(&this->product, "%.*s %.*s %.*s", os_name.len, os_name.ptr,
-                                                                                         os_version.len, os_version.ptr,
-                                                                                         os_arch.len, os_arch.ptr) == -1)
-       {
-               DBG1(DBG_IMC, "constructon of product string failed");
-               destroy(this);
-               return NULL;
-       }
-
-       return &this->public;
-}
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.h b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.h
deleted file mode 100644 (file)
index a54d788..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup sw_collector_info_t sw_collector_info
- * @{ @ingroup sw_collector
- */
-
-#ifndef SW_COLLECTOR_INFO_H_
-#define SW_COLLECTOR_INFO_H_
-
-typedef struct sw_collector_info_t sw_collector_info_t;
-
-#include "imc/imc_os_info.h"
-
-struct sw_collector_info_t {
-
-       /**
-        * Get OS type
-        *
-        * @return                              OS type
-        */
-       os_type_t (*get_os_type)(sw_collector_info_t *this);
-
-       /**
-        * Get OS and product strings
-        *
-        * @param product               Product string 'Name Version Arch'
-        * @return                              OS string      'Name_Version-Arch'
-        */
-       char* (*get_os)(sw_collector_info_t *this, char **product);
-
-       /**
-        * Create software identifier including tagCreator and OS
-        *
-        * @param package               Package string
-        * @param version               Version string
-        * @return                              Software Identifier string
-        */
-       char* (*create_sw_id)(sw_collector_info_t *this, char *package,
-                                                                                                        char *version);
-
-       /**
-        * Destroy sw_collector_info_t object
-        */
-       void (*destroy)(sw_collector_info_t *this);
-
-};
-
-/**
- * Create an sw_collector_info_t instance
- *
- * @param tag_creator          Regid of tagCreator
- */
-sw_collector_info_t* sw_collector_info_create(char *tag_creator);
-
-#endif /** SW_COLLECTOR_INFO_H_ @}*/
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.c b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.c
deleted file mode 100644 (file)
index 6b9b7b9..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include "sw_collector_rest_api.h"
-
-#include <rest/rest.h>
-#include <utils/debug.h>
-
-typedef struct private_sw_collector_rest_api_t private_sw_collector_rest_api_t;
-
-/**
- * Private data of an sw_collector_rest_api_t object.
- */
-struct private_sw_collector_rest_api_t {
-
-       /**
-        * Public members of sw_collector_rest_api_state_t
-        */
-       sw_collector_rest_api_t public;
-
-       /**
-        * Software collector database
-        */
-       sw_collector_db_t *db;
-
-       /**
-        * REST API of central collector database
-        */
-       rest_t *rest_api;
-
-};
-
-/**
- * Put all locally retrieved software identifiers into a json object
- */
-static json_object* create_rest_request(private_sw_collector_rest_api_t *this,
-                                                                               sw_collector_db_query_t type)
-{
-       json_object *jrequest, *jarray, *jstring;
-       char *name, *package, *version;
-       uint32_t sw_id, i;
-       enumerator_t *e;
-
-       jrequest = json_object_new_object();
-       jarray = json_object_new_array();
-       json_object_object_add(jrequest, "data", jarray);
-
-       e = this->db->create_sw_enumerator(this->db, type, NULL);
-       if (!e)
-       {
-               return NULL;
-       }
-       while (e->enumerate(e, &sw_id, &name, &package, &version, &i))
-       {
-               jstring = json_object_new_string(name);
-               json_object_array_add(jarray, jstring);
-       }
-       e->destroy(e);
-
-       return jrequest;
-}
-
-typedef struct {
-       /** public enumerator interface */
-       enumerator_t public;
-       /** enumerated json array */
-       json_object *jarray;
-       /** current index +1, initialized at 0 */
-       int idx;
-} json_array_enumerator_t;
-
-METHOD(enumerator_t, enumerate, bool,
-       json_array_enumerator_t *this, va_list args)
-{
-       json_object *jvalue;
-       char **out;
-
-       VA_ARGS_VGET(args, out);
-
-       if (this->idx >= json_object_array_length(this->jarray))
-       {
-               return FALSE;
-       }
-
-       jvalue = json_object_array_get_idx(this->jarray, this->idx++);
-       if (json_object_get_type(jvalue) != json_type_string)
-       {
-               DBG1(DBG_IMC, "json_string element expected in json_array");
-               return FALSE;
-       }
-       *out = (char*)json_object_get_string(jvalue);
-
-       return TRUE;
-}
-
-METHOD(enumerator_t, enumerator_destroy, void,
-       json_array_enumerator_t *this)
-{
-       json_object_put(this->jarray);
-       free(this);     
-}
-
-METHOD(sw_collector_rest_api_t, create_sw_enumerator, enumerator_t*,
-       private_sw_collector_rest_api_t *this, sw_collector_db_query_t type)
-{
-       json_array_enumerator_t *enumerator;
-       json_object *jrequest, *jresponse;
-       char cmd[BUF_LEN];
-       status_t status;
-
-       jrequest = create_rest_request(this, type);
-       if (!jrequest)
-       {
-               return NULL;
-       }
-       snprintf(cmd, BUF_LEN, "sessions/0/swid-measurement/");
-
-       status = this->rest_api->post(this->rest_api, cmd, jrequest, &jresponse);
-       json_object_put(jrequest);
-
-       switch (status)
-       {
-               case SUCCESS:
-               case NOT_FOUND:
-                       jresponse = json_object_new_array();
-                       break;
-               case NEED_MORE:
-                       if (json_object_get_type(jresponse) != json_type_array)
-                       {
-                               DBG1(DBG_IMC, "REST response was not a json_array");
-                               json_object_put(jresponse);
-                               return NULL;
-                       }
-                       break;
-               case FAILED:
-               default:
-                       return NULL;
-       }
-
-       INIT(enumerator,
-               .public = {
-                       .enumerate = enumerator_enumerate_default,
-                       .venumerate = _enumerate,
-                       .destroy = _enumerator_destroy,
-               },
-               .jarray = jresponse,
-       );
-
-       return &enumerator->public;
-}
-
-METHOD(sw_collector_rest_api_t, destroy, void,
-       private_sw_collector_rest_api_t *this)
-{
-       this->rest_api->destroy(this->rest_api);
-       free(this);
-}
-
-/**
- * Described in header.
- */
-sw_collector_rest_api_t *sw_collector_rest_api_create(sw_collector_db_t *db)
-{
-       private_sw_collector_rest_api_t *this;
-       int timeout;
-       char *uri;
-
-       uri = lib->settings->get_str(lib->settings, "%s.rest_api.uri", NULL,
-                                                                lib->ns);
-       timeout = lib->settings->get_int(lib->settings, "%s.rest_api.timeout", 120,
-                                                                lib->ns);
-       if (!uri)
-       {
-               DBG1(DBG_IMC, "REST URI to central collector database not set");
-               return NULL;
-       }
-
-       INIT(this,
-               .public = {
-                       .create_sw_enumerator = _create_sw_enumerator,
-                       .destroy = _destroy,
-               },
-               .db = db,
-               .rest_api = rest_create(uri, timeout),
-       );
-
-       return &this->public;
-}
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.h b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.h
deleted file mode 100644 (file)
index ca45230..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2017 Andreas Steffen
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup sw_collector_rest_api_t sw_collector_rest_api
- * @{ @ingroup imc_swima
- */
-
-#ifndef SW_COLLECTOR_REST_API_H_
-#define SW_COLLECTOR_REST_API_H_
-
-#include "sw_collector_db.h"
-
-typedef struct sw_collector_rest_api_t sw_collector_rest_api_t;
-
-/**
- * Software collector REST API object
- */
-struct sw_collector_rest_api_t {
-
-       /**
-        * List of locally stored software identifiers that are not registered
-        * in a central collector database
-        *
-        * @param type                  Query type (ALL, INSTALLED, DELETED)
-        * @return                              Enumerator
-        */
-       enumerator_t* (*create_sw_enumerator)(sw_collector_rest_api_t *this,
-                                                                                 sw_collector_db_query_t type);
-
-       /**
-        * Destroy sw_collector_rest_api_t object
-        */
-       void (*destroy)(sw_collector_rest_api_t *this);
-
-};
-
-/**
- * Create an sw_collector_rest_api_t instance
- *
- * @param db                           Software collector database to be used
- */
-sw_collector_rest_api_t* sw_collector_rest_api_create(sw_collector_db_t *db);
-
-#endif /** SW_COLLECTOR_REST_API_H_ @}*/
diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_tables.sql b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_tables.sql
deleted file mode 100644 (file)
index b7b430b..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SQLit database for an Endpoint Collector */
-
-DROP TABLE IF EXISTS "events";
-CREATE TABLE "events" (
-  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-  "epoch" INTEGER NOT NULL,
-  "timestamp" CHAR(20) NOT NULL
-);
-
-DROP TABLE IF EXISTS "sw_identifiers";
-CREATE TABLE "sw_identifiers" (
-  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-  "name" VARCHAR(255) NOT NULL,
-  "package" VARCHAR(255) NOT NULL,
-  "version" VARCHAR(255) NOT NULL,
-  "source" INTEGER DEFAULT 0,
-  "installed" INTEGER DEFAULT 1,
-  "tag" TEXT
-  );
-DROP INDEX IF EXISTS "sw_identifiers_name";
-CREATE INDEX "sw_identifiers_name" ON "sw_identifiers" (
-  "name"
-);
-
-DROP TABLE IF EXISTS "sw_events";
-CREATE TABLE "sw_events" (
-  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-  "eid" INTEGER REFERENCES "events" ("id"),
-  "sw_id" INTEGER NOT NULL REFERENCES "sw_identifiers" ("id"),
-  "action" INTEGER NOT NULL
-);
diff --git a/src/sw-collector/.gitignore b/src/sw-collector/.gitignore
new file mode 100644 (file)
index 0000000..152dd95
--- /dev/null
@@ -0,0 +1,2 @@
+sw-collector
+sw-collector.8
diff --git a/src/sw-collector/Makefile.am b/src/sw-collector/Makefile.am
new file mode 100644 (file)
index 0000000..c36aa37
--- /dev/null
@@ -0,0 +1,33 @@
+sbin_PROGRAMS = sw-collector
+
+sw_collector_SOURCES = \
+       sw-collector.c \
+       sw_collector_db.h sw_collector_db.c \
+       sw_collector_dpkg.h sw_collector_dpkg.c \
+       sw_collector_history.h sw_collector_history.c \
+       sw_collector_info.h sw_collector_info.c \
+       sw_collector_rest_api.h sw_collector_rest_api.c
+
+sw-collector.o : $(top_builddir)/config.status
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libtncif \
+       -I$(top_srcdir)/src/libimcv \
+       -DPLUGINS=\""random openssl sqlite curl"\"
+
+AM_CFLAGS = $(json_CFLAGS)
+
+sw_collector_LDADD = \
+       $(top_builddir)/src/libstrongswan/libstrongswan.la \
+       $(top_builddir)/src/libimcv/libimcv.la \
+       $(json_LIBS)
+
+templatesdir = $(pkgdatadir)/templates/database/sw-collector
+dist_templates_DATA = sw_collector_tables.sql
+
+man8_MANS = sw-collector.8
+
+CLEANFILES = $(man8_MANS)
+
+
diff --git a/src/sw-collector/sw-collector.8.in b/src/sw-collector/sw-collector.8.in
new file mode 100644 (file)
index 0000000..a31c401
--- /dev/null
@@ -0,0 +1,90 @@
+.TH SW-COLLECTOR 1 "2017-07-15" "@PACKAGE_VERSION@" "strongSwan"
+.
+.SH "NAME"
+.
+sw-collector \- Extracts software installation events from dpkg history log
+.
+.SH "SYNOPSIS"
+.
+.SY "sw-collector"
+.OP \-\-debug level
+.OP \-\-quiet
+.OP \-\-count event-count
+.YS
+.
+.SY "sw-collector"
+.OP \-\-debug level
+.OP \-\-quiet
+.BR \-\-list |\fB\-\-unregistered\fR|\fB\-\-generate\fR|\fB\-\-migrate
+.YS
+.
+.SY "sw-collector"
+.B \-h
+|
+.B \-\-help
+.YS
+.
+.SH "DESCRIPTION"
+.
+.B sw-collector
+extracts information about software package installation, update or removal
+events from the apt history log and stores the software events in an SQLite
+database. The retrieved history information is then merged and made consistent
+with the actual list of installed software packages obtained with dpkg-query.
+.
+.SH "OPTIONS"
+.
+.TP
+.B "\-h, \-\-help"
+Prints usage information and a short summary of the available commands.
+.TP
+.BI "\-v, \-\-debug " level
+Set debug level, default: 2.
+.TP
+.B "\-q, \-\-quiet
+Disable debug output to stderr.
+.TP
+.B "\-l, \-\-list
+Lists all software packages stored in the collector database showing their
+installation status.
+.TP
+.B "\-u, \-\-unregistered
+Lists all software packages residing in the local collector database but for
+which no SWID tags exist yet in a central collector database reachable via a
+REST interface. 
+.TP
+.B "\-g, \-\-generate
+Generates minimal SWID tags for all deleted software packages residing in the
+local collector database but for which no SWID tags exist in a central collector
+database reachable via a REST interface.
+.TP
+.B "\-m, \-\-migrate
+Can be used to migrate collector database versions. Currently all architecture
+suffixes are removed from dpkg package names.
+.
+.SH "CONFIGURATION"
+.
+The following parameters can be configured in strongswan.conf:
+.P
+ sw-collector {
+    database = sqlite:///etc/pts/collector.db
+    history = /var/log/apt/history.log
+    first_time = 2016-04-22T20:55:14Z
+    rest_api {
+       uri = https://admin-user:ietf99hackathon@tnc.strongswan.org/api/
+       timeout = 120
+    }
+    tag_creator {
+       name = strongSwan Project
+       regid = strongswan.org
+    }
+ }
+.
+.SH "FILES"
+.
+@PKGDATADIR@/templates/database/sw-collector/sw_collector_tables.sql
+.
+.SH "SEE ALSO"
+.
+.BR strongswan.conf (5)
+
diff --git a/src/sw-collector/sw-collector.c b/src/sw-collector/sw-collector.c
new file mode 100644 (file)
index 0000000..d76f751
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <unistd.h>
+#ifdef HAVE_SYSLOG
+# include <syslog.h>
+#endif
+
+#include "sw_collector_info.h"
+#include "sw_collector_db.h"
+#include "sw_collector_history.h"
+#include "sw_collector_rest_api.h"
+#include "sw_collector_dpkg.h"
+#
+#include <library.h>
+#include <utils/debug.h>
+#include <utils/lexparser.h>
+
+#include <imv/imv_os_info.h>
+
+/**
+ * global debug output variables
+ */
+static int debug_level = 2;
+static bool stderr_quiet = FALSE;
+static int count = 0;
+
+typedef enum collector_op_t collector_op_t;
+
+enum collector_op_t {
+       COLLECTOR_OP_EXTRACT,
+       COLLECTOR_OP_LIST,
+       COLLECTOR_OP_UNREGISTERED,
+       COLLECTOR_OP_GENERATE,
+       COLLECTOR_OP_MIGRATE
+};
+
+/**
+ * sw_collector dbg function
+ */
+static void sw_collector_dbg(debug_t group, level_t level, char *fmt, ...)
+{
+       va_list args;
+
+       if (level <= debug_level)
+       {
+               if (!stderr_quiet)
+               {
+                       va_start(args, fmt);
+                       vfprintf(stderr, fmt, args);
+                       fprintf(stderr, "\n");
+                       va_end(args);
+               }
+
+#ifdef HAVE_SYSLOG
+               {
+                       int priority = LOG_INFO;
+                       char buffer[8192];
+                       char *current = buffer, *next;
+
+                       /* write in memory buffer first */
+                       va_start(args, fmt);
+                       vsnprintf(buffer, sizeof(buffer), fmt, args);
+                       va_end(args);
+
+                       /* do a syslog with every line */
+                       while (current)
+                       {
+                               next = strchr(current, '\n');
+                               if (next)
+                               {
+                                       *(next++) = '\0';
+                               }
+                               syslog(priority, "%s\n", current);
+                               current = next;
+                       }
+               }
+#endif /* HAVE_SYSLOG */
+       }
+}
+
+/**
+ * atexit handler
+ */
+static void cleanup(void)
+{
+       library_deinit();
+#ifdef HAVE_SYSLOG
+       closelog();
+#endif
+}
+
+/**
+ * Display usage of sw-collector command
+ */
+static void usage(void)
+{
+       printf("\
+Usage:\n\
+  sw-collector --help\n\
+  sw-collector [--debug <level>] [--quiet] [--count <event count>]\n\
+  sw-collector [--debug <level>] [--quiet] --list|-unregistered\n\
+  sw-collector [--debug <level>] [--quiet] ---generate|--migrate\n");
+}
+
+/**
+ * Parse command line options
+ */
+static collector_op_t do_args(int argc, char *argv[])
+{
+       collector_op_t op = COLLECTOR_OP_EXTRACT;
+
+       /* reinit getopt state */
+       optind = 0;
+
+       while (TRUE)
+       {
+               int c;
+
+               struct option long_opts[] = {
+                       { "help", no_argument, NULL, 'h' },
+                       { "count", required_argument, NULL, 'c' },
+                       { "debug", required_argument, NULL, 'd' },
+                       { "generate", no_argument, NULL, 'g' },
+                       { "list", no_argument, NULL, 'l' },
+                       { "migrate", no_argument, NULL, 'm' },
+                       { "quiet", no_argument, NULL, 'q' },
+                       { "unregistered", no_argument, NULL, 'u' },
+                       { 0,0,0,0 }
+               };
+
+               c = getopt_long(argc, argv, "hc:d:lmqu", long_opts, NULL);
+               switch (c)
+               {
+                       case EOF:
+                               break;
+                       case 'h':
+                               usage();
+                               exit(SUCCESS);
+                               break;
+                       case 'c':
+                               count = atoi(optarg);
+                               continue;
+                       case 'd':
+                               debug_level = atoi(optarg);
+                               continue;
+                       case 'g':
+                               op = COLLECTOR_OP_GENERATE;
+                               continue;
+                       case 'l':
+                               op = COLLECTOR_OP_LIST;
+                               continue;
+                       case 'm':
+                               op = COLLECTOR_OP_MIGRATE;
+                               continue;
+                       case 'q':
+                               stderr_quiet = TRUE;
+                               continue;
+                       case 'u':
+                               op = COLLECTOR_OP_UNREGISTERED;
+                               continue;
+                       default:
+                               usage();
+                               exit(EXIT_FAILURE);
+               }
+               break;
+       }
+       return op;
+}
+
+/**
+ * Extract software events from apt history log files
+ */
+static int extract_history(sw_collector_info_t *info, sw_collector_db_t *db)
+{
+       sw_collector_history_t *history = NULL;
+       uint32_t epoch, last_eid, eid = 0;
+       char *history_path, *os, *last_time = NULL, rfc_time[21];
+       chunk_t *h, history_chunk, line, cmd;
+       os_type_t os_type;
+       int status = EXIT_FAILURE;
+       bool skip = TRUE;
+
+       /* check if OS supports apg/dpkg history logs */
+       info->get_os(info, &os);
+       os_type = info->get_os_type(info);
+
+       if (os_type !=  OS_TYPE_DEBIAN && os_type != OS_TYPE_UBUNTU)
+       {
+               DBG1(DBG_IMC, "%.*s not supported", os);
+               return EXIT_FAILURE;
+       }
+
+       /* open history file for reading */
+       history_path= lib->settings->get_str(lib->settings, "%s.history", NULL,
+                                                                                lib->ns);
+       if (!history_path)
+       {
+               fprintf(stderr, "sw-collector.history path not set.\n");
+               return EXIT_FAILURE;
+       }
+       h = chunk_map(history_path, FALSE);
+       if (!h)
+       {
+               fprintf(stderr, "opening '%s' failed: %s", history, strerror(errno));
+               return EXIT_FAILURE;
+       }
+       history_chunk = *h;
+
+       /* Instantiate history extractor */
+       history = sw_collector_history_create(info, db, 1);
+
+       /* retrieve last event in database */
+       if (!db->get_last_event(db, &last_eid, &epoch, &last_time) || !last_eid)
+       {
+               goto end;
+       }
+       DBG0(DBG_IMC, "Last-Event: %s, eid = %u, epoch = %u",
+                                  last_time, last_eid, epoch);
+
+       /* parse history file */
+       while (fetchline(&history_chunk, &line))
+       {
+               if (line.len == 0)
+               {
+                       continue;
+               }
+               if (!extract_token(&cmd, ':', &line))
+               {
+                       fprintf(stderr, "terminator symbol ':' not found.\n");
+                       goto end;
+               }
+               if (match("Start-Date", &cmd))
+               {
+                       if (!history->extract_timestamp(history, line, rfc_time))
+                       {
+                               goto end;
+                       }
+
+                       /* have we reached new history entries? */
+                       if (skip && strcmp(rfc_time, last_time) > 0)
+                       {
+                               skip = FALSE;
+                       }
+                       if (skip)
+                       {
+                               continue;
+                       }
+
+                       /* insert new event into database */
+                       eid = db->add_event(db, rfc_time);
+                       if (!eid)
+                       {
+                               goto end;
+                       }
+                       DBG1(DBG_IMC, "Start-Date: %s, eid = %u, epoch = %u",
+                                                  rfc_time, eid, epoch);
+               }
+               else if (skip)
+               {
+                       /* skip old history entries which have already been processed */
+                       continue;
+               }
+               else if (match("Install", &cmd))
+               {
+                       DBG1(DBG_IMC, "  Install:");
+                       if (!history->extract_packages(history, line, eid, SW_OP_INSTALL))
+                       {
+                               goto end;
+                       }
+               }
+               else if (match("Upgrade", &cmd))
+               {
+                       DBG1(DBG_IMC, "  Upgrade:");
+                       if (!history->extract_packages(history, line, eid, SW_OP_UPGRADE))
+                       {
+                               goto end;
+                       }
+               }
+               else if (match("Remove", &cmd))
+               {
+                       DBG1(DBG_IMC, "  Remove:");
+                       if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
+                       {
+                               goto end;
+                       }
+               }
+               else if (match("Purge", &cmd))
+               {
+                       DBG1(DBG_IMC, "  Purge:");
+                       if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
+                       {
+                               goto end;
+                       }
+               }
+               else if (match("End-Date", &cmd))
+               {
+                       /* Process 'count' events at a time */
+                       if (count > 0 && eid - last_eid == count)
+                       {
+                               fprintf(stderr, "added %d events\n", count);
+                               goto end;
+                       }
+               }
+       }
+
+       if (history->merge_installed_packages(history))
+       {
+               status = EXIT_SUCCESS;
+       }
+
+end:
+       free(last_time);
+       history->destroy(history);
+       chunk_unmap(h);
+
+       return status;
+}
+
+/**
+ * List all endpoint software identifiers stored in local collector database
+ */
+static int list_identifiers(sw_collector_db_t *db)
+{
+       enumerator_t *e;
+       char *name, *package, *version;
+       uint32_t sw_id, count = 0, installed_count = 0, installed;
+
+       e = db->create_sw_enumerator(db, SW_QUERY_ALL, NULL);
+       if (!e)
+       {
+               return EXIT_FAILURE;
+       }
+       while (e->enumerate(e, &sw_id, &name, &package, &version, &installed))
+       {
+               printf("%s,%s,%s,%d\n", name, package, version, installed);
+               if (installed)
+               {
+                       installed_count++;
+               }
+               count++;
+       }
+       e->destroy(e);
+       DBG1(DBG_IMC, "retrieved %u software identities with %u installed and %u "
+                                 "deleted", count, installed_count, count - installed_count);
+
+       return EXIT_SUCCESS;
+}
+
+static bool query_registry(sw_collector_rest_api_t *rest_api, bool installed)
+{
+       sw_collector_db_query_t type;
+       enumerator_t *enumerator;
+       char *sw_id;
+       int count = 0;
+
+       type = installed ? SW_QUERY_INSTALLED : SW_QUERY_DELETED;
+       enumerator = rest_api->create_sw_enumerator(rest_api, type);
+       if (!enumerator)
+       {
+               return FALSE;
+       }
+       while (enumerator->enumerate(enumerator, &sw_id))
+       {
+               printf("%s,%s\n", sw_id, installed ? "1" : "0");
+               count++;
+       }
+       enumerator->destroy(enumerator);
+       DBG1(DBG_IMC, "%d %s software identifiers not registered", count,
+                                  installed ? "installed" : "deleted");
+       return TRUE;
+}
+
+
+/**
+ * List all endpoint software identifiers stored in local collector database
+ * that are not registered yet in central collelector database
+ */
+static int unregistered_identifiers(sw_collector_db_t *db)
+{
+       sw_collector_rest_api_t *rest_api;
+       int status = EXIT_SUCCESS;
+
+       rest_api = sw_collector_rest_api_create(db);
+       if (!rest_api)
+       {
+               return EXIT_FAILURE;
+       }
+
+       /* List installed software identifiers not registered centrally */
+       if (!query_registry(rest_api, TRUE))
+       {
+               status = EXIT_FAILURE;
+       }
+
+       /* List deleted software identifiers not registered centrally */
+       if (!query_registry(rest_api, FALSE))
+       {
+               status = EXIT_FAILURE;
+       }
+       rest_api->destroy(rest_api);
+
+       return status;
+}
+
+/**
+ * Generate a minimalistic ISO 19770-2:2015 SWID tag
+ */
+static char* generate_tag(char *name, char *package, char *version,
+                                                 char* entity, char *regid, char *product)
+{
+       char *tag_id, *tag;
+       int res;
+
+       tag_id = strstr(name, "__");
+       if (!tag_id)
+       {
+               return NULL;
+       }
+       tag_id += 2;
+
+       res = asprintf(&tag, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+                       "<SoftwareIdentity name=\"%s\" tagId=\"%s\" version=\"%s\" "
+                       "versionScheme=\"alphanumeric\" "
+                       "xmlns=\"http://standards.iso.org/iso/19770/-2/2015/schema.xsd\">"
+                       "<Entity name=\"%s\" regid=\"%s\" role=\"tagCreator\"/>"
+                       "<Meta product=\"%s\"/>"
+                       "</SoftwareIdentity>",
+                        package, tag_id, version, entity, regid, product);
+
+       return (res == -1) ? NULL : tag;
+}
+
+/**
+ * Generate a minimalistic ISO 19770-2:2015 SWID tag for
+ * all deleted SW identifiers that are not registered centrally
+ */
+static int generate_tags(sw_collector_info_t *info, sw_collector_db_t *db)
+{
+       sw_collector_rest_api_t *rest_api;
+       char *pos, *name, *package, *version, *entity, *regid, *product, *tag;
+       enumerator_t *enumerator;
+       uint32_t sw_id;
+       int status = EXIT_FAILURE;
+
+       entity = lib->settings->get_str(lib->settings, "%s.tag_creator.name",
+                                                                       "strongSwan Project", lib->ns);
+       regid  = lib->settings->get_str(lib->settings, "%s.tag_creator.regid",
+                                                                       "strongswan.org", lib->ns);
+       info->get_os(info, &product);
+
+       rest_api = sw_collector_rest_api_create(db);
+       if (!rest_api)
+       {
+               goto end;
+       }
+
+       enumerator = rest_api->create_sw_enumerator(rest_api, SW_QUERY_DELETED);
+       if (!enumerator)
+       {
+               goto end;
+       }
+       while (enumerator->enumerate(enumerator, &name))
+       {
+               sw_id = db->get_sw_id(db, name, &package, &version, NULL, NULL);
+               if (sw_id)
+               {
+                       /* Remove architecture from package name */
+                       pos = strchr(package, ':');
+                       if (pos)
+                       {
+                               *pos = '\0';
+                       }
+                       tag = generate_tag(name, package, version, entity, regid, product);
+                       if (tag)
+                       {
+                               printf("%s\n", tag);
+                               free(tag);
+                               count++;
+                       }
+                       free(package);
+                       free(version);
+               }
+       }
+       enumerator->destroy(enumerator);
+       status = EXIT_SUCCESS;
+       DBG1(DBG_IMC, "%d tags for deleted unregistered software identifiers",
+                                  count);
+
+end:
+       DESTROY_IF(rest_api);
+
+       return status;
+}
+
+/**
+ * Append missing architecture suffix to package entries in the database
+ */
+static int migrate(sw_collector_info_t *info, sw_collector_db_t *db)
+{
+       sw_collector_dpkg_t *dpkg;
+
+       char *package, *arch, *version;
+       char package_filter[BUF_LEN];
+       int res, count = 0;
+       int status = EXIT_SUCCESS;
+       enumerator_t *enumerator;
+
+       dpkg = sw_collector_dpkg_create();
+       if (!dpkg)
+       {
+               return FAILED;
+       }
+
+       enumerator = dpkg->create_sw_enumerator(dpkg);
+       while (enumerator->enumerate(enumerator, &package, &arch, &version))
+       {
+
+               /* Look for package names with architecture suffix */
+               snprintf(package_filter, BUF_LEN, "%s:%%", package);
+
+               res = db->update_package(db, package_filter, package);
+               if (res < 0)
+               {
+                               status = EXIT_FAILURE;
+                               break;
+               }
+               else if (res > 0)
+               {
+                       count += res;
+                       DBG2(DBG_IMC, "%s: removed arch suffix %d times", package, res);
+               }
+       }
+       enumerator->destroy(enumerator);
+       dpkg->destroy(dpkg);
+
+       DBG1(DBG_IMC, "migrated %d sw identifier records", count);
+       return status;
+}
+
+
+int main(int argc, char *argv[])
+{
+       sw_collector_db_t *db = NULL;
+       sw_collector_info_t *info;
+       collector_op_t op;
+       char *uri, *tag_creator;
+       int status = EXIT_FAILURE;
+
+       op = do_args(argc, argv);
+
+       /* enable sw_collector debugging hook */
+       dbg = sw_collector_dbg;
+#ifdef HAVE_SYSLOG
+       openlog("sw-collector", 0, LOG_DEBUG);
+#endif
+
+       atexit(cleanup);
+
+       /* initialize library */
+       if (!library_init(NULL, "sw-collector"))
+       {
+               exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
+       }
+
+       /* load sw-collector plugins */
+       if (!lib->plugins->load(lib->plugins,
+                       lib->settings->get_str(lib->settings, "%s.load", PLUGINS, lib->ns)))
+       {
+               exit(SS_RC_INITIALIZATION_FAILED);
+       }
+
+       /* connect to sw-collector database */
+       uri = lib->settings->get_str(lib->settings, "%s.database", NULL, lib->ns);
+       if (!uri)
+       {
+               fprintf(stderr, "sw-collector.database URI not set.\n");
+               exit(EXIT_FAILURE);
+       }
+       db = sw_collector_db_create(uri);
+       if (!db)
+       {
+               fprintf(stderr, "connection to sw-collector database failed.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Attach OS info */
+       tag_creator = lib->settings->get_str(lib->settings, "%s.tag_creator.regid",
+                                                                                "strongswan.org", lib->ns);
+       info = sw_collector_info_create(tag_creator);
+
+       switch (op)
+       {
+               case COLLECTOR_OP_EXTRACT:
+                       status = extract_history(info, db);
+                       break;
+               case COLLECTOR_OP_LIST:
+                       status = list_identifiers(db);
+                       break;
+               case COLLECTOR_OP_UNREGISTERED:
+                       status = unregistered_identifiers(db);
+                       break;
+               case COLLECTOR_OP_GENERATE:
+                       status = generate_tags(info, db);
+                       break;
+               case COLLECTOR_OP_MIGRATE:
+                       status = migrate(info, db);
+                       break;
+       }
+       db->destroy(db);
+       info->destroy(info);
+
+       exit(status);
+}
diff --git a/src/sw-collector/sw_collector_db.c b/src/sw-collector/sw_collector_db.c
new file mode 100644 (file)
index 0000000..6f14818
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "sw_collector_db.h"
+
+#include "swima/swima_event.h"
+
+typedef struct private_sw_collector_db_t private_sw_collector_db_t;
+
+/**
+ * Private data of an sw_collector_db_t object.
+ */
+struct private_sw_collector_db_t {
+
+       /**
+        * Public members of sw_collector_db_state_t
+        */
+       sw_collector_db_t public;
+
+       /**
+        * Epoch
+        */
+       uint32_t epoch;
+
+       /**
+        * Event ID of last event stored in database
+        */
+       uint32_t last_eid;
+
+       /**
+        * Software collector database
+        */
+       database_t *db;
+
+};
+
+METHOD(sw_collector_db_t, add_event, uint32_t,
+       private_sw_collector_db_t *this, char *timestamp)
+{
+       uint32_t eid = 0;
+
+       if (this->db->execute(this->db, &eid,
+                       "INSERT INTO events (epoch, timestamp) VALUES (?, ?)",
+                        DB_UINT, this->epoch, DB_TEXT, timestamp) != 1)
+       {
+               DBG1(DBG_IMC, "unable to insert event into database");
+               return 0;
+       }
+
+       return eid;
+}
+
+METHOD(sw_collector_db_t, get_last_event, bool,
+       private_sw_collector_db_t *this, uint32_t *eid, uint32_t *epoch,
+       char **last_time)
+{
+       char *timestamp;
+       enumerator_t *e;
+
+       e = this->db->query(this->db,
+                       "SELECT id, epoch, timestamp FROM events ORDER BY timestamp DESC",
+                       DB_UINT, DB_UINT, DB_TEXT);
+       if (!e)
+       {
+               DBG1(DBG_IMC, "database query for event failed");
+               return FALSE;
+       }
+       if (e->enumerate(e, eid, epoch, &timestamp))
+       {
+               if (last_time)
+               {
+                       *last_time = strdup(timestamp);
+               }
+       }
+       else
+       {
+               *eid = 0;
+       }
+       e->destroy(e);
+
+       return TRUE;
+}
+
+METHOD(sw_collector_db_t, add_sw_event, bool,
+       private_sw_collector_db_t *this, uint32_t eid, uint32_t sw_id,
+       uint8_t action)
+{
+       if (this->db->execute(this->db, NULL,
+                       "INSERT INTO sw_events (eid, sw_id, action) VALUES (?, ?, ?)",
+                        DB_UINT, eid, DB_UINT, sw_id, DB_UINT, action) != 1)
+       {
+               DBG1(DBG_IMC, "unable to insert sw_event into database");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+METHOD(sw_collector_db_t, set_sw_id, uint32_t,
+       private_sw_collector_db_t *this, char *name,  char *package, char *version,
+       uint8_t source, bool installed)
+{
+       uint32_t sw_id;
+
+       if (this->db->execute(this->db, &sw_id,
+                       "INSERT INTO sw_identifiers "
+                       "(name, package, version, source, installed) VALUES (?, ?, ?, ?, ?)",
+                        DB_TEXT, name, DB_TEXT, package, DB_TEXT, version, DB_UINT, source,
+                        DB_UINT, installed) != 1)
+       {
+               DBG1(DBG_IMC, "unable to insert sw_id into database");
+               return 0;
+       }
+
+       return sw_id;
+}
+
+METHOD(sw_collector_db_t, get_sw_id, uint32_t,
+       private_sw_collector_db_t *this, char *name, char **package, char **version,
+       uint8_t *source, bool *installed)
+{
+       char *sw_package, *sw_version;
+       uint32_t sw_id = 0, sw_source, sw_installed;
+       enumerator_t *e;
+
+       /* Does software identifier already exist in database? */
+       e = this->db->query(this->db,
+                       "SELECT id, package, version, source, installed "
+                       "FROM sw_identifiers WHERE name = ?",
+                       DB_TEXT, name, DB_UINT, DB_TEXT, DB_TEXT, DB_UINT, DB_UINT);
+       if (!e)
+       {
+               DBG1(DBG_IMC, "database query for sw_identifier failed");
+               return 0;
+       }
+       if (e->enumerate(e, &sw_id, &sw_package, &sw_version, &sw_source,
+                                               &sw_installed))
+       {
+               if (package)
+               {
+                       *package = strdup(sw_package);
+               }
+               if (version)
+               {
+                       *version = strdup(sw_version);
+               }
+               if (source)
+               {
+                       *source = sw_source;
+               }
+               if (installed)
+               {
+                       *installed = sw_installed;
+               }
+       }
+       e->destroy(e);
+
+       return sw_id;
+}
+
+METHOD(sw_collector_db_t, get_sw_id_count, uint32_t,
+       private_sw_collector_db_t *this, sw_collector_db_query_t type)
+{
+       uint32_t count, installed;
+       enumerator_t *e;
+
+       if (type == SW_QUERY_ALL)
+       {
+               e = this->db->query(this->db,
+                       "SELECT COUNT(installed) FROM sw_identifiers", DB_UINT);
+       }
+       else
+       {
+               installed = (type == SW_QUERY_INSTALLED);
+               e = this->db->query(this->db,
+                       "SELECT COUNT(installed) FROM sw_identifiers WHERE installed = ?",
+                        DB_UINT, installed, DB_UINT);
+       }
+
+       if (!e)
+       {
+               DBG1(DBG_IMC, "database query for sw_identifier count failed");
+               return 0;
+       }
+       if (!e->enumerate(e, &count))
+       {
+               count = 0;
+       }
+       e->destroy(e);
+
+       return count;
+}
+
+METHOD(sw_collector_db_t, update_sw_id, bool,
+       private_sw_collector_db_t *this, uint32_t sw_id, char *name, char *version,
+       bool installed)
+{
+       int res;
+
+       if (name && version)
+       {
+               res = this->db->execute(this->db, NULL,
+                       "UPDATE sw_identifiers SET name = ?, version = ?, installed = ? "
+                       "WHERE id = ?", DB_TEXT, name, DB_TEXT, version, DB_UINT, installed,
+                        DB_UINT, sw_id);
+       }
+       else
+       {
+               res = this->db->execute(this->db, NULL,
+                       "UPDATE sw_identifiers SET installed = ? WHERE id = ?",
+                        DB_UINT, installed, DB_UINT, sw_id);
+       }
+       if (res != 1)
+       {
+               DBG1(DBG_IMC, "unable to update software identifier in database");
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(sw_collector_db_t, update_package, int,
+       private_sw_collector_db_t *this, char *package_filter, char *package)
+{
+       int count;
+
+       count = this->db->execute(this->db, NULL,
+                       "UPDATE sw_identifiers SET package = ? WHERE package LIKE ?",
+                        DB_TEXT, package, DB_TEXT, package_filter);
+       if (count < 0)
+       {
+               DBG1(DBG_IMC, "unable to update package name in database");
+       }
+
+       return count;
+}
+
+METHOD(sw_collector_db_t, create_sw_enumerator, enumerator_t*,
+       private_sw_collector_db_t *this, sw_collector_db_query_t type, char *package)
+{
+       enumerator_t *e;
+       u_int installed;
+
+       if (type == SW_QUERY_ALL)
+       {
+               if (package)
+               {
+                       e = this->db->query(this->db,
+                               "SELECT id, name, package, version, installed "
+                               "FROM sw_identifiers WHERE package = ? ORDER BY name ASC",
+                                DB_TEXT, package, DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
+               }
+               else
+               {
+                       e = this->db->query(this->db,
+                               "SELECT id, name, package, version, installed "
+                               "FROM sw_identifiers ORDER BY name ASC",
+                                DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
+               }
+       }
+       else
+       {
+               installed = (type == SW_QUERY_INSTALLED);
+
+               if (package)
+               {
+                       e = this->db->query(this->db,
+                               "SELECT id, name, package, version, installed "
+                               "FROM sw_identifiers WHERE package = ? AND installed = ? "
+                               "ORDER BY name ASC", DB_TEXT, package, DB_UINT, installed,
+                                DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
+               }
+               else
+               {
+                       e = this->db->query(this->db,
+                               "SELECT id, name, package, version, installed "
+                               "FROM sw_identifiers WHERE installed = ? ORDER BY name ASC",
+                                DB_UINT, installed, DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
+               }
+       }
+       if (!e)
+       {
+               DBG1(DBG_IMC, "database query for sw_identifier count failed");
+               return NULL;
+       }
+
+       return e;
+}
+
+METHOD(sw_collector_db_t, destroy, void,
+       private_sw_collector_db_t *this)
+{
+       this->db->destroy(this->db);
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+sw_collector_db_t *sw_collector_db_create(char *uri)
+{
+       private_sw_collector_db_t *this;
+       uint32_t first_eid, last_eid;
+       char *first_time;
+
+       INIT(this,
+               .public = {
+                       .add_event = _add_event,
+                       .get_last_event = _get_last_event,
+                       .add_sw_event = _add_sw_event,
+                       .set_sw_id = _set_sw_id,
+                       .get_sw_id = _get_sw_id,
+                       .get_sw_id_count = _get_sw_id_count,
+                       .update_sw_id = _update_sw_id,
+                       .update_package = _update_package,
+                       .create_sw_enumerator = _create_sw_enumerator,
+                       .destroy = _destroy,
+               },
+               .db = lib->db->create(lib->db, uri),
+       );
+
+       if (!this->db)
+       {
+               DBG1(DBG_IMC, "opening database URI '%s' failed", uri);
+               return NULL;
+       }
+
+       /* Retrieve last event in database */
+       if (!get_last_event(this, &last_eid, &this->epoch, NULL))
+       {
+               destroy(this);
+               return NULL;
+       }
+
+       /* Create random epoch and first event if no events exist yet */
+       if (!last_eid)
+       {
+               rng_t *rng;
+
+               rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
+               if (!rng ||
+                       !rng->get_bytes(rng, sizeof(uint32_t), (uint8_t*)&this->epoch))
+               {
+                       DESTROY_IF(rng);
+                       destroy(this);
+                       DBG1(DBG_IMC, "generating random epoch value failed");
+                       return NULL;
+               }
+               rng->destroy(rng);
+
+               /* strongTNC workaround - limit epoch to 31 bit unsigned integer */
+               this->epoch &= 0x7fffffff;
+
+               /* Create first event when the OS was installed */
+               first_time = lib->settings->get_str(lib->settings,
+                                               "sw-collector.first_time", "0000-00-00T00:00:00Z");
+               first_eid = add_event(this, first_time);
+               if (!first_eid)
+               {
+                       destroy(this);
+                       return NULL;
+               }
+               DBG0(DBG_IMC, "First-Date: %s, eid = %u, epoch = %u",
+                                          first_time, first_eid, this->epoch);
+       }
+
+       return &this->public;
+}
diff --git a/src/sw-collector/sw_collector_db.h b/src/sw-collector/sw_collector_db.h
new file mode 100644 (file)
index 0000000..7aa41b0
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup sw_collector_db_t sw_collector_db
+ * @{ @ingroup imc_swima
+ */
+
+#ifndef SW_COLLECTOR_DB_H_
+#define SW_COLLECTOR_DB_H_
+
+#include <library.h>
+
+typedef struct sw_collector_db_t sw_collector_db_t;
+typedef enum sw_collector_db_query_t sw_collector_db_query_t;
+
+/**
+ * Type of software identifier queries
+ */
+enum sw_collector_db_query_t {
+       SW_QUERY_ALL,
+       SW_QUERY_INSTALLED,
+       SW_QUERY_DELETED
+};
+
+/**
+ * Software collector database object
+ */
+struct sw_collector_db_t {
+
+       /**
+        * bAdd event to database
+        *
+        * @param timestamp             Timestamp in 20 octet RFC 3339 format
+        * @return                              Primary key pointing to event ID or 0 if failed
+        */
+       uint32_t (*add_event)(sw_collector_db_t *this, char *timestamp);
+
+       /**
+        * Get last event, zero EID if none exists
+        *
+        * @param eid                   Primary key pointing to last event
+        * @param epoch                 Epoch
+        * @param last_time             Timestamp in 20 octet RFC 3339 format of last event
+        * @return                              
+        */
+       bool (*get_last_event)(sw_collector_db_t *this, uint32_t *eid,
+                                                          uint32_t *epoch, char **last_time);
+
+       /**
+        * Add software identifier event to database
+        *
+        * @param eid                   Foreign key pointing to an event ID
+        * @param sw_id                 Foreign key pointing to a software identifier
+        * @param action                1 for CREATION, 2 for deletion
+        * @return                              TRUE if successful
+        */
+       bool (*add_sw_event)(sw_collector_db_t *this, uint32_t eid, uint32_t sw_id,
+                                                uint8_t action);
+
+       /**
+        * Set software_identifier, checking if the identifier already exists
+        *
+        * @param name                  Software identifier
+        * @param package               Software package
+        * @param version               Version of software package
+        * @param source                Source ID of the software collector
+        * @param installed             Installation status to be set, TRUE if installed
+        * @return                              Primary key pointing to SW ID or 0 if failed
+        */
+       uint32_t (*set_sw_id)(sw_collector_db_t *this, char *name, char *package,
+                                                 char *version, uint8_t source, bool installed);
+
+       /**
+        * Get software_identifier record
+        *
+        * @param name                  Software identifier
+        * @param package               Software package
+        * @param version               Version of software package
+        * @param source                Source ID of the software collector
+        * @param installed             Installation status
+        * @return                              Primary key pointing to SW ID or 0 if failed
+        */
+       uint32_t (*get_sw_id)(sw_collector_db_t *this, char *name, char **package,
+                                                 char **version, uint8_t *source, bool *installed);
+
+       /**
+        * Get number of installed or deleted software identifiers
+        *
+        * @param type                  Query type (ALL, INSTALLED, DELETED)
+        * @return                              Count
+        */
+       uint32_t (*get_sw_id_count)(sw_collector_db_t *this,
+                                                               sw_collector_db_query_t type);
+
+       /**
+        * Update the software identifier version
+        *
+        * @param sw_id                 Primary key of software identifier
+        * @param name                  Software identifier
+        * @param version               Package version
+        * @param installed             Installation status
+        * @return                              TRUE if update successful
+        */
+       bool (*update_sw_id)(sw_collector_db_t *this, uint32_t sw_id, char *name,
+                                                char *version, bool installed);
+
+       /**
+        * Update the package name
+        *
+        * @param package_filter        Package name[s] to be changed
+        * @param package                       New package name
+        * @return                                      TRUE if update successful
+        */
+       int (*update_package)(sw_collector_db_t *this, char *package_filter,
+                                                                                                  char *package);
+
+       /**
+        * Enumerate over all collected [installed] software identities
+        *
+        * @param type                  Query type (ALL, INSTALLED, DELETED)
+        * @param package               If not NULL enumerate over all package versions
+        * @return                              Enumerator
+        */
+       enumerator_t* (*create_sw_enumerator)(sw_collector_db_t *this,
+                                                                                 sw_collector_db_query_t type,
+                                                                                 char *package);
+
+       /**
+        * Destroy sw_collector_db_t object
+        */
+       void (*destroy)(sw_collector_db_t *this);
+
+};
+
+/**
+ * Create an sw_collector_db_t instance
+ *
+ * @param uri                          database URI
+ */
+sw_collector_db_t* sw_collector_db_create(char *uri);
+
+#endif /** SW_COLLECTOR_DB_H_ @}*/
diff --git a/src/sw-collector/sw_collector_dpkg.c b/src/sw-collector/sw_collector_dpkg.c
new file mode 100644 (file)
index 0000000..b5a8582
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+
+#include "sw_collector_dpkg.h"
+
+typedef struct private_sw_collector_dpkg_t private_sw_collector_dpkg_t;
+
+/**
+ * Private data of an sw_collector_dpkg_t object.
+ */
+struct private_sw_collector_dpkg_t {
+
+       /**
+        * Public members of sw_collector_dpkg_state_t
+        */
+       sw_collector_dpkg_t public;
+
+};
+
+typedef struct {
+       /** public enumerator interface */
+       enumerator_t public;
+       /** dpkg output stream */
+       FILE *file;
+       /** current dpkg output line */
+       char line[BUF_LEN];
+} dpkg_enumerator_t;
+
+METHOD(enumerator_t, enumerate, bool,
+       dpkg_enumerator_t *this, va_list args)
+{
+       char **package, **arch, **version, *state, *pos;
+
+       VA_ARGS_VGET(args, package, arch, version);
+
+       while (TRUE)
+       {
+               if (!fgets(this->line, BUF_LEN, this->file))
+               {
+                       return FALSE;
+               }
+
+               *package = this->line;
+               pos = strchr(this->line, '\t');
+               if (!pos)
+               {
+                       return FALSE;
+               }
+               *pos = '\0';
+
+               *arch = ++pos;
+               pos = strchr(pos, '\t');
+               if (!pos)
+               {
+                       return FALSE;
+               }
+               *pos = '\0';
+
+               *version = ++pos;
+               pos = strchr(pos, '\t');
+               if (!pos)
+               {
+                       return FALSE;
+               }
+               *pos = '\0';
+
+               state = ++pos;
+               pos = strchr(pos, '\n');
+               if (!pos)
+               {
+                       return FALSE;
+               }
+               *pos = '\0';
+
+               if (streq(state, "install ok installed"))
+               {
+                       return TRUE;
+               }
+       }
+}
+
+METHOD(enumerator_t, enumerator_destroy, void,
+       dpkg_enumerator_t *this)
+{
+       pclose(this->file);
+       free(this);     
+}
+
+METHOD(sw_collector_dpkg_t, create_sw_enumerator, enumerator_t*,
+       private_sw_collector_dpkg_t *this)
+{
+       dpkg_enumerator_t *enumerator;
+       char cmd[] = "dpkg-query -W -f="
+                                "\'${Package}\t${Architecture}\t${Version}\t${Status}\n\'";
+       FILE *file;
+
+       file = popen(cmd, "r");
+       if (!file)
+       {
+               DBG1(DBG_IMC, "failed to run dpgk-query command");
+               return NULL;
+       }
+
+       INIT(enumerator,
+               .public = {
+                       .enumerate = enumerator_enumerate_default,
+                       .venumerate = _enumerate,
+                       .destroy = _enumerator_destroy,
+               },
+               .file = file,
+       );
+
+       return &enumerator->public;
+}
+
+METHOD(sw_collector_dpkg_t, destroy, void,
+       private_sw_collector_dpkg_t *this)
+{
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+sw_collector_dpkg_t *sw_collector_dpkg_create(void)
+{
+       private_sw_collector_dpkg_t *this;
+
+       INIT(this,
+               .public = {
+                       .create_sw_enumerator = _create_sw_enumerator,
+                       .destroy = _destroy,
+               },
+       );
+
+       return &this->public;
+}
diff --git a/src/sw-collector/sw_collector_dpkg.h b/src/sw-collector/sw_collector_dpkg.h
new file mode 100644 (file)
index 0000000..eab792e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup sw_collector_dpkg_t sw_collector_dpkg
+ * @{ @ingroup sw_collector
+ */
+
+#ifndef SW_COLLECTOR_DPKG_H_
+#define SW_COLLECTOR_DPKG_H_
+
+#include <library.h>
+
+typedef struct sw_collector_dpkg_t sw_collector_dpkg_t;
+
+/**
+ * Software collector dpkg object
+ */
+struct sw_collector_dpkg_t {
+
+       /**
+        * List of installed software identifiers managed by the
+        * Debian "dpkg" package manager
+        *
+        * @return                              Enumerator
+        */
+       enumerator_t* (*create_sw_enumerator)(sw_collector_dpkg_t *this);
+
+       /**
+        * Destroy sw_collector_dpkg_t object
+        */
+       void (*destroy)(sw_collector_dpkg_t *this);
+
+};
+
+/**
+ * Create an sw_collector_dpkg_t instance
+ */
+sw_collector_dpkg_t* sw_collector_dpkg_create(void);
+
+#endif /** SW_COLLECTOR_DPKG_H_ @}*/
diff --git a/src/sw-collector/sw_collector_history.c b/src/sw-collector/sw_collector_history.c
new file mode 100644 (file)
index 0000000..71f888c
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <time.h>
+
+#include "sw_collector_history.h"
+#include "sw_collector_dpkg.h"
+
+#include "swima/swima_event.h"
+
+typedef struct private_sw_collector_history_t private_sw_collector_history_t;
+
+/**
+ * Private data of an sw_collector_history_t object.
+ */
+struct private_sw_collector_history_t {
+
+       /**
+        * Public members of sw_collector_history_state_t
+        */
+       sw_collector_history_t public;
+
+       /**
+        * Software Event Source Number
+        */
+       uint8_t source;
+
+       /**
+        * Reference to OS info object
+        */
+       sw_collector_info_t *info;
+
+       /**
+        * Reference to collector database
+        */
+       sw_collector_db_t *db;
+
+};
+
+/**
+ * Define auxiliary package_t list item object
+ */
+typedef struct package_t package_t;
+
+struct package_t {
+       char *package;
+       char *version;
+       char *old_version;
+       char *sw_id;
+       char *old_sw_id;
+};
+
+/**
+ * Create package_t list item object
+ */
+static package_t* create_package(sw_collector_info_t *info, chunk_t package,
+                                                                chunk_t version, chunk_t old_version)
+{
+       package_t *this;
+
+       INIT(this,
+               .package = strndup(package.ptr, package.len),
+               .version = strndup(version.ptr, version.len),
+               .old_version = strndup(old_version.ptr, old_version.len),
+       )
+
+       this->sw_id = info->create_sw_id(info, this->package, this->version);
+       if (old_version.len)
+       {
+               this->old_sw_id = info->create_sw_id(info, this->package,
+                                                                                                  this->old_version);
+       }
+
+       return this;
+}
+
+/**
+ * Free package_t list item object
+ */
+static void free_package(package_t *this)
+{
+       if (this)
+       {
+               free(this->package);
+               free(this->version);
+               free(this->old_version);
+               free(this->sw_id);
+               free(this->old_sw_id);
+               free(this);
+       }
+}
+
+/**
+ * Extract and parse a single package item
+ */
+static package_t* extract_package(chunk_t item, sw_collector_info_t *info,
+                                                                                               sw_collector_history_op_t op)
+{
+       chunk_t package, package_stripped, version, old_version;
+       package_t *p;
+
+       /* extract package name */
+       if (!extract_token(&package, ' ', &item))
+       {
+               fprintf(stderr, "version not found.\n");
+               return NULL;
+       }
+       item = chunk_skip(item, 1);
+
+       /* strip architecture suffix if present */
+       if (extract_token(&package_stripped, ':', &package))
+       {
+               package = package_stripped;
+       }
+
+       /* extract versions */
+       version = old_version = chunk_empty;
+
+       if (item.len > 0)
+       {
+               if (extract_token(&version, ',', &item))
+               {
+                       eat_whitespace(&item);
+                       if (!match("automatic", &item))
+                       {
+                               old_version = version;
+                               version = item;
+                       }
+               }
+               else
+               {
+                       version = item;
+               }
+       }
+       p = create_package(info, package, version, old_version);
+
+       /* generate log entry */
+       if (op == SW_OP_UPGRADE)
+       {
+               DBG2(DBG_IMC, "    %s (%s, %s)", p->package, p->old_version, p->version);
+               DBG2(DBG_IMC, "      +%s", p->sw_id);
+               DBG2(DBG_IMC, "      -%s", p->old_sw_id);
+       }
+       else
+       {
+               DBG2(DBG_IMC, "    %s (%s)", p->package, p->version);
+               DBG2(DBG_IMC, "      %s%s", (op == SW_OP_INSTALL) ? "+" : "-", p->sw_id);
+       }
+
+       return p;
+}
+
+METHOD(sw_collector_history_t, extract_timestamp, bool,
+       private_sw_collector_history_t *this, chunk_t args, char *buf)
+{
+       struct tm loc, utc;
+       chunk_t t1, t2;
+       time_t t;
+
+       /* Break down local time with format t1 = yyyy-mm-dd and t2 = hh:mm:ss */
+       if (!eat_whitespace(&args) || !extract_token(&t1, ' ', &args) ||
+               !eat_whitespace(&args) || t1.len != 10 || args.len != 8)
+       {
+               DBG1(DBG_IMC, "unable to parse start-date");
+               return FALSE;
+       }
+       t2 = args;
+
+       if (sscanf(t1.ptr, "%4d-%2d-%2d",
+                                               &loc.tm_year, &loc.tm_mon, &loc.tm_mday) != 3)
+       {
+               DBG1(DBG_IMC, "unable to parse date format yyyy-mm-dd");
+               return FALSE;
+       }
+       loc.tm_year -= 1900;
+       loc.tm_mon  -= 1;
+       loc.tm_isdst = -1;
+
+       if (sscanf(t2.ptr, "%2d:%2d:%2d",
+                                               &loc.tm_hour, &loc.tm_min, &loc.tm_sec) != 3)
+       {
+               DBG1(DBG_IMC, "unable to parse time format hh:mm:ss");
+               return FALSE;
+       }
+
+       /* Convert from local time to UTC */
+       t = mktime(&loc);
+       gmtime_r(&t, &utc);
+       utc.tm_year += 1900;
+       utc.tm_mon  += 1;
+
+       /* Form timestamp according to RFC 3339 (20 characters) */
+       snprintf(buf, 21, "%4d-%02d-%02dT%02d:%02d:%02dZ",
+                        utc.tm_year, utc.tm_mon, utc.tm_mday,
+                        utc.tm_hour, utc.tm_min, utc.tm_sec);
+
+       return TRUE;
+}
+
+METHOD(sw_collector_history_t, extract_packages, bool,
+       private_sw_collector_history_t *this, chunk_t args, uint32_t eid,
+       sw_collector_history_op_t op)
+{
+       bool success = FALSE;
+       package_t *p = NULL;
+       chunk_t item;
+
+       eat_whitespace(&args);
+
+       while (extract_token(&item, ')', &args))
+       {
+               char *del_sw_id = NULL, *del_version = NULL;
+               char *nx, *px, *vx, *v1;
+               bool installed;
+               u_int sw_idx, ix;
+               uint32_t sw_id, sw_id_epoch_less = 0;
+               enumerator_t *e;
+
+               p = extract_package(item, this->info, op);
+               if (!p)
+               {
+                       goto end;
+               }
+
+               /* packages without version information cannot be handled */
+               if (strlen(p->version) == 0)
+               {
+                       free_package(p);
+                       continue;
+               }
+
+               switch (op)
+               {
+                       case SW_OP_REMOVE:
+                               /* prepare subsequent deletion sw event */
+                               del_sw_id = p->sw_id;
+                               del_version = p->version;
+                               break;
+                       case SW_OP_UPGRADE:
+                               /* prepare subsequent deletion sw event */
+                               del_sw_id = p->old_sw_id;
+                               del_version = p->old_version;
+                               /* fall through to next case */
+                       case SW_OP_INSTALL:
+                               sw_id = this->db->get_sw_id(this->db, p->sw_id, NULL, NULL,
+                                                                                       NULL, &installed);
+                               if (sw_id)
+                               {
+                                       /* sw identifier exists - update state to 'installed' */
+                                       if (installed)
+                                       {
+                                               /* this case should not occur */
+                                               DBG1(DBG_IMC, "  warning:  sw_id %d is already "
+                                                                         "installed", sw_id);
+                                       }
+                                       else if (!this->db->update_sw_id(this->db, sw_id, NULL,
+                                                                                                        NULL, TRUE))
+                                       {
+                                               goto end;
+                                       }
+                               }
+                               else
+                               {
+                                       /* new sw identifier - create with state 'installed' */
+                                       sw_id = this->db->set_sw_id(this->db, p->sw_id, p->package,
+                                                                                               p->version,     this->source, TRUE);
+                                       if (!sw_id)
+                                       {
+                                               goto end;
+                                       }
+                               }
+
+                               /* add creation sw event with current eid */
+                               if (!this->db->add_sw_event(this->db, eid, sw_id,
+                                                                       SWIMA_EVENT_ACTION_CREATION))
+                               {
+                                       goto end;
+                               }
+                               break;
+               }
+
+               if (op != SW_OP_INSTALL)
+               {
+                       sw_id = 0;
+
+                       /* look for existing installed package versions */
+                       e = this->db->create_sw_enumerator(this->db, SW_QUERY_INSTALLED,
+                                                                                          p->package);
+                       if (!e)
+                       {
+                               goto end;
+                       }
+
+                       while (e->enumerate(e, &sw_idx, &nx, &px, &vx, &ix))
+                       {
+                               if (streq(vx, del_version))
+                               {
+                                       /* full match with epoch */
+                                       sw_id = sw_idx;
+                                       break;
+                               }
+                               v1 = strchr(vx, ':');
+                               if (v1 && streq(++v1, del_version))
+                               {
+                                       /* match with stripped epoch */
+                                       sw_id_epoch_less = sw_idx;
+                               }
+                       }
+                       e->destroy(e);
+
+                       if (!sw_id && sw_id_epoch_less)
+                       {
+                               /* no full match - fall back to epoch-less match */
+                               sw_id = sw_id_epoch_less;
+                       }
+                       if (sw_id)
+                       {
+                               /* sw identifier exists - update state to 'deleted' */
+                               if (!this->db->update_sw_id(this->db, sw_id, NULL, NULL, FALSE))
+                               {
+                                       goto end;
+                               }
+                       }
+                       else
+                       {
+                               /* new sw identifier - create with state 'deleted' */
+                               sw_id = this->db->set_sw_id(this->db, del_sw_id, p->package,
+                                                                       del_version, this->source, FALSE);
+
+                               /* add creation sw event with eid = 1 */
+                               if (!sw_id || !this->db->add_sw_event(this->db, 1, sw_id,
+                                                                                       SWIMA_EVENT_ACTION_CREATION))
+                               {
+                                       goto end;
+                               }
+                       }
+
+                       /* add creation sw event with current eid */
+                       if (!this->db->add_sw_event(this->db, eid, sw_id,
+                                                               SWIMA_EVENT_ACTION_DELETION))
+                       {
+                               goto end;
+                       }
+               }
+               free_package(p);
+
+               if (args.len < 2)
+               {
+                       break;
+               }
+               args = chunk_skip(args, 2);
+       }
+       p = NULL;
+       success = TRUE;
+
+end:
+       free_package(p);
+
+       return success;
+}
+
+METHOD(sw_collector_history_t, merge_installed_packages, bool,
+       private_sw_collector_history_t *this)
+{
+       uint32_t sw_id, count = 0;
+       char *package, *arch, *version, *v1, *name, *n1;
+       bool installed, success = FALSE;
+       sw_collector_dpkg_t *dpkg;
+       enumerator_t *enumerator;
+
+       DBG1(DBG_IMC, "Merging:");
+
+       dpkg = sw_collector_dpkg_create();
+       if (!dpkg)
+       {
+               return FALSE;
+       }
+
+       enumerator = dpkg->create_sw_enumerator(dpkg);
+       while (enumerator->enumerate(enumerator, &package, &arch, &version))
+       {
+               name = this->info->create_sw_id(this->info, package, version);
+               DBG3(DBG_IMC, "  %s merged", name);
+
+               sw_id = this->db->get_sw_id(this->db, name, NULL, NULL, NULL,
+                                                                       &installed);
+               if (sw_id)
+               {
+                       if (!installed)
+                       {
+                               DBG1(DBG_IMC, "  warning: existing sw_id %u"
+                                                         " is not installed", sw_id);
+
+                               if (!this->db->update_sw_id(this->db, sw_id, name, version,
+                                                                                       TRUE))
+                               {
+                                       free(name);
+                                       goto end;
+                               }
+                       }
+               }
+               else
+               {
+                       /* check for a Debian epoch number */
+                       v1 = strchr(version, ':');
+                       if (v1)
+                       {
+                               /* check for existing and installed epoch-less version */
+                               n1 = this->info->create_sw_id(this->info, package, ++v1);
+                               sw_id = this->db->get_sw_id(this->db, n1, NULL, NULL, NULL,
+                                                                                       &installed);
+                               free(n1);
+
+                               if (sw_id && installed)
+                               {
+                                       /* add epoch to existing version */
+                                       if (!this->db->update_sw_id(this->db, sw_id, name, version,
+                                                                                               installed))
+                                       {
+                                               free(name);
+                                               goto end;
+                                       }
+                               }
+                               else
+                               {
+                                       sw_id = 0;
+                               }
+                       }
+               }
+
+               if (!sw_id)
+               {
+                       /* new sw identifier - create with state 'installed' */
+                       sw_id = this->db->set_sw_id(this->db, name, package, version,
+                                                                               this->source, TRUE);
+
+                       /* add creation sw event with eid = 1 */
+                       if (!sw_id || !this->db->add_sw_event(this->db, 1, sw_id,
+                                                                               SWIMA_EVENT_ACTION_CREATION))
+                       {
+                               free(name);
+                               goto end;
+                       }
+
+               }
+               free(name);
+               count++;
+       }
+       success = TRUE;
+
+       DBG1(DBG_IMC, "  merged %u installed packages, %u registered in database",
+                count, this->db->get_sw_id_count(this->db, SW_QUERY_INSTALLED));
+
+end:
+       enumerator->destroy(enumerator);
+       dpkg->destroy(dpkg);
+
+       return success;
+}
+
+METHOD(sw_collector_history_t, destroy, void,
+       private_sw_collector_history_t *this)
+{
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+sw_collector_history_t *sw_collector_history_create(sw_collector_info_t *info,
+                                                                                                       sw_collector_db_t *db,
+                                                                                                       uint8_t source)
+{
+       private_sw_collector_history_t *this;
+
+       INIT(this,
+               .public = {
+                       .extract_timestamp = _extract_timestamp,
+                       .extract_packages = _extract_packages,
+                       .merge_installed_packages = _merge_installed_packages,
+                       .destroy = _destroy,
+               },
+               .source = source,
+               .info = info,
+               .db = db,
+       );
+
+       return &this->public;
+}
diff --git a/src/sw-collector/sw_collector_history.h b/src/sw-collector/sw_collector_history.h
new file mode 100644 (file)
index 0000000..857cc17
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup sw_collector_history_t sw_collector_history
+ * @{ @ingroup sw_collector
+ */
+
+#ifndef SW_COLLECTOR_HISTORY_H_
+#define SW_COLLECTOR_HISTORY_H_
+
+#include "sw_collector_history.h"
+#include "sw_collector_info.h"
+#include "sw_collector_db.h"
+
+#include <library.h>
+#include <utils/debug.h>
+#include <utils/lexparser.h>
+
+typedef struct sw_collector_history_t sw_collector_history_t;
+typedef enum sw_collector_history_op_t sw_collector_history_op_t;
+
+/**
+ * Define major history event operations
+ */
+enum sw_collector_history_op_t {
+       SW_OP_INSTALL,
+       SW_OP_UPGRADE,
+       SW_OP_REMOVE
+};
+
+/**
+ * Software collector history object
+ */
+struct sw_collector_history_t {
+
+       /**
+        * Extract timestamp from event in installation history
+        *
+        * @param args                  Arguments to be processed
+        * @param buf                   timestamp buffer for 21 byte RFC 3339 string
+        * @return                              TRUE if extraction succeeded
+        */
+       bool (*extract_timestamp)(sw_collector_history_t *this, chunk_t args,
+                                                         char *buf);
+
+       /**
+        * Extract packages from event in installation history 
+        *
+        * @param args                  Arguments to be processed
+        * @param eid                   Primary key pointing to current event
+        * @param op                    Extraction operation 
+        * @return                              TRUE if extraction succeeded
+        */
+       bool (*extract_packages)(sw_collector_history_t *this, chunk_t args,
+                                                        uint32_t eid, sw_collector_history_op_t op);
+
+       /**
+        * Merge packages from initial installation
+        *
+        * @return                              TRUE if merge succeeded
+        */
+       bool (*merge_installed_packages)(sw_collector_history_t *this);
+
+       /**
+        * Destroy sw_collector_history_t object
+        */
+       void (*destroy)(sw_collector_history_t *this);
+
+};
+
+/**
+ * Create an sw_collector_history_t instance
+ *
+ * @param info                         Internal reference to collector info
+ * @param db                           Internal reference to collector database
+ * @param source                       Software event source number
+ */
+sw_collector_history_t* sw_collector_history_create(sw_collector_info_t *info,
+                                                                                                       sw_collector_db_t *db,
+                                                                                                       uint8_t source);
+
+#endif /** SW_COLLECTOR_HISTORY_H_ @}*/
diff --git a/src/sw-collector/sw_collector_info.c b/src/sw-collector/sw_collector_info.c
new file mode 100644 (file)
index 0000000..b046450
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+
+#include "sw_collector_info.h"
+
+#include <library.h>
+#include <utils/lexparser.h>
+
+typedef struct private_sw_collector_info_t private_sw_collector_info_t;
+
+/**
+ * Private data of an sw_collector_info_t object.
+ */
+struct private_sw_collector_info_t {
+
+       /**
+        * Public members of sw_collector_info_state_t
+        */
+       sw_collector_info_t public;
+
+       /**
+        * tagCreator
+        */
+       char *tag_creator;
+
+       /**
+        * OS string 'Name_Version-Arch'
+        */
+       char *os;
+
+       /**
+        * Product string 'Name Version Arch'
+        */
+       char *product;
+
+       /**
+        * OS info about endpoint
+        */
+       imc_os_info_t *os_info;
+
+};
+
+/**
+ * Replaces invalid character by a valid one
+ */
+static void sanitize_uri(char *uri, char a, char b)
+{
+       char *pos = uri;
+
+       while (TRUE)
+       {
+               pos = strchr(pos, a);
+               if (!pos)
+               {
+                       break;
+               }
+               *pos = b;
+               pos++;
+       }
+}
+
+METHOD(sw_collector_info_t, get_os_type, os_type_t,
+       private_sw_collector_info_t *this)
+{
+       return this->os_info->get_type(this->os_info);
+}
+
+METHOD(sw_collector_info_t, get_os, char*,
+       private_sw_collector_info_t *this, char **product)
+{
+       if (product)
+       {
+               *product = this->product;
+       }
+       return this->os;
+}
+
+METHOD(sw_collector_info_t, create_sw_id, char*,
+       private_sw_collector_info_t *this, char *package, char *version)
+{
+       char *sw_id;
+
+       if (asprintf(&sw_id, "%s__%s-%s%s%s", this->tag_creator, this->os,
+                                package, strlen(version) ? "-" : "", version) == -1)
+       {
+               return NULL;
+       }
+       sanitize_uri(sw_id, ':', '~');
+       sanitize_uri(sw_id, '+', '~');
+
+       return sw_id;
+}
+
+METHOD(sw_collector_info_t, destroy, void,
+       private_sw_collector_info_t *this)
+{
+       this->os_info->destroy(this->os_info);
+       free(this->os);
+       free(this->product);
+       free(this->tag_creator);
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+sw_collector_info_t *sw_collector_info_create(char *tag_creator)
+{
+       private_sw_collector_info_t *this;
+       chunk_t os_name, os_version, os_arch;
+
+       INIT(this,
+               .public = {
+                       .get_os_type = _get_os_type,
+                       .get_os = _get_os,
+                       .create_sw_id = _create_sw_id,
+                       .destroy = _destroy,
+               },
+               .os_info = imc_os_info_create(),
+               .tag_creator = strdup(tag_creator),
+       );
+
+       os_name = this->os_info->get_name(this->os_info);
+       os_arch = this->os_info->get_version(this->os_info);
+
+       /* get_version() returns version followed by arch */ 
+       if (!extract_token(&os_version, ' ', &os_arch))
+       {
+               DBG1(DBG_IMC, "separation of OS version from arch failed");
+               destroy(this);
+               return NULL;
+       }
+
+       /* construct OS string */
+       if (asprintf(&this->os, "%.*s_%.*s-%.*s", os_name.len, os_name.ptr,
+                                                                                         os_version.len, os_version.ptr,
+                                                                                         os_arch.len, os_arch.ptr) == -1)
+       {
+               DBG1(DBG_IMC, "constructon of OS string failed");
+               destroy(this);
+               return NULL;
+       }
+
+       /* construct product string */
+       if (asprintf(&this->product, "%.*s %.*s %.*s", os_name.len, os_name.ptr,
+                                                                                         os_version.len, os_version.ptr,
+                                                                                         os_arch.len, os_arch.ptr) == -1)
+       {
+               DBG1(DBG_IMC, "constructon of product string failed");
+               destroy(this);
+               return NULL;
+       }
+
+       return &this->public;
+}
diff --git a/src/sw-collector/sw_collector_info.h b/src/sw-collector/sw_collector_info.h
new file mode 100644 (file)
index 0000000..a54d788
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup sw_collector_info_t sw_collector_info
+ * @{ @ingroup sw_collector
+ */
+
+#ifndef SW_COLLECTOR_INFO_H_
+#define SW_COLLECTOR_INFO_H_
+
+typedef struct sw_collector_info_t sw_collector_info_t;
+
+#include "imc/imc_os_info.h"
+
+struct sw_collector_info_t {
+
+       /**
+        * Get OS type
+        *
+        * @return                              OS type
+        */
+       os_type_t (*get_os_type)(sw_collector_info_t *this);
+
+       /**
+        * Get OS and product strings
+        *
+        * @param product               Product string 'Name Version Arch'
+        * @return                              OS string      'Name_Version-Arch'
+        */
+       char* (*get_os)(sw_collector_info_t *this, char **product);
+
+       /**
+        * Create software identifier including tagCreator and OS
+        *
+        * @param package               Package string
+        * @param version               Version string
+        * @return                              Software Identifier string
+        */
+       char* (*create_sw_id)(sw_collector_info_t *this, char *package,
+                                                                                                        char *version);
+
+       /**
+        * Destroy sw_collector_info_t object
+        */
+       void (*destroy)(sw_collector_info_t *this);
+
+};
+
+/**
+ * Create an sw_collector_info_t instance
+ *
+ * @param tag_creator          Regid of tagCreator
+ */
+sw_collector_info_t* sw_collector_info_create(char *tag_creator);
+
+#endif /** SW_COLLECTOR_INFO_H_ @}*/
diff --git a/src/sw-collector/sw_collector_rest_api.c b/src/sw-collector/sw_collector_rest_api.c
new file mode 100644 (file)
index 0000000..6b9b7b9
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "sw_collector_rest_api.h"
+
+#include <rest/rest.h>
+#include <utils/debug.h>
+
+typedef struct private_sw_collector_rest_api_t private_sw_collector_rest_api_t;
+
+/**
+ * Private data of an sw_collector_rest_api_t object.
+ */
+struct private_sw_collector_rest_api_t {
+
+       /**
+        * Public members of sw_collector_rest_api_state_t
+        */
+       sw_collector_rest_api_t public;
+
+       /**
+        * Software collector database
+        */
+       sw_collector_db_t *db;
+
+       /**
+        * REST API of central collector database
+        */
+       rest_t *rest_api;
+
+};
+
+/**
+ * Put all locally retrieved software identifiers into a json object
+ */
+static json_object* create_rest_request(private_sw_collector_rest_api_t *this,
+                                                                               sw_collector_db_query_t type)
+{
+       json_object *jrequest, *jarray, *jstring;
+       char *name, *package, *version;
+       uint32_t sw_id, i;
+       enumerator_t *e;
+
+       jrequest = json_object_new_object();
+       jarray = json_object_new_array();
+       json_object_object_add(jrequest, "data", jarray);
+
+       e = this->db->create_sw_enumerator(this->db, type, NULL);
+       if (!e)
+       {
+               return NULL;
+       }
+       while (e->enumerate(e, &sw_id, &name, &package, &version, &i))
+       {
+               jstring = json_object_new_string(name);
+               json_object_array_add(jarray, jstring);
+       }
+       e->destroy(e);
+
+       return jrequest;
+}
+
+typedef struct {
+       /** public enumerator interface */
+       enumerator_t public;
+       /** enumerated json array */
+       json_object *jarray;
+       /** current index +1, initialized at 0 */
+       int idx;
+} json_array_enumerator_t;
+
+METHOD(enumerator_t, enumerate, bool,
+       json_array_enumerator_t *this, va_list args)
+{
+       json_object *jvalue;
+       char **out;
+
+       VA_ARGS_VGET(args, out);
+
+       if (this->idx >= json_object_array_length(this->jarray))
+       {
+               return FALSE;
+       }
+
+       jvalue = json_object_array_get_idx(this->jarray, this->idx++);
+       if (json_object_get_type(jvalue) != json_type_string)
+       {
+               DBG1(DBG_IMC, "json_string element expected in json_array");
+               return FALSE;
+       }
+       *out = (char*)json_object_get_string(jvalue);
+
+       return TRUE;
+}
+
+METHOD(enumerator_t, enumerator_destroy, void,
+       json_array_enumerator_t *this)
+{
+       json_object_put(this->jarray);
+       free(this);     
+}
+
+METHOD(sw_collector_rest_api_t, create_sw_enumerator, enumerator_t*,
+       private_sw_collector_rest_api_t *this, sw_collector_db_query_t type)
+{
+       json_array_enumerator_t *enumerator;
+       json_object *jrequest, *jresponse;
+       char cmd[BUF_LEN];
+       status_t status;
+
+       jrequest = create_rest_request(this, type);
+       if (!jrequest)
+       {
+               return NULL;
+       }
+       snprintf(cmd, BUF_LEN, "sessions/0/swid-measurement/");
+
+       status = this->rest_api->post(this->rest_api, cmd, jrequest, &jresponse);
+       json_object_put(jrequest);
+
+       switch (status)
+       {
+               case SUCCESS:
+               case NOT_FOUND:
+                       jresponse = json_object_new_array();
+                       break;
+               case NEED_MORE:
+                       if (json_object_get_type(jresponse) != json_type_array)
+                       {
+                               DBG1(DBG_IMC, "REST response was not a json_array");
+                               json_object_put(jresponse);
+                               return NULL;
+                       }
+                       break;
+               case FAILED:
+               default:
+                       return NULL;
+       }
+
+       INIT(enumerator,
+               .public = {
+                       .enumerate = enumerator_enumerate_default,
+                       .venumerate = _enumerate,
+                       .destroy = _enumerator_destroy,
+               },
+               .jarray = jresponse,
+       );
+
+       return &enumerator->public;
+}
+
+METHOD(sw_collector_rest_api_t, destroy, void,
+       private_sw_collector_rest_api_t *this)
+{
+       this->rest_api->destroy(this->rest_api);
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+sw_collector_rest_api_t *sw_collector_rest_api_create(sw_collector_db_t *db)
+{
+       private_sw_collector_rest_api_t *this;
+       int timeout;
+       char *uri;
+
+       uri = lib->settings->get_str(lib->settings, "%s.rest_api.uri", NULL,
+                                                                lib->ns);
+       timeout = lib->settings->get_int(lib->settings, "%s.rest_api.timeout", 120,
+                                                                lib->ns);
+       if (!uri)
+       {
+               DBG1(DBG_IMC, "REST URI to central collector database not set");
+               return NULL;
+       }
+
+       INIT(this,
+               .public = {
+                       .create_sw_enumerator = _create_sw_enumerator,
+                       .destroy = _destroy,
+               },
+               .db = db,
+               .rest_api = rest_create(uri, timeout),
+       );
+
+       return &this->public;
+}
diff --git a/src/sw-collector/sw_collector_rest_api.h b/src/sw-collector/sw_collector_rest_api.h
new file mode 100644 (file)
index 0000000..ca45230
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup sw_collector_rest_api_t sw_collector_rest_api
+ * @{ @ingroup imc_swima
+ */
+
+#ifndef SW_COLLECTOR_REST_API_H_
+#define SW_COLLECTOR_REST_API_H_
+
+#include "sw_collector_db.h"
+
+typedef struct sw_collector_rest_api_t sw_collector_rest_api_t;
+
+/**
+ * Software collector REST API object
+ */
+struct sw_collector_rest_api_t {
+
+       /**
+        * List of locally stored software identifiers that are not registered
+        * in a central collector database
+        *
+        * @param type                  Query type (ALL, INSTALLED, DELETED)
+        * @return                              Enumerator
+        */
+       enumerator_t* (*create_sw_enumerator)(sw_collector_rest_api_t *this,
+                                                                                 sw_collector_db_query_t type);
+
+       /**
+        * Destroy sw_collector_rest_api_t object
+        */
+       void (*destroy)(sw_collector_rest_api_t *this);
+
+};
+
+/**
+ * Create an sw_collector_rest_api_t instance
+ *
+ * @param db                           Software collector database to be used
+ */
+sw_collector_rest_api_t* sw_collector_rest_api_create(sw_collector_db_t *db);
+
+#endif /** SW_COLLECTOR_REST_API_H_ @}*/
diff --git a/src/sw-collector/sw_collector_tables.sql b/src/sw-collector/sw_collector_tables.sql
new file mode 100644 (file)
index 0000000..b7b430b
--- /dev/null
@@ -0,0 +1,31 @@
+/* SQLit database for an Endpoint Collector */
+
+DROP TABLE IF EXISTS "events";
+CREATE TABLE "events" (
+  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+  "epoch" INTEGER NOT NULL,
+  "timestamp" CHAR(20) NOT NULL
+);
+
+DROP TABLE IF EXISTS "sw_identifiers";
+CREATE TABLE "sw_identifiers" (
+  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+  "name" VARCHAR(255) NOT NULL,
+  "package" VARCHAR(255) NOT NULL,
+  "version" VARCHAR(255) NOT NULL,
+  "source" INTEGER DEFAULT 0,
+  "installed" INTEGER DEFAULT 1,
+  "tag" TEXT
+  );
+DROP INDEX IF EXISTS "sw_identifiers_name";
+CREATE INDEX "sw_identifiers_name" ON "sw_identifiers" (
+  "name"
+);
+
+DROP TABLE IF EXISTS "sw_events";
+CREATE TABLE "sw_events" (
+  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+  "eid" INTEGER REFERENCES "events" ("id"),
+  "sw_id" INTEGER NOT NULL REFERENCES "sw_identifiers" ("id"),
+  "action" INTEGER NOT NULL
+);