Skip to content
Snippets Groups Projects
main.c 9.96 KiB
Newer Older
Michal Privoznik's avatar
Michal Privoznik committed
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <getopt.h>
#include <osinfo/osinfo.h>


#define ERROR(...) \
do { \
    fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \
    fprintf(stderr, __VA_ARGS__); \
    fprintf(stderr, "\n"); \
} while (0)

#define NULLSTR(s) \
    ((s) ? (s) : "")


typedef struct _Control Control;
struct _Control {
    const char *db_path;
    const char *os_id;
    const char *detect_path;
    bool list_os_ids;
};


static void
print_libosinfo_version(void)
{
    printf("libosinfo version: %d.%d.%d\n",
           OSINFO_MAJOR_VERSION,
           OSINFO_MINOR_VERSION,
           OSINFO_MICRO_VERSION);
}


static void
print_help(const char *progname)
{
    printf("\n"
           "%s [options] [<ID>]\n"
           "\n"
           "  options:\n"
           "    -v | --version          print libosinfo version and exit\n"
           "    -h | --help             print this help and exit\n"
           "    -p | --db-path <PATH>   where to load libosinfo DB from\n"
           "    -i | --os-id <ID>       OS identifier (short or long)\n"
           "    -l | --list             list OS identifiers and exit\n"
           "    -d | --detect <PATH>    detect OS from given path\n",
           progname);
}


static int
parseArgv(Control *ctl,
          int argc,
          char *argv[])
{
    int ret = -1;
    int arg;
    int longindex = -1;
    const char *progname = NULL;
    struct option opt[] = {
        { "version", no_argument, NULL, 'v' },
        { "help", no_argument, NULL, 'h' },
        { "db-path", required_argument, NULL, 'p' },
        { "os-id", required_argument, NULL, 'i' },
        { "list", no_argument, NULL, 'l' },
        { "detect", required_argument, NULL, 'd' },
        { NULL, 0, NULL, 0 },
    };

    progname = strrchr(argv[0], '/');
    if (!progname) {
        progname = argv[0];
    } else {
        progname++;
    }

    while ((arg = getopt_long(argc, argv, ":vhp:ild:", opt, &longindex)) != -1) {
        switch (arg) {
        case 'v':
            print_libosinfo_version();
            exit(EXIT_SUCCESS);
            break;

        case 'h':
            print_help(progname);
            exit(EXIT_SUCCESS);
            break;

        case 'p':
            ctl->db_path = optarg;
            break;

        case 'i':
            ctl->os_id = optarg;
            break;

        case 'l':
            ctl->list_os_ids = true;
            break;

        case 'd':
            ctl->detect_path = optarg;
            break;

        case '?':
            if (optopt)
                ERROR("unsupported option '-%1$c'. See --help.", optopt);
            else
                ERROR("unsupported option '%1$s'. See --help.", argv[optind - 1]);
            goto cleanup;
        default:
            ERROR("unknown option");
            goto cleanup;
        }

        longindex = -1;
    }

    if (argc != optind) {
        if (ctl->os_id || optind + 1 != argc) {
            ERROR("Too much args");
            goto cleanup;
        }
        ctl->os_id = argv[optind];
    }

    if (ctl->detect_path && ctl->os_id) {
        ERROR("--os-id and --detect are mutually exclusive");
        goto cleanup;
    }

    ret = 0;
 cleanup:
    return ret;
}


static OsinfoDb *
load_db(Control *ctl)
{
    g_autoptr(OsinfoLoader) loader = osinfo_loader_new();
    GError *error = NULL;

    if (ctl->db_path) {
        osinfo_loader_process_path(loader, ctl->db_path, &error);
    } else {
        osinfo_loader_process_default_path(loader, &error);
    }

    if (error) {
        ERROR(error->message);
        return NULL;
    }

    return g_object_ref(osinfo_loader_get_db(loader));
}


static gint
sort_entity(gconstpointer a,
            gconstpointer b,
            gpointer data)
{
    OsinfoEntity *entityA = OSINFO_ENTITY((gpointer) a);
    OsinfoEntity *entityB = OSINFO_ENTITY((gpointer) b);
    gchar *key = data;
    const gchar *valA;
    const gchar *valB;

    valA = osinfo_entity_get_param_value(entityA, key);
    valB = osinfo_entity_get_param_value(entityB, key);

    if (!valA && !valB)
        return 0;

    if (!valA && valB)
        return 1;

    if (valA && !valB)
        return 1;

    return strcmp(valA, valB);
}


#define FMT_STRING "%25s\t%-50s\t%-10s\t%s\n"
static void
list_os_ids(OsinfoDb *db)
{
    g_autoptr(OsinfoOsList) list = osinfo_db_get_os_list(db);
    g_autoptr(GList) entities = osinfo_list_get_elements(OSINFO_LIST(list));
    GList *tmp;

    tmp = entities = g_list_sort_with_data(entities, sort_entity,
                                           OSINFO_PRODUCT_PROP_SHORT_ID);

    printf(FMT_STRING, "Short ID", "Name", "Version", "ID");

    /* No match */
    if (tmp == NULL)
        return;

    while (tmp) {
        OsinfoEntity *entity = OSINFO_ENTITY(tmp->data);
        const char *shortID = osinfo_entity_get_param_value(entity, OSINFO_PRODUCT_PROP_SHORT_ID);
        const char *name = osinfo_entity_get_param_value(entity, OSINFO_PRODUCT_PROP_NAME);
        const char *version = osinfo_entity_get_param_value(entity, OSINFO_PRODUCT_PROP_VERSION);
        const char *id = osinfo_entity_get_param_value(entity, OSINFO_ENTITY_PROP_ID);

        printf(FMT_STRING, NULLSTR(shortID), NULLSTR(name), NULLSTR(version), NULLSTR(id));

        tmp = tmp->next;
    }
}
#undef FMT_STRING


static OsinfoOs *
find_os_from_path(Control *ctl,
                  OsinfoDb *db)
{
    g_autoptr(OsinfoMedia) media = NULL;
    GError *error = NULL;
    OsinfoOs *os = NULL;
    g_autoptr(OsinfoMediaList) matched = NULL;

    media = osinfo_media_create_from_location(ctl->detect_path, NULL, &error);
    if (error) {
        ERROR(error->message);
        return NULL;
    }

    matched = osinfo_db_identify_medialist(db, media);
    if (osinfo_list_get_length(OSINFO_LIST(matched)) > 0) {
        OsinfoMedia *newmedia = OSINFO_MEDIA(osinfo_list_get_nth(OSINFO_LIST(matched), 0));

        g_object_get(G_OBJECT(newmedia), "os", &os, NULL);
        if (os) {
            return os;
        }
    }

    ERROR("Unable to find OS from: %s", ctl->detect_path);
    return NULL;
}


static OsinfoOs *
find_os(Control *ctl,
        OsinfoDb *db)
{
    OsinfoOs *os = NULL;
    g_autoptr(OsinfoOsList) oslist = NULL;
    g_autoptr(OsinfoFilter) filter = NULL;
    g_autoptr(OsinfoOsList) filteredList = NULL;

    if (!ctl->os_id) {
        ERROR("No OS specified");
        return NULL;
    }

    if ((os = osinfo_db_get_os(db, ctl->os_id))) {
        return g_object_ref(os);
    }

    oslist = osinfo_db_get_os_list(db);
    filter = osinfo_filter_new();
    osinfo_filter_add_constraint(filter,
                                 OSINFO_PRODUCT_PROP_SHORT_ID,
                                 ctl->os_id);

    filteredList = OSINFO_OSLIST(osinfo_list_new_filtered(OSINFO_LIST(oslist),
                                                          filter));

    if (osinfo_list_get_length(OSINFO_LIST(filteredList)) > 0) {
        os = OSINFO_OS(osinfo_list_get_nth(OSINFO_LIST(filteredList), 0));
        return g_object_ref(os);
    }

    ERROR("Unable to find OS: %s", ctl->os_id);
    return NULL;
}


#define PRINT_POSITIVE(val, ...) \
    do { \
        typeof(val) _v = val; \
        if (_v > 0) { \
            printf(__VA_ARGS__, _v); \
        } \
    } while (0)
static void
print_one_resource(OsinfoResources *res)
{
    PRINT_POSITIVE(osinfo_resources_get_n_cpus(res),
                   "N CPUs: %d\n");
    PRINT_POSITIVE(osinfo_resources_get_cpu(res) / OSINFO_MEGAHERTZ,
                   "CPU freq: %" G_GINT64_FORMAT " MHz\n");
    PRINT_POSITIVE(osinfo_resources_get_ram(res) / OSINFO_MEBIBYTES,
                   "Memory: %" G_GINT64_FORMAT " MiB\n");
    PRINT_POSITIVE(osinfo_resources_get_storage(res) / OSINFO_GIBIBYTES,
                   "Storage: %" G_GINT64_FORMAT " GiB\n");
    printf("\n");
}
#undef PRINT_POSITIVE

static int
print_resources(Control *ctl,
                OsinfoOs *os)
{
    OsinfoResourcesList *resources_list;
    OsinfoResources *resources;

    resources_list = osinfo_os_get_minimum_resources(os);
    if (resources_list && osinfo_list_get_length(OSINFO_LIST(resources_list)) > 0) {
        resources = OSINFO_RESOURCES(osinfo_list_get_nth(OSINFO_LIST(resources_list), 0));
        printf("Minimum resources\n");
        print_one_resource(resources);
    }
    g_clear_object(&resources_list);

    resources_list = osinfo_os_get_recommended_resources(os);
    if (resources_list && osinfo_list_get_length(OSINFO_LIST(resources_list)) > 0) {
        resources = OSINFO_RESOURCES(osinfo_list_get_nth(OSINFO_LIST(resources_list), 0));
        printf("Recommended resources\n");
        print_one_resource(resources);
    }
    g_clear_object(&resources_list);

    resources_list = osinfo_os_get_maximum_resources(os);
    if (resources_list && osinfo_list_get_length(OSINFO_LIST(resources_list)) > 0) {
        resources = OSINFO_RESOURCES(osinfo_list_get_nth(OSINFO_LIST(resources_list), 0));
        printf("Maximum resources\n");
        print_one_resource(resources);
    }
    g_clear_object(&resources_list);

    resources_list = osinfo_os_get_network_install_resources(os);
    if (resources_list && osinfo_list_get_length(OSINFO_LIST(resources_list)) > 0) {
        resources = OSINFO_RESOURCES(osinfo_list_get_nth(OSINFO_LIST(resources_list), 0));
        printf("Network install resources\n");
        print_one_resource(resources);
    }
    g_clear_object(&resources_list);

    return 0;
}


int
main(int argc, char *argv[])
{
    Control ctl = { };
    g_autoptr(OsinfoDb) db = NULL;
    g_autoptr(OsinfoOs) os = NULL;

    if (parseArgv(&ctl, argc, argv) < 0) {
        return EXIT_FAILURE;
    }

    if (!(db = load_db(&ctl))) {
        return EXIT_FAILURE;
    }

    if (ctl.list_os_ids) {
        list_os_ids(db);
        return EXIT_SUCCESS;
    }

    if (ctl.detect_path) {
        os = find_os_from_path(&ctl, db);
    } else {
        os = find_os(&ctl, db);
    }

    if (!os) {
        return EXIT_FAILURE;
    }

    if (print_resources(&ctl, os) < 0) {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}