diff options
author | Andreas Steffen <andreas.steffen@strongswan.org> | 2017-07-09 23:17:22 +0200 |
---|---|---|
committer | Andreas Steffen <andreas.steffen@strongswan.org> | 2017-07-12 19:12:22 +0200 |
commit | 5b1dbc3a8da2244e14a6a488647acbfa91ead517 (patch) | |
tree | f348bbd1e357f4b33fcd4684b11111b98031ae88 /src/libimcv/plugins/imc_swima | |
parent | eab650d62fac17c9ea24143818aca7e9a66cceb1 (diff) | |
download | strongswan-5b1dbc3a8da2244e14a6a488647acbfa91ead517.tar.bz2 strongswan-5b1dbc3a8da2244e14a6a488647acbfa91ead517.tar.xz |
sw-collector: Check for epoch-less Debian package versions
Diffstat (limited to 'src/libimcv/plugins/imc_swima')
12 files changed, 881 insertions, 314 deletions
diff --git a/src/libimcv/plugins/imc_swima/Makefile.am b/src/libimcv/plugins/imc_swima/Makefile.am index cde20980f..e14556c62 100644 --- a/src/libimcv/plugins/imc_swima/Makefile.am +++ b/src/libimcv/plugins/imc_swima/Makefile.am @@ -37,7 +37,9 @@ 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 = \ diff --git a/src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c b/src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c index bb72349c7..2f4df181a 100644 --- a/src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw-collector.c @@ -23,14 +23,18 @@ # 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 */ @@ -44,7 +48,8 @@ enum collector_op_t { COLLECTOR_OP_EXTRACT, COLLECTOR_OP_LIST, COLLECTOR_OP_UNREGISTERED, - COLLECTOR_OP_GENERATE + COLLECTOR_OP_GENERATE, + COLLECTOR_OP_MIGRATE }; /** @@ -110,9 +115,9 @@ static void usage(void) printf("\ 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"); + 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"); } /** @@ -135,12 +140,13 @@ static collector_op_t do_args(int argc, char *argv[]) { "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:lqu", long_opts, NULL); + c = getopt_long(argc, argv, "hc:d:lmqu", long_opts, NULL); switch (c) { case EOF: @@ -161,6 +167,9 @@ static collector_op_t do_args(int argc, char *argv[]) case 'l': op = COLLECTOR_OP_LIST; continue; + case 'm': + op = COLLECTOR_OP_MIGRATE; + continue; case 'q': stderr_quiet = TRUE; continue; @@ -179,38 +188,44 @@ static collector_op_t do_args(int argc, char *argv[]) /** * Extract software events from apt history log files */ -static int extract_history(sw_collector_db_t *db) +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, *last_time = NULL, rfc_time[21]; + 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 FALSE; + return EXIT_FAILURE; } h = chunk_map(history_path, FALSE); if (!h) { fprintf(stderr, "opening '%s' failed: %s", history, strerror(errno)); - return FALSE; + return EXIT_FAILURE; } history_chunk = *h; /* Instantiate history extractor */ - history = sw_collector_history_create(db, 1); - if (!history) - { - /* OS is not supported */ - goto end; - } + 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) @@ -313,7 +328,7 @@ static int extract_history(sw_collector_db_t *db) end: free(last_time); - DESTROY_IF(history); + history->destroy(history); chunk_unmap(h); return status; @@ -326,14 +341,14 @@ static int list_identifiers(sw_collector_db_t *db) { enumerator_t *e; char *name, *package, *version; - uint32_t count = 0, installed_count = 0, installed; + uint32_t sw_id, count = 0, installed_count = 0, installed; - e = db->create_sw_enumerator(db, SW_QUERY_ALL); + e = db->create_sw_enumerator(db, SW_QUERY_ALL, NULL); if (!e) { return EXIT_FAILURE; } - while (e->enumerate(e, &name, &package, &version, &installed)) + while (e->enumerate(e, &sw_id, &name, &package, &version, &installed)) { printf("%s,%s,%s,%d\n", name, package, version, installed); if (installed) @@ -433,9 +448,12 @@ static char* generate_tag(char *name, char *package, char *version, return (res == -1) ? NULL : tag; } -static int generate_tags(sw_collector_db_t *db) +/** + * 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_history_t *os_info; sw_collector_rest_api_t *rest_api; char *pos, *name, *package, *version, *entity, *regid, *product, *tag; enumerator_t *enumerator; @@ -446,13 +464,7 @@ static int generate_tags(sw_collector_db_t *db) "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); + info->get_os(info, &product); rest_api = sw_collector_rest_api_create(db); if (!rest_api) @@ -493,18 +505,69 @@ static int generate_tags(sw_collector_db_t *db) count); end: - os_info->destroy(os_info); 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_arch[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)) + { + if (streq(arch, "all")) + { + continue; + } + + /* Concatenate package and architecture strings */ + snprintf(package_arch, BUF_LEN, "%s:%s", package, arch); + + res = db->update_package(db, package, package_arch); + if (res < 0) + { + status = EXIT_FAILURE; + break; + } + else if (res > 0) + { + count += res; + DBG2(DBG_IMC, "replaced '%s' by '%s'", package, package_arch); + } + } + 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; - int status; + char *uri, *tag_creator; + int status = EXIT_FAILURE; op = do_args(argc, argv); @@ -543,10 +606,15 @@ int main(int argc, char *argv[]) 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(db); + status = extract_history(info, db); break; case COLLECTOR_OP_LIST: status = list_identifiers(db); @@ -555,11 +623,14 @@ int main(int argc, char *argv[]) status = unregistered_identifiers(db); break; case COLLECTOR_OP_GENERATE: - status = generate_tags(db); - default: + 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 index 62a650787..206cbde2f 100644 --- a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.c +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.c @@ -110,71 +110,19 @@ METHOD(sw_collector_db_t, add_sw_event, bool, 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) + uint8_t source, bool installed) { - uint32_t sw_id = 0, status; - enumerator_t *e; + uint32_t sw_id; - /* Does software identifier already exist in database? */ - e = this->db->query(this->db, - "SELECT id, installed FROM sw_identifiers WHERE name = ?", - DB_TEXT, name, DB_UINT, DB_UINT); - if (!e) + 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, "database query for sw_identifier failed"); + DBG1(DBG_IMC, "unable to insert sw_id into database"); return 0; } - if (!e->enumerate(e, &sw_id, &status)) - { - sw_id = 0; - } - e->destroy(e); - - if (sw_id) - { - if (status == installed) - { - if (!check) - { - DBG1(DBG_IMC, " Warning: sw_id %u is already %s", sw_id, - status ? "installed" : "deleted"); - } - return sw_id; - } - if (check) - { - DBG1(DBG_IMC, " Warning: sw_id %u is %s", sw_id, - status ? "installed" : "deleted"); - } - - /* Change installation status */ - if (this->db->execute(this->db, NULL, - "UPDATE sw_identifiers SET installed = ? WHERE id = ?", - DB_UINT, installed, DB_UINT, sw_id) != 1) - { - DBG1(DBG_IMC, "unable to update sw_id status in database"); - return 0; - } - } - else - { - /* Create new software identifier */ - 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; - } - - if (check || !installed) - { - add_sw_event(this, 1, sw_id, SWIMA_EVENT_ACTION_CREATION); - } - } return sw_id; } @@ -255,25 +203,91 @@ METHOD(sw_collector_db_t, get_sw_id_count, uint32_t, 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, char *package_new) +{ + int count; + + count = this->db->execute(this->db, NULL, + "UPDATE sw_identifiers SET package = ? " + "WHERE package = ?", DB_TEXT, package_new, DB_TEXT, package); + 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) + private_sw_collector_db_t *this, sw_collector_db_query_t type, char *package) { enumerator_t *e; - uint32_t installed; + u_int installed; if (type == SW_QUERY_ALL) { - 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); + 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); - e = this->db->query(this->db, - "SELECT name, package, version, installed FROM sw_identifiers " - "WHERE installed = ? ORDER BY name ASC", - DB_UINT, installed, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT); + + 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) { @@ -308,6 +322,8 @@ sw_collector_db_t *sw_collector_db_create(char *uri) .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, }, 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 index c4e82a990..88a13bbdb 100644 --- a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.h +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_db.h @@ -78,12 +78,10 @@ struct sw_collector_db_t { * @param version Version of software package * @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 (*set_sw_id)(sw_collector_db_t *this, char *name, char *package, - char *version, uint8_t source, bool installed, - bool check); + char *version, uint8_t source, bool installed); /** * Get software_identifier record @@ -108,13 +106,37 @@ struct sw_collector_db_t { 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 Package name to be changed + * @param package_new New package name + * @return TRUE if update successful + */ + int (*update_package)(sw_collector_db_t *this, char *package, + char *package_new); + + /** * 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); + sw_collector_db_query_t type, + char *package); /** * Destroy sw_collector_db_t object 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 new file mode 100644 index 000000000..b5a858297 --- /dev/null +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.c @@ -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/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.h b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.h new file mode 100644 index 000000000..eab792e8a --- /dev/null +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_dpkg.h @@ -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/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c index 9371285d4..0fc6164cd 100644 --- a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.c @@ -18,8 +18,8 @@ #include <time.h> #include "sw_collector_history.h" +#include "sw_collector_dpkg.h" -#include "imc/imc_os_info.h" #include "swima/swima_event.h" typedef struct private_sw_collector_history_t private_sw_collector_history_t; @@ -35,29 +35,14 @@ struct private_sw_collector_history_t { sw_collector_history_t public; /** - * tagCreator - */ - char *tag_creator; - - /** - * OS string 'name_version-arch' - */ - char *os; - - /** - * Product string 'name version arch' - */ - char *product; - - /** - * OS info about endpoint + * Software Event Source Number */ - imc_os_info_t *os_info; + uint8_t source; /** - * Software Event Source Number + * Reference to OS info object */ - uint8_t source; + sw_collector_info_t *info; /** * Reference to collector database @@ -66,16 +51,6 @@ 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 */ @@ -90,52 +65,9 @@ struct package_t { }; /** - * 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++; - } -} - -/** - * Create software identifier - */ -char* create_sw_id(char *tag_creator, char *os, char *package, char *version) -{ - char *pos, *sw_id; - size_t len; - - /* Remove architecture from package name */ - pos = strchr(package, ':'); - len = pos ? (pos - package) : strlen(package); - - /* Build software identifier */ - if (asprintf(&sw_id, "%s__%s-%.*s%s%s", tag_creator, os, len, package, - strlen(version) ? "-" : "", version) == -1) - { - return NULL; - } - sanitize_uri(sw_id, ':', '~'); - sanitize_uri(sw_id, '+', '~'); - - return sw_id; -} - -/** * Create package_t list item object */ -static package_t* create_package(char* tag_creator, char *os, chunk_t package, +static package_t* create_package(sw_collector_info_t *info, chunk_t package, chunk_t version, chunk_t old_version) { package_t *this; @@ -146,11 +78,11 @@ static package_t* create_package(char* tag_creator, char *os, chunk_t package, .old_version = strndup(old_version.ptr, old_version.len), ) - this->sw_id = create_sw_id(tag_creator, os, this->package, this->version); + this->sw_id = info->create_sw_id(info, this->package, this->version); if (old_version.len) { - this->old_sw_id = create_sw_id(tag_creator, os, this->package, - this->old_version); + this->old_sw_id = info->create_sw_id(info, this->package, + this->old_version); } return this; @@ -175,8 +107,8 @@ static void free_package(package_t *this) /** * Extract and parse a single package item */ -static package_t* extract_package(chunk_t item, char *tag_creator, char *os, - sw_collector_history_op_t op) +static package_t* extract_package(chunk_t item, sw_collector_info_t *info, + sw_collector_history_op_t op) { chunk_t package, version, old_version; package_t *p; @@ -208,7 +140,7 @@ static package_t* extract_package(chunk_t item, char *tag_creator, char *os, version = item; } } - p = create_package(tag_creator, os, package, version, old_version); + p = create_package(info, package, version, old_version); /* generate log entry */ if (op == SW_OP_UPGRADE) @@ -277,16 +209,22 @@ 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; - uint32_t sw_id; chunk_t item; - bool success = FALSE; eat_whitespace(&args); while (extract_token(&item, ')', &args)) { - p = extract_package(item, this->tag_creator, this->os, op); + 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; @@ -299,29 +237,115 @@ METHOD(sw_collector_history_t, extract_packages, bool, 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) + switch (op) { - goto end; - } - if (!this->db->add_sw_event(this->db, eid, sw_id, op != SW_OP_REMOVE ? - SWIMA_EVENT_ACTION_CREATION : SWIMA_EVENT_ACTION_DELETION)) - { - goto end; + 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_UPGRADE) + if (op != SW_OP_INSTALL) { - 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) + 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)) + SWIMA_EVENT_ACTION_DELETION)) { goto end; } @@ -346,150 +370,136 @@ end: METHOD(sw_collector_history_t, merge_installed_packages, bool, private_sw_collector_history_t *this) { - FILE *file; uint32_t sw_id, count = 0; - char line[BUF_LEN], *pos, *package, *version, *state, *name; - bool success = FALSE; - char cmd[] = "dpkg-query -W -f=\'${Package}\t${Version}\t${Status}\n\'"; + char package_arch[BUF_LEN]; + char *package, *arch, *version, *v1, *name, *n1; + bool installed, success = FALSE; + sw_collector_dpkg_t *dpkg; + enumerator_t *enumerator; DBG1(DBG_IMC, "Merging:"); - file = popen(cmd, "r"); - if (!file) + dpkg = sw_collector_dpkg_create(); + if (!dpkg) { - DBG1(DBG_IMC, "failed to run dpgk-query command"); return FALSE; } - while (TRUE) + enumerator = dpkg->create_sw_enumerator(dpkg); + while (enumerator->enumerate(enumerator, &package, &arch, &version)) { - if (!fgets(line, sizeof(line), file)) - { - break; - } + name = this->info->create_sw_id(this->info, package, version); + DBG3(DBG_IMC, " %s merged", name); - package = line; - pos = strchr(line, '\t'); - if (!pos) + sw_id = this->db->get_sw_id(this->db, name, NULL, NULL, NULL, + &installed); + if (sw_id) { - goto end; + 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; + } + } } - *pos = '\0'; - - version = ++pos; - pos = strchr(pos, '\t'); - if (!pos) + else { - goto end; + /* 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; + } + } } - *pos = '\0'; - state = ++pos; - pos = strchr(pos, '\n'); - if (!pos) + if (!sw_id) { - goto end; - } - *pos = '\0'; + /* Package name is stored with appended architecture */ + if (!streq(arch, "all")) + { + snprintf(package_arch, BUF_LEN, "%s:%s", package, arch); + package = package_arch; + } - if (!streq(state, "install ok installed")) - { - continue; - } - name = create_sw_id(this->tag_creator, this->os, package, version); - DBG3(DBG_IMC, " %s merged", name); + /* 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; + } - sw_id = this->db->set_sw_id(this->db, name, package, version, - this->source, TRUE, TRUE); - free(name); - if (!sw_id) - { - goto end; } + free(name); count++; } success = TRUE; - DBG1(DBG_IMC, " merged %u installed packages, %u registed in database", + + DBG1(DBG_IMC, " merged %u installed packages, %u registered in database", count, this->db->get_sw_id_count(this->db, SW_QUERY_INSTALLED)); end: - pclose(file); + enumerator->destroy(enumerator); + dpkg->destroy(dpkg); + return success; } METHOD(sw_collector_history_t, destroy, void, private_sw_collector_history_t *this) { - this->os_info->destroy(this->os_info); - free(this->os); - free(this->product); free(this); } /** * Described in header. */ -sw_collector_history_t *sw_collector_history_create(sw_collector_db_t *db, +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; - chunk_t os_name, os_version, os_arch; - os_type_t os_type; INIT(this, .public = { - .get_os = _get_os, .extract_timestamp = _extract_timestamp, .extract_packages = _extract_packages, .merge_installed_packages = _merge_installed_packages, .destroy = _destroy, }, - .db = db, .source = source, - .os_info = imc_os_info_create(), - .tag_creator = lib->settings->get_str(lib->settings, - "%s.tag_creator.regid", "strongswan.org", lib->ns), + .info = info, + .db = db, ); - os_type = this->os_info->get_type(this->os_info); - os_name = this->os_info->get_name(this->os_info); - os_arch = this->os_info->get_version(this->os_info); - - /* check if OS is supported */ - if (os_type != OS_TYPE_DEBIAN && os_type != OS_TYPE_UBUNTU) - { - DBG1(DBG_IMC, "%.*s OS not supported", os_name.len, os_name.ptr); - destroy(this); - return NULL; - } - - /* 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_history.h b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.h index d1a3b2130..857cc17d2 100644 --- a/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.h +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_history.h @@ -15,12 +15,14 @@ /** * @defgroup sw_collector_history_t sw_collector_history - * @{ @ingroup imc_swima + * @{ @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> @@ -45,14 +47,6 @@ 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 @@ -90,10 +84,12 @@ struct sw_collector_history_t { /** * 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_db_t *db, +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 new file mode 100644 index 000000000..0ac5f0dfb --- /dev/null +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.c @@ -0,0 +1,176 @@ +/* + * 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 *pos, *sw_id; + size_t len; + + /* Remove architecture from package name */ + pos = strchr(package, ':'); + len = pos ? (pos - package) : strlen(package); + + /* Build software identifier */ + if (asprintf(&sw_id, "%s__%s-%.*s%s%s", this->tag_creator, this->os, len, + 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 new file mode 100644 index 000000000..a54d788f4 --- /dev/null +++ b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_info.h @@ -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/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.c b/src/libimcv/plugins/imc_swima/sw_collector/sw_collector_rest_api.c index 15108a01c..6b9b7b96a 100644 --- 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 @@ -50,19 +50,19 @@ static json_object* create_rest_request(private_sw_collector_rest_api_t *this, { json_object *jrequest, *jarray, *jstring; char *name, *package, *version; - uint32_t i; + 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); + e = this->db->create_sw_enumerator(this->db, type, NULL); if (!e) { return NULL; } - while (e->enumerate(e, &name, &package, &version, &i)) + while (e->enumerate(e, &sw_id, &name, &package, &version, &i)) { jstring = json_object_new_string(name); json_object_array_add(jarray, jstring); 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 index e44ab6d06..ca45230e6 100644 --- 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 @@ -26,7 +26,7 @@ typedef struct sw_collector_rest_api_t sw_collector_rest_api_t; /** - * Software collector database object + * Software collector REST API object */ struct sw_collector_rest_api_t { |