Annotation of embedaddon/strongswan/src/sw-collector/sw_collector_history.c, revision 1.1.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
                     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>