sw-collector: Query central collector database
authorAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 26 Jun 2017 16:07:53 +0000 (18:07 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Sat, 8 Jul 2017 21:19:51 +0000 (23:19 +0200)
conf/options/sw-collector.opt
src/libimcv/plugins/imc_swima/Makefile.am
src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.c
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.h
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.h
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.c [new file with mode: 0644]
src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.h [new file with mode: 0644]

index ccd2977..cf4ce36 100644 (file)
@@ -13,5 +13,17 @@ sw-collector.history =
 sw-collector.first_time = 0000-00-00T00:00:00Z
        Time in UTC when the Linux OS was installed.
 
+sw-collector.rest_api.uri =
+       HTTP URI of the central collector's REST API.
+
+sw-collector.rest_api.timeout = 120
+       Timeout of REST API HTTP POST transaction.
+
+sw-collector.tag_creator.name = strongSwan Project
+       Name of the tagCreator entity.
+
+sw-collector.tag_creator.regid = strongswan.org
+       regid of the tagCreator entity.
+
 sw-collector.load =
        Plugins to load in sw-collector tool.
index 67ea25e..cde2098 100644 (file)
@@ -23,7 +23,7 @@ AM_CPPFLAGS = \
        -DPLUGINS=\""random openssl sqlite curl"\"
 
 AM_CFLAGS = \
-       $(PLUGIN_CFLAGS)
+       $(PLUGIN_CFLAGS) $(json_CFLAGS)
 
 imcv_LTLIBRARIES = imc-swima.la
 
@@ -37,11 +37,14 @@ 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_history.h sw_collector/sw_collector_history.c
+       sw_collector/sw_collector_history.h sw_collector/sw_collector_history.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
+       $(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
index 24cf4fb..bb72349 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "sw_collector_db.h"
 #include "sw_collector_history.h"
+#include "sw_collector_rest_api.h"
 
 #include <library.h>
 #include <utils/debug.h>
@@ -41,7 +42,9 @@ typedef enum collector_op_t collector_op_t;
 
 enum collector_op_t {
        COLLECTOR_OP_EXTRACT,
-       COLLECTOR_OP_LIST
+       COLLECTOR_OP_LIST,
+       COLLECTOR_OP_UNREGISTERED,
+       COLLECTOR_OP_GENERATE
 };
 
 /**
@@ -108,6 +111,7 @@ static void usage(void)
 Usage:\n\
   sw-collector --help\n\
   sw-collector [--debug <level>] [--quiet] --list\n\
+  sw-collector [--debug <level>] [--quiet] --unregistered|--generate\n\
   sw-collector [--debug <level>] [--quiet] [--count <event count>]\n");
 }
 
@@ -129,12 +133,14 @@ static collector_op_t do_args(int argc, char *argv[])
                        { "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' },
                        { "quiet", no_argument, NULL, 'q' },
+                       { "unregistered", no_argument, NULL, 'u' },
                        { 0,0,0,0 }
                };
 
-               c = getopt_long(argc, argv, "hc:d:lq", long_opts, NULL);
+               c = getopt_long(argc, argv, "hc:d:lqu", long_opts, NULL);
                switch (c)
                {
                        case EOF:
@@ -149,12 +155,18 @@ static collector_op_t do_args(int argc, char *argv[])
                        case 'd':
                                debug_level = atoi(optarg);
                                continue;
+                       case 'g':
+                               op = COLLECTOR_OP_GENERATE;
+                               continue;
                        case 'l':
                                op = COLLECTOR_OP_LIST;
                                continue;
                        case 'q':
                                stderr_quiet = TRUE;
                                continue;
+                       case 'u':
+                               op = COLLECTOR_OP_UNREGISTERED;
+                               continue;
                        default:
                                usage();
                                exit(EXIT_FAILURE);
@@ -177,8 +189,8 @@ static int extract_history(sw_collector_db_t *db)
        bool skip = TRUE;
 
        /* open history file for reading */
-       history_path= lib->settings->get_str(lib->settings, "sw-collector.history",
-                                                                                NULL);
+       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");
@@ -308,7 +320,7 @@ end:
 }
 
 /**
- * List all software identifiers stored in the collector database
+ * List all endpoint software identifiers stored in local collector database
  */
 static int list_identifiers(sw_collector_db_t *db)
 {
@@ -316,7 +328,7 @@ static int list_identifiers(sw_collector_db_t *db)
        char *name, *package, *version;
        uint32_t count = 0, installed_count = 0, installed;
 
-       e = db->create_sw_enumerator(db, FALSE);
+       e = db->create_sw_enumerator(db, SW_QUERY_ALL);
        if (!e)
        {
                return EXIT_FAILURE;
@@ -337,6 +349,156 @@ static int list_identifiers(sw_collector_db_t *db)
        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;
+}
+
+static int generate_tags(sw_collector_db_t *db)
+{
+       sw_collector_history_t *os_info;
+       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);
+
+       os_info = sw_collector_history_create(db, 0);
+       if (!os_info)
+       {
+               return EXIT_FAILURE;
+       }
+       os_info->get_os(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:
+       os_info->destroy(os_info);
+       DESTROY_IF(rest_api);
+
+       return status;
+}
+
 int main(int argc, char *argv[])
 {
        sw_collector_db_t *db = NULL;
@@ -362,13 +524,13 @@ int main(int argc, char *argv[])
 
        /* load sw-collector plugins */
        if (!lib->plugins->load(lib->plugins,
-                       lib->settings->get_str(lib->settings, "sw-collector.load", 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, "sw-collector.database", NULL);
+       uri = lib->settings->get_str(lib->settings, "%s.database", NULL, lib->ns);
        if (!uri)
        {
                fprintf(stderr, "sw-collector.database URI not set.\n");
@@ -389,6 +551,11 @@ int main(int argc, char *argv[])
                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(db);
                default:
                        break;
        }
index e3c8b00..62a6507 100644 (file)
@@ -108,8 +108,8 @@ METHOD(sw_collector_db_t, add_sw_event, bool,
        return TRUE;
 }
 
-METHOD(sw_collector_db_t, get_sw_id, uint32_t,
-       private_sw_collector_db_t *this, char *package, char *version, char *name,
+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, bool check)
 {
        uint32_t sw_id = 0, status;
@@ -179,22 +179,66 @@ METHOD(sw_collector_db_t, get_sw_id, uint32_t,
        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, bool installed_only)
+       private_sw_collector_db_t *this, sw_collector_db_query_t type)
 {
-       uint32_t count;
+       uint32_t count, installed;
        enumerator_t *e;
 
-       if (installed_only)
+       if (type == SW_QUERY_ALL)
        {
                e = this->db->query(this->db,
-                       "SELECT COUNT(installed) FROM sw_identifiers WHERE installed = 1 ",
-                        DB_UINT);
+                       "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", DB_UINT);
+                       "SELECT COUNT(installed) FROM sw_identifiers WHERE installed = ?",
+                        DB_UINT, installed, DB_UINT);
        }
 
        if (!e)
@@ -212,22 +256,24 @@ METHOD(sw_collector_db_t, get_sw_id_count, uint32_t,
 }
 
 METHOD(sw_collector_db_t, create_sw_enumerator, enumerator_t*,
-       private_sw_collector_db_t *this, bool installed_only)
+       private_sw_collector_db_t *this, sw_collector_db_query_t type)
 {
        enumerator_t *e;
+       uint32_t installed;
 
-       if (installed_only)
+       if (type == SW_QUERY_ALL)
        {
                e = this->db->query(this->db,
                                "SELECT name, package, version, installed FROM sw_identifiers "
-                               "WHERE installed = 1 ORDER BY name ASC",
-                                DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
+                               "ORDER BY name ASC", DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
        }
        else
        {
+               installed = (type == SW_QUERY_INSTALLED);
                e = this->db->query(this->db,
                                "SELECT name, package, version, installed FROM sw_identifiers "
-                               "ORDER BY name ASC", DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
+                               "WHERE installed = ? ORDER BY name ASC",
+                                DB_UINT, installed, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
        }
        if (!e)
        {
@@ -259,6 +305,7 @@ sw_collector_db_t *sw_collector_db_create(char *uri)
                        .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,
                        .create_sw_enumerator = _create_sw_enumerator,
@@ -296,6 +343,9 @@ sw_collector_db_t *sw_collector_db_create(char *uri)
                }
                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");
index de3138d..c4e82a9 100644 (file)
 #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
@@ -31,7 +41,7 @@ typedef struct sw_collector_db_t sw_collector_db_t;
 struct sw_collector_db_t {
 
        /**
-        * Add event to database
+        * bAdd event to database
         *
         * @param timestamp             Timestamp in 20 octet RFC 3339 format
         * @return                              Primary key pointing to event ID or 0 if failed
@@ -61,35 +71,50 @@ struct sw_collector_db_t {
                                                 uint8_t action);
 
        /**
-        * Get software_identifier, creating one if it doesn't exist yet
+        * Set software_identifier, checking if the identifier already exists
         *
+        * @param name                  Software identifier
         * @param package               Software package
         * @param version               Version of software package
-        * @param name                  Software identifier
         * @param source                Source ID of the software collector
         * @param installed             Installation status to be set, TRUE if installed
         * @param check                 Check if SW ID is already installed
         * @return                              Primary key pointing to SW ID or 0 if failed
         */
-       uint32_t (*get_sw_id)(sw_collector_db_t *this, char *package, char *version,
-                                                 char *name, uint8_t source, bool installed, bool check);
+       uint32_t (*set_sw_id)(sw_collector_db_t *this, char *name, char *package,
+                                                 char *version, uint8_t source, bool installed,
+                                                 bool check);
+
+       /**
+        * 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 installed_only        Count installed SW IDs if TRUE
-        * @return                                      Count
+        * @param type                  Query type (ALL, INSTALLED, DELETED)
+        * @return                              Count
         */
-       uint32_t (*get_sw_id_count)(sw_collector_db_t *this, bool installed_only);
+       uint32_t (*get_sw_id_count)(sw_collector_db_t *this,
+                                                               sw_collector_db_query_t type);
 
        /**
         * Enumerate over all collected [installed] software identities
         *
-        * @param installed_only        Return only installed software identities
-        * @return                                      Enumerator
+        * @param type                  Query type (ALL, INSTALLED, DELETED)
+        * @return                              Enumerator
         */
        enumerator_t* (*create_sw_enumerator)(sw_collector_db_t *this,
-                                                                                 bool installed_only);
+                                                                                 sw_collector_db_query_t type);
 
        /**
         * Destroy sw_collector_db_t object
index 4cca637..9371285 100644 (file)
@@ -45,6 +45,11 @@ struct private_sw_collector_history_t {
        char *os;
 
        /**
+        * Product string 'name version arch'
+        */
+       char *product;
+
+       /**
         * OS info about endpoint
         */
        imc_os_info_t *os_info;
@@ -61,6 +66,16 @@ struct private_sw_collector_history_t {
 
 };
 
+METHOD(sw_collector_history_t, get_os, char*,
+       private_sw_collector_history_t *this, char **product)
+{
+       if (product)
+       {
+               *product = this->product;
+       }
+       return this->os;
+}
+
 /**
  * Define auxiliary package_t list item object
  */
@@ -277,7 +292,14 @@ METHOD(sw_collector_history_t, extract_packages, bool,
                        goto end;
                }
 
-               sw_id = this->db->get_sw_id(this->db, p->package, p->version, p->sw_id,
+               /* packages without version information cannot be handled */
+               if (strlen(p->version) == 0)
+               {
+                       free_package(p);
+                       continue;
+               }
+
+               sw_id = this->db->set_sw_id(this->db, p->sw_id, p->package,     p->version,
                                                                        this->source, op != SW_OP_REMOVE, FALSE);
                if (!sw_id)
                {
@@ -291,8 +313,9 @@ METHOD(sw_collector_history_t, extract_packages, bool,
 
                if (op == SW_OP_UPGRADE)
                {
-                       sw_id = this->db->get_sw_id(this->db, p->package, p->old_version,
-                                                                               p->old_sw_id, this->source, FALSE, FALSE);
+                       sw_id = this->db->set_sw_id(this->db, p->old_sw_id, p->package,
+                                                                               p->old_version, this->source, FALSE,
+                                                                               FALSE);
                        if (!sw_id)
                        {
                                goto end;
@@ -376,7 +399,7 @@ METHOD(sw_collector_history_t, merge_installed_packages, bool,
                name = create_sw_id(this->tag_creator, this->os, package, version);
                DBG3(DBG_IMC, "  %s merged", name);
 
-               sw_id = this->db->get_sw_id(this->db, package, version, name,
+               sw_id = this->db->set_sw_id(this->db, name, package, version,
                                                                        this->source, TRUE, TRUE);
                free(name);
                if (!sw_id)
@@ -387,7 +410,7 @@ METHOD(sw_collector_history_t, merge_installed_packages, bool,
        }
        success = TRUE;
        DBG1(DBG_IMC, "  merged %u installed packages, %u registed in database",
-                count, this->db->get_sw_id_count(this->db, TRUE));
+                count, this->db->get_sw_id_count(this->db, SW_QUERY_INSTALLED));
 
 end:
        pclose(file);
@@ -399,6 +422,7 @@ METHOD(sw_collector_history_t, destroy, void,
 {
        this->os_info->destroy(this->os_info);
        free(this->os);
+       free(this->product);
        free(this);
 }
 
@@ -414,6 +438,7 @@ sw_collector_history_t *sw_collector_history_create(sw_collector_db_t *db,
 
        INIT(this,
                .public = {
+                       .get_os = _get_os,
                        .extract_timestamp = _extract_timestamp,
                        .extract_packages = _extract_packages,
                        .merge_installed_packages = _merge_installed_packages,
@@ -423,7 +448,7 @@ sw_collector_history_t *sw_collector_history_create(sw_collector_db_t *db,
                .source = source,
                .os_info = imc_os_info_create(),
                .tag_creator = lib->settings->get_str(lib->settings,
-                               "sw-collector.tag_creator", "strongswan.org"),
+                               "%s.tag_creator.regid", "strongswan.org", lib->ns),
        );
 
        os_type = this->os_info->get_type(this->os_info);
@@ -445,6 +470,8 @@ sw_collector_history_t *sw_collector_history_create(sw_collector_db_t *db,
                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)
@@ -454,5 +481,15 @@ sw_collector_history_t *sw_collector_history_create(sw_collector_db_t *db,
                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;
 }
index d6efcc5..d1a3b21 100644 (file)
@@ -45,6 +45,14 @@ enum sw_collector_history_op_t {
 struct sw_collector_history_t {
 
        /**
+        * Get OS and product strings
+        *
+        * @param product               Product string formed from OS info
+        * @return                              OS string formed from OS info
+        */
+       char* (*get_os)(sw_collector_history_t *this, char **product);
+
+       /**
         * Extract timestamp from event in installation history
         *
         * @param args                  Arguments to be processed
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
new file mode 100644 (file)
index 0000000..15108a0
--- /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 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);
+       if (!e)
+       {
+               return NULL;
+       }
+       while (e->enumerate(e, &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
new file mode 100644 (file)
index 0000000..e44ab6d
--- /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 database 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_ @}*/