File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / sw-collector / sw_collector_history.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 09:46:44 2020 UTC (4 years, 1 month ago) by misho
Branches: strongswan, MAIN
CVS tags: v5_9_2p0, v5_8_4p7, HEAD
Strongswan

    1: /*
    2:  * Copyright (C) 2017 Andreas Steffen
    3:  * HSR Hochschule fuer Technik Rapperswil
    4:  *
    5:  * This program is free software; you can redistribute it and/or modify it
    6:  * under the terms of the GNU General Public License as published by the
    7:  * Free Software Foundation; either version 2 of the License, or (at your
    8:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
    9:  *
   10:  * This program is distributed in the hope that it will be useful, but
   11:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   12:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   13:  * for more details.
   14:  */
   15: 
   16: #define _GNU_SOURCE
   17: #include <stdio.h>
   18: #include <time.h>
   19: 
   20: #include "sw_collector_history.h"
   21: #include "sw_collector_dpkg.h"
   22: 
   23: #include <swima/swima_event.h>
   24: #include <swid_gen/swid_gen_info.h>
   25: 
   26: typedef struct private_sw_collector_history_t private_sw_collector_history_t;
   27: 
   28: /**
   29:  * Private data of an sw_collector_history_t object.
   30:  */
   31: struct private_sw_collector_history_t {
   32: 
   33: 	/**
   34: 	 * Public members of sw_collector_history_state_t
   35: 	 */
   36: 	sw_collector_history_t public;
   37: 
   38: 	/**
   39: 	 * Software Event Source Number
   40: 	 */
   41: 	uint8_t source;
   42: 
   43: 	/**
   44: 	 * Reference to OS info object
   45: 	 */
   46: 	swid_gen_info_t *info;
   47: 
   48: 	/**
   49: 	 * Reference to collector database
   50: 	 */
   51: 	sw_collector_db_t *db;
   52: 
   53: };
   54: 
   55: /**
   56:  * Define auxiliary package_t list item object
   57:  */
   58: typedef struct package_t package_t;
   59: 
   60: struct package_t {
   61: 	char *package;
   62: 	char *version;
   63: 	char *old_version;
   64: 	char *sw_id;
   65: 	char *old_sw_id;
   66: };
   67: 
   68: /**
   69:  * Create package_t list item object
   70:  */
   71: static package_t* create_package(swid_gen_info_t *info, chunk_t package,
   72: 								 chunk_t version, chunk_t old_version)
   73: {
   74: 	package_t *this;
   75: 
   76: 	INIT(this,
   77: 		.package = strndup(package.ptr, package.len),
   78: 		.version = strndup(version.ptr, version.len),
   79: 		.old_version = strndup(old_version.ptr, old_version.len),
   80: 	)
   81: 
   82: 	this->sw_id = info->create_sw_id(info, this->package, this->version);
   83: 	if (old_version.len)
   84: 	{
   85: 		this->old_sw_id = info->create_sw_id(info, this->package,
   86: 												   this->old_version);
   87: 	}
   88: 
   89: 	return this;
   90: }
   91: 
   92: /**
   93:  * Free package_t list item object
   94:  */
   95: static void free_package(package_t *this)
   96: {
   97: 	if (this)
   98: 	{
   99: 		free(this->package);
  100: 		free(this->version);
  101: 		free(this->old_version);
  102: 		free(this->sw_id);
  103: 		free(this->old_sw_id);
  104: 		free(this);
  105: 	}
  106: }
  107: 
  108: /**
  109:  * Extract and parse a single package item
  110:  */
  111: static package_t* extract_package(chunk_t item, swid_gen_info_t *info,
  112: 												sw_collector_history_op_t op)
  113: {
  114: 	chunk_t package, package_stripped, version, old_version;
  115: 	package_t *p;
  116: 
  117: 	/* extract package name */
  118: 	if (!extract_token(&package, ' ', &item))
  119: 	{
  120: 		fprintf(stderr, "version not found.\n");
  121: 		return NULL;
  122: 	}
  123: 	item = chunk_skip(item, 1);
  124: 
  125: 	/* strip architecture suffix if present */
  126: 	if (extract_token(&package_stripped, ':', &package))
  127: 	{
  128: 		package = package_stripped;
  129: 	}
  130: 
  131: 	/* extract versions */
  132: 	version = old_version = chunk_empty;
  133: 
  134: 	if (item.len > 0)
  135: 	{
  136: 		if (extract_token(&version, ',', &item))
  137: 		{
  138: 			eat_whitespace(&item);
  139: 			if (!match("automatic", &item))
  140: 			{
  141: 				old_version = version;
  142: 				version = item;
  143: 			}
  144: 		}
  145: 		else
  146: 		{
  147: 			version = item;
  148: 		}
  149: 	}
  150: 	p = create_package(info, package, version, old_version);
  151: 
  152: 	/* generate log entry */
  153: 	if (op == SW_OP_UPGRADE)
  154: 	{
  155: 		DBG2(DBG_IMC, "    %s (%s, %s)", p->package, p->old_version, p->version);
  156: 		DBG2(DBG_IMC, "      +%s", p->sw_id);
  157: 		DBG2(DBG_IMC, "      -%s", p->old_sw_id);
  158: 	}
  159: 	else
  160: 	{
  161: 		DBG2(DBG_IMC, "    %s (%s)", p->package, p->version);
  162: 		DBG2(DBG_IMC, "      %s%s", (op == SW_OP_INSTALL) ? "+" : "-", p->sw_id);
  163: 	}
  164: 
  165: 	return p;
  166: }
  167: 
  168: METHOD(sw_collector_history_t, extract_timestamp, bool,
  169: 	private_sw_collector_history_t *this, chunk_t args, char *buf)
  170: {
  171: 	struct tm loc, utc;
  172: 	chunk_t t1, t2;
  173: 	time_t t;
  174: 
  175: 	/* Break down local time with format t1 = yyyy-mm-dd and t2 = hh:mm:ss */
  176: 	if (!eat_whitespace(&args) || !extract_token(&t1, ' ', &args) ||
  177: 		!eat_whitespace(&args) || t1.len != 10 || args.len != 8)
  178: 	{
  179: 		DBG1(DBG_IMC, "unable to parse start-date");
  180: 		return FALSE;
  181: 	}
  182: 	t2 = args;
  183: 
  184: 	if (sscanf(t1.ptr, "%4d-%2d-%2d",
  185: 						&loc.tm_year, &loc.tm_mon, &loc.tm_mday) != 3)
  186: 	{
  187: 		DBG1(DBG_IMC, "unable to parse date format yyyy-mm-dd");
  188: 		return FALSE;
  189: 	}
  190: 	loc.tm_year -= 1900;
  191: 	loc.tm_mon  -= 1;
  192: 	loc.tm_isdst = -1;
  193: 
  194: 	if (sscanf(t2.ptr, "%2d:%2d:%2d",
  195: 						&loc.tm_hour, &loc.tm_min, &loc.tm_sec) != 3)
  196: 	{
  197: 		DBG1(DBG_IMC, "unable to parse time format hh:mm:ss");
  198: 		return FALSE;
  199: 	}
  200: 
  201: 	/* Convert from local time to UTC */
  202: 	t = mktime(&loc);
  203: 	gmtime_r(&t, &utc);
  204: 	utc.tm_year += 1900;
  205: 	utc.tm_mon  += 1;
  206: 
  207: 	/* Form timestamp according to RFC 3339 (20 characters) */
  208: 	snprintf(buf, 21, "%4d-%02d-%02dT%02d:%02d:%02dZ",
  209: 			 utc.tm_year, utc.tm_mon, utc.tm_mday,
  210: 			 utc.tm_hour, utc.tm_min, utc.tm_sec);
  211: 
  212: 	return TRUE;
  213: }
  214: 
  215: METHOD(sw_collector_history_t, extract_packages, bool,
  216: 	private_sw_collector_history_t *this, chunk_t args, uint32_t eid,
  217: 	sw_collector_history_op_t op)
  218: {
  219: 	bool success = FALSE;
  220: 	package_t *p = NULL;
  221: 	chunk_t item;
  222: 
  223: 	eat_whitespace(&args);
  224: 
  225: 	while (extract_token(&item, ')', &args))
  226: 	{
  227: 		char *del_sw_id = NULL, *del_version = NULL;
  228: 		char *nx, *px, *vx, *v1;
  229: 		bool installed;
  230: 		u_int sw_idx, ix;
  231: 		uint32_t sw_id, sw_id_epoch_less = 0;
  232: 		enumerator_t *e;
  233: 
  234: 		p = extract_package(item, this->info, op);
  235: 		if (!p)
  236: 		{
  237: 			goto end;
  238: 		}
  239: 
  240: 		/* packages without version information cannot be handled */
  241: 		if (strlen(p->version) == 0)
  242: 		{
  243: 			free_package(p);
  244: 			continue;
  245: 		}
  246: 
  247: 		switch (op)
  248: 		{
  249: 			case SW_OP_REMOVE:
  250: 				/* prepare subsequent deletion sw event */
  251: 				del_sw_id = p->sw_id;
  252: 				del_version = p->version;
  253: 				break;
  254: 			case SW_OP_UPGRADE:
  255: 				/* prepare subsequent deletion sw event */
  256: 				del_sw_id = p->old_sw_id;
  257: 				del_version = p->old_version;
  258: 				/* fall through to next case */
  259: 			case SW_OP_INSTALL:
  260: 				sw_id = this->db->get_sw_id(this->db, p->sw_id, NULL, NULL,
  261: 											NULL, &installed);
  262: 				if (sw_id)
  263: 				{
  264: 					/* sw identifier exists - update state to 'installed' */
  265: 					if (installed)
  266: 					{
  267: 						/* this case should not occur */
  268: 						DBG1(DBG_IMC, "  warning:  sw_id %d is already "
  269: 									  "installed", sw_id);
  270: 					}
  271: 					else if (!this->db->update_sw_id(this->db, sw_id, NULL,
  272: 													 NULL, TRUE))
  273: 					{
  274: 						goto end;
  275: 					}
  276: 				}
  277: 				else
  278: 				{
  279: 					/* new sw identifier - create with state 'installed' */
  280: 					sw_id = this->db->set_sw_id(this->db, p->sw_id, p->package,
  281: 												p->version,	this->source, TRUE);
  282: 					if (!sw_id)
  283: 					{
  284: 						goto end;
  285: 					}
  286: 				}
  287: 
  288: 				/* add creation sw event with current eid */
  289: 				if (!this->db->add_sw_event(this->db, eid, sw_id,
  290: 									SWIMA_EVENT_ACTION_CREATION))
  291: 				{
  292: 					goto end;
  293: 				}
  294: 				break;
  295: 		}
  296: 
  297: 		if (op != SW_OP_INSTALL)
  298: 		{
  299: 			sw_id = 0;
  300: 
  301: 			/* look for existing installed package versions */
  302: 			e = this->db->create_sw_enumerator(this->db, SW_QUERY_INSTALLED,
  303: 											   p->package);
  304: 			if (!e)
  305: 			{
  306: 				goto end;
  307: 			}
  308: 
  309: 			while (e->enumerate(e, &sw_idx, &nx, &px, &vx, &ix))
  310: 			{
  311: 				if (streq(vx, del_version))
  312: 				{
  313: 					/* full match with epoch */
  314: 					sw_id = sw_idx;
  315: 					break;
  316: 				}
  317: 				v1 = strchr(vx, ':');
  318: 				if (v1 && streq(++v1, del_version))
  319: 				{
  320: 					/* match with stripped epoch */
  321: 					sw_id_epoch_less = sw_idx;
  322: 				}
  323: 			}
  324: 			e->destroy(e);
  325: 
  326: 			if (!sw_id && sw_id_epoch_less)
  327: 			{
  328: 				/* no full match - fall back to epoch-less match */
  329: 				sw_id = sw_id_epoch_less;
  330: 			}
  331: 			if (sw_id)
  332: 			{
  333: 				/* sw identifier exists - update state to 'removed' */
  334: 				if (!this->db->update_sw_id(this->db, sw_id, NULL, NULL, FALSE))
  335: 				{
  336: 					goto end;
  337: 				}
  338: 			}
  339: 			else
  340: 			{
  341: 				/* new sw identifier - create with state 'removed' */
  342: 				sw_id = this->db->set_sw_id(this->db, del_sw_id, p->package,
  343: 									del_version, this->source, FALSE);
  344: 
  345: 				/* add creation sw event with eid = 1 */
  346: 				if (!sw_id || !this->db->add_sw_event(this->db, 1, sw_id,
  347: 											SWIMA_EVENT_ACTION_CREATION))
  348: 				{
  349: 					goto end;
  350: 				}
  351: 			}
  352: 
  353: 			/* add creation sw event with current eid */
  354: 			if (!this->db->add_sw_event(this->db, eid, sw_id,
  355: 								SWIMA_EVENT_ACTION_DELETION))
  356: 			{
  357: 				goto end;
  358: 			}
  359: 		}
  360: 		free_package(p);
  361: 
  362: 		if (args.len < 2)
  363: 		{
  364: 			break;
  365: 		}
  366: 		args = chunk_skip(args, 2);
  367: 	}
  368: 	p = NULL;
  369: 	success = TRUE;
  370: 
  371: end:
  372: 	free_package(p);
  373: 
  374: 	return success;
  375: }
  376: 
  377: METHOD(sw_collector_history_t, merge_installed_packages, bool,
  378: 	private_sw_collector_history_t *this)
  379: {
  380: 	uint32_t sw_id, count = 0;
  381: 	char *package, *arch, *version, *v1, *name, *n1;
  382: 	bool installed, success = FALSE;
  383: 	sw_collector_dpkg_t *dpkg;
  384: 	enumerator_t *enumerator;
  385: 
  386: 	DBG1(DBG_IMC, "Merging:");
  387: 
  388: 	dpkg = sw_collector_dpkg_create();
  389: 	if (!dpkg)
  390: 	{
  391: 		return FALSE;
  392: 	}
  393: 
  394: 	enumerator = dpkg->create_sw_enumerator(dpkg);
  395: 	while (enumerator->enumerate(enumerator, &package, &arch, &version))
  396: 	{
  397: 		name = this->info->create_sw_id(this->info, package, version);
  398: 		DBG3(DBG_IMC, "  %s merged", name);
  399: 
  400: 		sw_id = this->db->get_sw_id(this->db, name, NULL, NULL, NULL,
  401: 									&installed);
  402: 		if (sw_id)
  403: 		{
  404: 			if (!installed)
  405: 			{
  406: 				DBG1(DBG_IMC, "  warning: existing sw_id %u"
  407: 							  " is not installed", sw_id);
  408: 
  409: 				if (!this->db->update_sw_id(this->db, sw_id, name, version,
  410: 											TRUE))
  411: 				{
  412: 					free(name);
  413: 					goto end;
  414: 				}
  415: 			}
  416: 		}
  417: 		else
  418: 		{
  419: 			/* check for a Debian epoch number */
  420: 			v1 = strchr(version, ':');
  421: 			if (v1)
  422: 			{
  423: 				/* check for existing and installed epoch-less version */
  424: 				n1 = this->info->create_sw_id(this->info, package, ++v1);
  425: 				sw_id = this->db->get_sw_id(this->db, n1, NULL, NULL, NULL,
  426: 											&installed);
  427: 				free(n1);
  428: 
  429: 				if (sw_id && installed)
  430: 				{
  431: 					/* add epoch to existing version */
  432: 					if (!this->db->update_sw_id(this->db, sw_id, name, version,
  433: 												installed))
  434: 					{
  435: 						free(name);
  436: 						goto end;
  437: 					}
  438: 				}
  439: 				else
  440: 				{
  441: 					sw_id = 0;
  442: 				}
  443: 			}
  444: 		}
  445: 
  446: 		if (!sw_id)
  447: 		{
  448: 			/* new sw identifier - create with state 'installed' */
  449: 			sw_id = this->db->set_sw_id(this->db, name, package, version,
  450: 										this->source, TRUE);
  451: 
  452: 			/* add creation sw event with eid = 1 */
  453: 			if (!sw_id || !this->db->add_sw_event(this->db, 1, sw_id,
  454: 										SWIMA_EVENT_ACTION_CREATION))
  455: 			{
  456: 				free(name);
  457: 				goto end;
  458: 			}
  459: 
  460: 		}
  461: 		free(name);
  462: 		count++;
  463: 	}
  464: 	success = TRUE;
  465: 
  466: 	DBG1(DBG_IMC, "  merged %u installed packages, %u registered in database",
  467: 		 count, this->db->get_sw_id_count(this->db, SW_QUERY_INSTALLED));
  468: 
  469: end:
  470: 	enumerator->destroy(enumerator);
  471: 	dpkg->destroy(dpkg);
  472: 
  473: 	return success;
  474: }
  475: 
  476: METHOD(sw_collector_history_t, destroy, void,
  477: 	private_sw_collector_history_t *this)
  478: {
  479: 	this->info->destroy(this->info);
  480: 	free(this);
  481: }
  482: 
  483: /**
  484:  * Described in header.
  485:  */
  486: sw_collector_history_t *sw_collector_history_create(sw_collector_db_t *db,
  487: 													uint8_t source)
  488: {
  489: 	private_sw_collector_history_t *this;
  490: 	swid_gen_info_t *info;
  491: 	os_type_t os_type;
  492: 	char *os;
  493: 
  494: 	info = swid_gen_info_create();
  495: 
  496: 	/* check if OS supports apg/dpkg history logs */
  497: 	info->get_os(info, &os);
  498: 	os_type = info->get_os_type(info);
  499: 	if (os_type != 	OS_TYPE_DEBIAN && os_type != OS_TYPE_UBUNTU)
  500: 	{
  501: 		DBG1(DBG_IMC, "%.*s not supported", os);
  502: 		info->destroy(info);
  503: 		return NULL;
  504: 	}
  505: 
  506: 	INIT(this,
  507: 		.public = {
  508: 			.extract_timestamp = _extract_timestamp,
  509: 			.extract_packages = _extract_packages,
  510: 			.merge_installed_packages = _merge_installed_packages,
  511: 			.destroy = _destroy,
  512: 		},
  513: 		.source = source,
  514: 		.info = info,
  515: 		.db = db,
  516: 	);
  517: 
  518: 	return &this->public;
  519: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>