Annotation of embedaddon/strongswan/src/libimcv/swima/swima_collector.c, revision 1.1
1.1 ! misho 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 /* for asprintf() */
! 17:
! 18: #include "swima_collector.h"
! 19:
! 20: #include <swid_gen/swid_gen.h>
! 21:
! 22: #include <collections/linked_list.h>
! 23: #include <utils/debug.h>
! 24:
! 25: #include <stdio.h>
! 26: #include <fcntl.h>
! 27: #include <unistd.h>
! 28: #include <sys/stat.h>
! 29: #include <libgen.h>
! 30: #include <errno.h>
! 31:
! 32: #define SOURCE_ID_GENERATOR 1
! 33: #define SOURCE_ID_COLLECTOR 2
! 34:
! 35: #ifndef SWID_DIRECTORY
! 36: #define SWID_DIRECTORY NULL
! 37: #endif
! 38:
! 39: /**
! 40: * Directories to be skipped by collector
! 41: */
! 42: static const char* skip_directories[] = {
! 43: "/usr/share/doc",
! 44: "/usr/share/help",
! 45: "/usr/share/icons",
! 46: "/usr/share/gnome/help"
! 47: };
! 48:
! 49: typedef struct private_swima_collector_t private_swima_collector_t;
! 50:
! 51: /**
! 52: * Private data of a swima_collector_t object.
! 53: *
! 54: */
! 55: struct private_swima_collector_t {
! 56:
! 57: /**
! 58: * Public swima_collector_t interface.
! 59: */
! 60: swima_collector_t public;
! 61:
! 62: /**
! 63: * Collect Software Identifiers only
! 64: */
! 65: bool sw_id_only;
! 66:
! 67: /**
! 68: * Software Collector Database [if it exists]
! 69: */
! 70: database_t *db;
! 71:
! 72: /**
! 73: * List of Software [Identifier] records
! 74: */
! 75: swima_inventory_t *inventory;
! 76:
! 77: /**
! 78: * List of Software [Identifier] events
! 79: */
! 80: swima_events_t *events;
! 81:
! 82: };
! 83:
! 84: /**
! 85: * Extract Software Identifier from SWID tag
! 86: */
! 87: static status_t extract_sw_id(chunk_t swid_tag, chunk_t *sw_id)
! 88: {
! 89: char *pos, *tag, *tagid, *regid;
! 90: size_t len, tagid_len, regid_len;
! 91: status_t status = NOT_FOUND;
! 92:
! 93: /* Copy at most 1023 bytes of the SWID tag and null-terminate it */
! 94: len = min(1023, swid_tag.len);
! 95: pos = tag = strndup(swid_tag.ptr, len);
! 96:
! 97: tagid= strstr(pos, "tagId=\"");
! 98: if (tagid == NULL)
! 99: {
! 100: goto end;
! 101: }
! 102: tagid += 7;
! 103: len -= tagid - pos - 7;
! 104:
! 105: pos = strchr(tagid, '"');
! 106: if (pos == NULL)
! 107: {
! 108: goto end;
! 109: }
! 110: tagid_len = pos - tagid;
! 111:
! 112: regid= strstr(pos, "regid=\"");
! 113: if (regid == NULL)
! 114: {
! 115: goto end;
! 116: }
! 117: regid += 7;
! 118: len -= regid - pos - 7;
! 119:
! 120: pos = strchr(regid, '"');
! 121: if (pos == NULL)
! 122: {
! 123: goto end;
! 124: }
! 125: regid_len = pos - regid;
! 126:
! 127: *sw_id = chunk_cat("ccc", chunk_create(regid, regid_len),
! 128: chunk_from_chars('_','_'),
! 129: chunk_create(tagid, tagid_len));
! 130: status = SUCCESS;
! 131: end:
! 132: free(tag);
! 133:
! 134: return status;
! 135: }
! 136:
! 137: static status_t retrieve_inventory(private_swima_collector_t *this,
! 138: swima_inventory_t *targets)
! 139: {
! 140: char *name;
! 141: uint32_t record_id, source;
! 142: swima_record_t *sw_record;
! 143: chunk_t sw_id;
! 144: enumerator_t *e;
! 145:
! 146: /* Retrieve complete software identifier inventory */
! 147: e = this->db->query(this->db,
! 148: "SELECT id, name, source FROM sw_identifiers WHERE installed = 1 "
! 149: "ORDER BY name ASC", DB_UINT, DB_TEXT, DB_UINT);
! 150: if (!e)
! 151: {
! 152: DBG1(DBG_IMC, "database query for installed sw_identifiers failed");
! 153: return FAILED;
! 154: }
! 155: while (e->enumerate(e, &record_id, &name, &source))
! 156: {
! 157: sw_id = chunk_from_str(name);
! 158: sw_record = swima_record_create(record_id, sw_id, chunk_empty);
! 159: sw_record->set_source_id(sw_record, source);
! 160: this->inventory->add(this->inventory, sw_record);
! 161: }
! 162: e->destroy(e);
! 163:
! 164: return SUCCESS;
! 165: }
! 166:
! 167: static status_t retrieve_events(private_swima_collector_t *this,
! 168: swima_inventory_t *targets)
! 169: {
! 170: enumerator_t *e;
! 171: char *name, *timestamp;
! 172: uint32_t record_id, source, action, eid, earliest_eid;
! 173: chunk_t sw_id, ev_ts;
! 174: swima_record_t *sw_record;
! 175: swima_event_t *sw_event;
! 176:
! 177: earliest_eid = targets->get_eid(targets, NULL);
! 178:
! 179: /* Retrieve complete software identifier inventory */
! 180: e = this->db->query(this->db,
! 181: "SELECT e.id, e.timestamp, i.id, i.name, i.source, s.action "
! 182: "FROM sw_events as s JOIN events AS e ON s.eid = e.id "
! 183: "JOIN sw_identifiers as i ON s.sw_id = i.id WHERE s.eid >= ?"
! 184: "ORDER BY s.eid, i.name, s.action ASC", DB_UINT, earliest_eid,
! 185: DB_UINT, DB_TEXT, DB_UINT, DB_TEXT, DB_UINT, DB_UINT);
! 186: if (!e)
! 187: {
! 188: DBG1(DBG_IMC, "database query for sw_events failed");
! 189: return FAILED;
! 190: }
! 191: while (e->enumerate(e, &eid, ×tamp, &record_id, &name, &source, &action))
! 192: {
! 193: sw_id = chunk_from_str(name);
! 194: ev_ts = chunk_from_str(timestamp);
! 195: sw_record = swima_record_create(record_id, sw_id, chunk_empty);
! 196: sw_record->set_source_id(sw_record, source);
! 197: sw_event = swima_event_create(eid, ev_ts, action, sw_record);
! 198: this->events->add(this->events, sw_event);
! 199: }
! 200: e->destroy(e);
! 201:
! 202: return SUCCESS;
! 203: }
! 204:
! 205: static status_t generate_tags(private_swima_collector_t *this,
! 206: swima_inventory_t *targets, bool pretty, bool full)
! 207: {
! 208: swid_gen_t *swid_gen;
! 209: swima_record_t *target, *sw_record;
! 210: enumerator_t *enumerator;
! 211: status_t status = SUCCESS;
! 212:
! 213: swid_gen = swid_gen_create();
! 214:
! 215: if (targets->get_count(targets) == 0)
! 216: {
! 217: chunk_t out, sw_id, swid_tag = chunk_empty;
! 218:
! 219: DBG2(DBG_IMC, "SWID tag%s generation by package manager",
! 220: this->sw_id_only ? " ID" : "");
! 221:
! 222: enumerator = swid_gen->create_tag_enumerator(swid_gen, this->sw_id_only,
! 223: full, pretty);
! 224: if (enumerator)
! 225: {
! 226: while (enumerator->enumerate(enumerator, &out))
! 227: {
! 228: if (this->sw_id_only)
! 229: {
! 230: sw_id = out;
! 231: }
! 232: else
! 233: {
! 234: swid_tag = out;
! 235: status = extract_sw_id(swid_tag, &sw_id);
! 236: if (status != SUCCESS)
! 237: {
! 238: DBG1(DBG_IMC, "software id could not be extracted "
! 239: "from tag");
! 240: chunk_free(&swid_tag);
! 241: break;
! 242: }
! 243: }
! 244: sw_record = swima_record_create(0, sw_id, chunk_empty);
! 245: sw_record->set_source_id(sw_record, SOURCE_ID_GENERATOR);
! 246: if (!this->sw_id_only)
! 247: {
! 248: sw_record->set_record(sw_record, swid_tag);
! 249: chunk_free(&swid_tag);
! 250: }
! 251: this->inventory->add(this->inventory, sw_record);
! 252: chunk_free(&sw_id);
! 253: }
! 254: enumerator->destroy(enumerator);
! 255: }
! 256: else
! 257: {
! 258: status = NOT_SUPPORTED;
! 259: }
! 260: }
! 261: else if (!this->sw_id_only)
! 262: {
! 263: DBG2(DBG_IMC, "targeted SWID tag generation");
! 264:
! 265: enumerator = targets->create_enumerator(targets);
! 266: while (enumerator->enumerate(enumerator, &target))
! 267: {
! 268: swima_record_t *sw_record;
! 269: char *tag = NULL, *name, *package, *version;
! 270: u_int installed;
! 271: chunk_t sw_id;
! 272: enumerator_t *e;
! 273:
! 274: sw_id = target->get_sw_id(target, NULL);
! 275: name = strndup(sw_id.ptr, sw_id.len);
! 276:
! 277: if (this->db)
! 278: {
! 279: e = this->db->query(this->db,
! 280: "SELECT package, version, installed "
! 281: "FROM sw_identifiers WHERE name = ?", DB_TEXT, name,
! 282: DB_TEXT, DB_TEXT, DB_UINT);
! 283: if (!e)
! 284: {
! 285: DBG1(DBG_IMC, "database query for sw_identifiers failed");
! 286: status = FAILED;
! 287: free(name);
! 288: break;
! 289: }
! 290: if (e->enumerate(e, &package, &version, &installed))
! 291: {
! 292: tag = swid_gen->generate_tag(swid_gen, name, package,
! 293: version, full && installed, pretty);
! 294: }
! 295: e->destroy(e);
! 296: }
! 297: else
! 298: {
! 299: tag = swid_gen->generate_tag(swid_gen, name, NULL, NULL,
! 300: full, pretty);
! 301: }
! 302: free(name);
! 303:
! 304: if (tag)
! 305: {
! 306: DBG2(DBG_IMC, " %.*s", sw_id.len, sw_id.ptr);
! 307: sw_record = swima_record_create(0, sw_id, chunk_empty);
! 308: sw_record->set_source_id(sw_record, SOURCE_ID_GENERATOR);
! 309: sw_record->set_record(sw_record, chunk_from_str(tag));
! 310: this->inventory->add(this->inventory, sw_record);
! 311: free(tag);
! 312: }
! 313: }
! 314: enumerator->destroy(enumerator);
! 315: }
! 316: swid_gen->destroy(swid_gen);
! 317:
! 318: return status;
! 319: }
! 320:
! 321: static bool collect_tags(private_swima_collector_t *this, char *pathname,
! 322: swima_inventory_t *targets, bool is_swidtag_dir)
! 323: {
! 324: char *rel_name, *abs_name, *suffix, *pos, *uri;
! 325: chunk_t *swid_tag, sw_id, sw_locator;
! 326: swima_record_t *sw_record;
! 327: struct stat st;
! 328: bool success = FALSE, skip, is_new_swidtag_dir;
! 329: enumerator_t *enumerator;
! 330: int i;
! 331:
! 332: if (!pathname)
! 333: {
! 334: return TRUE;
! 335: }
! 336:
! 337: enumerator = enumerator_create_directory(pathname);
! 338: if (!enumerator)
! 339: {
! 340: DBG1(DBG_IMC, "directory '%s' can not be opened, %s",
! 341: pathname, strerror(errno));
! 342: return FALSE;
! 343: }
! 344:
! 345: while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
! 346: {
! 347: if (S_ISDIR(st.st_mode))
! 348: {
! 349: skip = FALSE;
! 350:
! 351: for (i = 0; i < countof(skip_directories); i++)
! 352: {
! 353: if (streq(abs_name, skip_directories[i]))
! 354: {
! 355: skip = TRUE;
! 356: break;
! 357: }
! 358: }
! 359:
! 360: if (skip)
! 361: {
! 362: continue;
! 363: }
! 364:
! 365: is_new_swidtag_dir = streq(rel_name, "swidtag");
! 366: if (is_new_swidtag_dir)
! 367: {
! 368: DBG2(DBG_IMC, "entering %s", pathname);
! 369: }
! 370: if (!collect_tags(this, abs_name, targets, is_swidtag_dir ||
! 371: is_new_swidtag_dir))
! 372: {
! 373: goto end;
! 374: }
! 375: if (is_new_swidtag_dir)
! 376: {
! 377: DBG2(DBG_IMC, "leaving %s", pathname);
! 378: }
! 379: }
! 380:
! 381: if (!is_swidtag_dir)
! 382: {
! 383: continue;
! 384: }
! 385:
! 386: /* found a swidtag file? */
! 387: suffix = strstr(rel_name, ".swidtag");
! 388: if (!suffix)
! 389: {
! 390: continue;
! 391: }
! 392:
! 393: /* load the swidtag file */
! 394: swid_tag = chunk_map(abs_name, FALSE);
! 395: if (!swid_tag)
! 396: {
! 397: DBG1(DBG_IMC, " opening '%s' failed: %s", abs_name,
! 398: strerror(errno));
! 399: goto end;
! 400: }
! 401:
! 402: /* extract software identity from SWID tag */
! 403: if (extract_sw_id(*swid_tag, &sw_id) != SUCCESS)
! 404: {
! 405: DBG1(DBG_IMC, "software id could not be extracted from SWID tag");
! 406: chunk_unmap(swid_tag);
! 407: goto end;
! 408: }
! 409:
! 410: /* In case of a targeted request */
! 411: if (targets->get_count(targets))
! 412: {
! 413: enumerator_t *target_enumerator;
! 414: swima_record_t *target;
! 415: bool match = FALSE;
! 416:
! 417: target_enumerator = targets->create_enumerator(targets);
! 418: while (target_enumerator->enumerate(target_enumerator, &target))
! 419: {
! 420: if (chunk_equals(target->get_sw_id(target, NULL), sw_id))
! 421: {
! 422: DBG2(DBG_IMC, " %.*s", sw_id.len, sw_id.ptr);
! 423: match = TRUE;
! 424: break;
! 425: }
! 426: }
! 427: target_enumerator->destroy(target_enumerator);
! 428:
! 429: if (!match)
! 430: {
! 431: chunk_unmap(swid_tag);
! 432: chunk_free(&sw_id);
! 433: continue;
! 434: }
! 435: }
! 436: DBG2(DBG_IMC, " %s", rel_name);
! 437:
! 438: sw_locator = chunk_empty;
! 439: pos = strstr(pathname, "/swidtag");
! 440: if (pos &&
! 441: asprintf(&uri, "file://%.*s", (int)(pos - pathname), pathname) > 0)
! 442: {
! 443: sw_locator = chunk_from_str(uri);
! 444: }
! 445: sw_record = swima_record_create(0, sw_id, sw_locator);
! 446: sw_record->set_source_id(sw_record, SOURCE_ID_COLLECTOR);
! 447: if (!this->sw_id_only)
! 448: {
! 449: sw_record->set_record(sw_record, *swid_tag);
! 450: }
! 451: this->inventory->add(this->inventory, sw_record);
! 452:
! 453: chunk_unmap(swid_tag);
! 454: chunk_free(&sw_id);
! 455: chunk_free(&sw_locator);
! 456: }
! 457: success = TRUE;
! 458:
! 459: end:
! 460: enumerator->destroy(enumerator);
! 461:
! 462: return success;
! 463: }
! 464:
! 465: METHOD(swima_collector_t, collect_inventory, swima_inventory_t*,
! 466: private_swima_collector_t *this, bool sw_id_only, swima_inventory_t *targets)
! 467: {
! 468: bool pretty, full;
! 469: char *directory;
! 470: status_t status;
! 471:
! 472: directory = lib->settings->get_str(lib->settings,
! 473: "%s.plugins.imc-swima.swid_directory",
! 474: SWID_DIRECTORY, lib->ns);
! 475: pretty = lib->settings->get_bool(lib->settings,
! 476: "%s.plugins.imc-swima.swid_pretty",
! 477: FALSE, lib->ns);
! 478: full = lib->settings->get_bool(lib->settings,
! 479: "%s.plugins.imc-swima.swid_full",
! 480: FALSE, lib->ns);
! 481:
! 482: /**
! 483: * Re-initialize collector
! 484: */
! 485: this->sw_id_only = sw_id_only;
! 486: this->inventory->clear(this->inventory);
! 487:
! 488: /**
! 489: * Source 1: Tags are generated by a package manager
! 490: */
! 491: if (sw_id_only && this->db)
! 492: {
! 493: status = retrieve_inventory(this, targets);
! 494: }
! 495: else
! 496: {
! 497: status = generate_tags(this, targets, pretty, full);
! 498: }
! 499:
! 500: /**
! 501: * Source 2: Collect swidtag files by iteratively entering all
! 502: * directories in the tree under the "directory" path.
! 503: */
! 504: DBG2(DBG_IMC, "SWID tag%s collection", sw_id_only ? " ID" : "");
! 505: collect_tags(this, directory, targets, FALSE);
! 506:
! 507: return status == SUCCESS ? this->inventory : NULL;
! 508: }
! 509:
! 510: METHOD(swima_collector_t, collect_events, swima_events_t*,
! 511: private_swima_collector_t *this, bool sw_id_only, swima_inventory_t *targets)
! 512: {
! 513: if (!sw_id_only || !this->db)
! 514: {
! 515: return NULL;
! 516: }
! 517:
! 518: /**
! 519: * Re-initialize collector
! 520: */
! 521: this->sw_id_only = sw_id_only;
! 522: this->events->clear(this->events);
! 523:
! 524: return retrieve_events(this, targets) == SUCCESS ? this->events : NULL;
! 525: }
! 526:
! 527: METHOD(swima_collector_t, destroy, void,
! 528: private_swima_collector_t *this)
! 529: {
! 530: DESTROY_IF(this->db);
! 531: this->inventory->destroy(this->inventory);
! 532: this->events->destroy(this->events);
! 533: free(this);
! 534: }
! 535:
! 536: /**
! 537: * See header
! 538: */
! 539: swima_collector_t *swima_collector_create(void)
! 540: {
! 541: private_swima_collector_t *this;
! 542: char *database;
! 543: uint32_t last_eid = 1, eid_epoch = 0x11223344;
! 544:
! 545: INIT(this,
! 546: .public = {
! 547: .collect_inventory = _collect_inventory,
! 548: .collect_events = _collect_events,
! 549: .destroy = _destroy,
! 550: },
! 551: .inventory = swima_inventory_create(),
! 552: .events = swima_events_create(),
! 553: );
! 554:
! 555: database = lib->settings->get_str(lib->settings,
! 556: "%s.plugins.imc-swima.swid_database", NULL, lib->ns);
! 557:
! 558: /* If we have an URI, try to connect to sw_collector database */
! 559: if (database)
! 560: {
! 561: database_t *db = lib->db->create(lib->db, database);
! 562:
! 563: if (db)
! 564: {
! 565: enumerator_t *e;
! 566:
! 567: /* Get last event ID and corresponding epoch */
! 568: e = db->query(db,
! 569: "SELECT id, epoch FROM events ORDER BY timestamp DESC",
! 570: DB_UINT, DB_UINT);
! 571: if (!e || !e->enumerate(e, &last_eid, &eid_epoch))
! 572: {
! 573: DBG1(DBG_IMC, "database query for last event failed");
! 574: DESTROY_IF(e);
! 575: db->destroy(db);
! 576: }
! 577: else
! 578: {
! 579: /* The query worked, attach collector database permanently */
! 580: e->destroy(e);
! 581: this->db = db;
! 582: }
! 583: }
! 584: else
! 585: {
! 586: DBG1(DBG_IMC, "opening sw-collector database URI '%s' failed",
! 587: database);
! 588: }
! 589: }
! 590: if (!this->db)
! 591: {
! 592: /* Set the event ID epoch and last event ID manually */
! 593: eid_epoch = lib->settings->get_int(lib->settings,
! 594: "%s.plugins.imc-swima.eid_epoch",
! 595: eid_epoch, lib->ns);
! 596: }
! 597: this->inventory->set_eid(this->inventory, last_eid, eid_epoch);
! 598: this->events->set_eid(this->events, last_eid, eid_epoch);
! 599:
! 600: return &this->public;
! 601: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>