From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> Enable end users to search the database with a new osinfo-query command. For example $ osinfo-query --fields=short-id,name os vendor="Fedora Project" Short ID | Name ----------------------+------------------ fedora1 | Fedora Core 1 fedora2 | Fedora Core 2 fedora3 | Fedora Core 3 fedora4 | Fedora Core 4 fedora5 | Fedora Core 5 fedora6 | Fedora Core 6 ... --- libosinfo.spec.in | 1 + mingw32-libosinfo.spec.in | 1 + tools/Makefile.am | 9 +- tools/osinfo-query.c | 707 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 716 insertions(+), 2 deletions(-) create mode 100644 tools/osinfo-query.c diff --git a/libosinfo.spec.in b/libosinfo.spec.in index 28f4804..9bfeff8 100644 --- a/libosinfo.spec.in +++ b/libosinfo.spec.in @@ -100,6 +100,7 @@ rm -fr %{buildroot} %{_bindir}/osinfo-usbids-convert %{_bindir}/osinfo-detect %{_bindir}/osinfo-db-validate +%{_bindir}/osinfo-query %dir %{_datadir}/libosinfo/ %dir %{_datadir}/libosinfo/data/ %dir %{_datadir}/libosinfo/schemas/ diff --git a/mingw32-libosinfo.spec.in b/mingw32-libosinfo.spec.in index 0ec510c..7da0736 100644 --- a/mingw32-libosinfo.spec.in +++ b/mingw32-libosinfo.spec.in @@ -63,6 +63,7 @@ rm -rf $RPM_BUILD_ROOT %{_mingw32_bindir}/osinfo-usbids-convert %{_mingw32_bindir}/osinfo-detect.exe %{_mingw32_bindir}/osinfo-db-validate.exe +%{_mingw32_bindir}/osinfo-query.exe %{_mingw32_bindir}/libosinfo-1.0-0.dll %{_mingw32_libdir}/libosinfo-1.0.dll.a %{_mingw32_libdir}/libosinfo-1.0.la diff --git a/tools/Makefile.am b/tools/Makefile.am index ddee9be..29ef078 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -6,9 +6,9 @@ AM_CFLAGS = $(GOBJECT_CFLAGS) \ -I$(top_srcdir) \ $(NULL) -bin_PROGRAMS = osinfo-detect osinfo-db-validate +bin_PROGRAMS = osinfo-detect osinfo-db-validate osinfo-query -man1_MANS = osinfo-db-validate.1 osinfo-detect.1 +man1_MANS = osinfo-db-validate.1 osinfo-detect.1 osinfo-query.1 POD2MAN = pod2man -c "Virtualization Support" -r "$(PACKAGE)-$(VERSION)" @@ -26,3 +26,8 @@ osinfo_db_validate_LDADD = $(GOBJECT_LIBS) \ $(GIO_LIBS) \ $(LIBXML_LIBS) \ $(top_builddir)/osinfo/libosinfo-1.0.la + +osinfo_query_SOURCES = osinfo-query.c +osinfo_query_LDADD = $(GOBJECT_LIBS) \ + $(GIO_LIBS) \ + $(top_builddir)/osinfo/libosinfo-1.0.la diff --git a/tools/osinfo-query.c b/tools/osinfo-query.c new file mode 100644 index 0000000..cfb807e --- /dev/null +++ b/tools/osinfo-query.c @@ -0,0 +1,707 @@ +/* + * Copyright (C) 2012 Red Hat, Inc + * + * osinfo-query: query the contents of the database + * + * 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Authors: + * Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#include <config.h> + +#include <glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <osinfo/osinfo.h> + + +struct OsinfoLabel { + const gchar *prop; + const gchar *label; + gboolean enabled; + gsize width; +}; + +typedef OsinfoList * (*osinfo_list_func)(OsinfoDb *db); + +struct OsinfoType { + const gchar *name; + osinfo_list_func listFunc; + GType entityType; + GType filterType; + GType listType; + struct OsinfoLabel *labels; +}; + +static struct OsinfoLabel os_labels[] = { + { OSINFO_PRODUCT_PROP_SHORT_ID, + "Short ID", TRUE, 20 }, + { OSINFO_PRODUCT_PROP_NAME, + "Name", TRUE, 50 }, + { OSINFO_PRODUCT_PROP_VERSION, + "Version", TRUE, 8 }, + { OSINFO_OS_PROP_FAMILY, + "Family", TRUE, 10 }, + { OSINFO_PRODUCT_PROP_VENDOR, + "Vendor", TRUE, 25 }, + { OSINFO_PRODUCT_PROP_RELEASE_DATE, + "Release date", FALSE, 12 }, + { OSINFO_PRODUCT_PROP_EOL_DATE, + "End of life", FALSE, 12 }, + { OSINFO_PRODUCT_PROP_CODENAME, + "Code name", FALSE, 10 }, + { OSINFO_ENTITY_PROP_ID, + "ID", TRUE, 40 }, + { NULL, NULL, 0 }, +}; + +static struct OsinfoLabel platform_labels[] = { + { OSINFO_PRODUCT_PROP_SHORT_ID, + "Short ID", TRUE, 20 }, + { OSINFO_PRODUCT_PROP_NAME, + "Name", TRUE, 50 }, + { OSINFO_PRODUCT_PROP_VERSION, + "Version", TRUE, 8 }, + { OSINFO_PRODUCT_PROP_VENDOR, + "Vendor", TRUE, 25 }, + { OSINFO_PRODUCT_PROP_RELEASE_DATE, + "Release date", FALSE, 12 }, + { OSINFO_PRODUCT_PROP_EOL_DATE, + "End of life", FALSE, 12 }, + { OSINFO_PRODUCT_PROP_CODENAME, + "Code name", FALSE, 10 }, + { OSINFO_ENTITY_PROP_ID, + "ID", TRUE, 40 }, + { NULL, NULL, 0 }, +}; + +static struct OsinfoLabel device_labels[] = { + { OSINFO_DEVICE_PROP_VENDOR, + "Vendor", TRUE, 20 }, + { OSINFO_DEVICE_PROP_VENDOR_ID, + "Vendor ID", TRUE, 12 }, + { OSINFO_DEVICE_PROP_PRODUCT, + "Product", TRUE, 20 }, + { OSINFO_DEVICE_PROP_PRODUCT_ID, + "Product ID", TRUE, 12 }, + { OSINFO_PRODUCT_PROP_NAME, + "Name", TRUE, 14 }, + { OSINFO_DEVICE_PROP_CLASS, + "Class", TRUE, 15 }, + { OSINFO_DEVICE_PROP_BUS_TYPE, + "Bus", TRUE, 8 }, + { OSINFO_ENTITY_PROP_ID, + "ID", TRUE, 40 }, + { NULL, NULL, 0 }, +}; + +static struct OsinfoLabel deployment_labels[] = { + { OSINFO_ENTITY_PROP_ID, + "ID", TRUE, 40 }, + { NULL, NULL, 0 }, +}; + + +static gboolean toggle_fields(struct OsinfoLabel *labels, + const gchar *fieldStr, + GError **error) +{ + gboolean ret = FALSE; + gchar **fields; + gsize i, j; + + if (!fieldStr) + return TRUE; + + fields = g_strsplit(fieldStr, ",", 0); + + for (j = 0 ; labels[j].prop ; j++) { + labels[j].enabled = FALSE; + } + + for (i = 0 ; fields[i] != NULL ; i++) { + gboolean found = FALSE; + for (j = 0 ; labels[j].prop ; j++) { + if (g_str_equal(fields[i], labels[j].prop)) { + labels[j].enabled = TRUE; + found = TRUE; + } + } + if (!found) { + g_set_error(error, 0, 0, + "Unknown property name %s", fields[i]); + goto cleanup; + } + } + + + ret = TRUE; + + cleanup: + g_strfreev(fields); + return ret; +} + +static gboolean build_filter(struct OsinfoLabel *labels, + OsinfoFilter *filter, + gint argc, char **argv, + GError **error) +{ + gboolean ret = FALSE; + gsize i, j; + + for (i = 0 ; i < argc ; i++) { + const gchar *tmp = strchr(argv[i], '='); + if (!tmp) { + g_set_error(error, 0, 0, "%s", "Syntax error in condition, expecting KEY=VALUE"); + goto cleanup; + } + gchar *key = g_strndup(argv[i], tmp-argv[i]); + gchar *val = g_strdup(tmp+1); + gboolean found = FALSE; + + for (j = 0 ; labels[j].prop != NULL ; j++) { + if (g_str_equal(key, labels[j].prop)) + found = TRUE; + } + + if (!found) { + g_set_error(error, 0, 0, + "Unknown property name %s", key); + goto cleanup; + } + + osinfo_filter_add_constraint(filter, key, val); + g_free(key); + g_free(val); + } + + ret = TRUE; + cleanup: + return ret; +} + + +static gint sort_entity(gconstpointer a, + gconstpointer b, + gpointer data) +{ + OsinfoEntity *entityA = OSINFO_ENTITY(a); + OsinfoEntity *entityB = OSINFO_ENTITY(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); +} + + +static gboolean print_entity_text(OsinfoEntity *entity, + const struct OsinfoLabel *labels) +{ + gsize i; + gboolean first = TRUE; + for (i = 0 ; labels[i].prop != NULL ; i++) { + gsize pad; + gchar *padstr; + const gchar *val = osinfo_entity_get_param_value(entity, labels[i].prop); + if (!labels[i].enabled) + continue; + + if (first) + g_print(" "); + else + g_print(" | "); + first = FALSE; + + if (val && (strlen(val) > labels[i].width)) + pad = 0; + else + pad = labels[i].width - (val ? strlen(val) : 0); + + padstr = g_new0(gchar, pad+1); + memset(padstr, ' ', pad); + padstr[pad] = '\0'; + + g_print("%s%s", + val ? val : "", padstr); + g_free(padstr); + } + g_print("\n"); + + return FALSE; +} + +static gboolean print_results_text(OsinfoList *list, + const struct OsinfoLabel *labels, + const gchar *sortKey) +{ + gboolean ret = FALSE; + GList *entities = osinfo_list_get_elements(list); + GList *tmp; + gsize i; + gboolean first = TRUE; + + tmp = entities = g_list_sort_with_data(entities, sort_entity, + (gchar*)(sortKey ? sortKey : + labels[0].prop)); + + for (i = 0 ; labels[i].prop != NULL ; i++) { + gsize pad; + gchar *padstr; + if (!labels[i].enabled) + continue; + + if (first) + g_print(" "); + else + g_print(" | "); + first = FALSE; + + if (strlen(labels[i].label) > labels[i].width) + pad = 0; + else + pad = labels[i].width - strlen(labels[i].label); + + padstr = g_new0(gchar, pad+1); + memset(padstr, ' ', pad); + padstr[pad] = '\0'; + + g_print("%s%s", + labels[i].label, padstr); + g_free(padstr); + } + g_print("\n"); + + first = TRUE; + for (i = 0 ; labels[i].prop != NULL ; i++) { + gchar *padstr; + if (!labels[i].enabled) + continue; + + if (first) + g_print("-"); + else + g_print("-+-"); + first = FALSE; + + padstr = g_new0(gchar, labels[i].width+1); + memset(padstr, '-', labels[i].width); + padstr[labels[i].width] = '\0'; + + g_print("%s", padstr); + g_free(padstr); + } + g_print("\n"); + + while (tmp) { + OsinfoEntity *entity = OSINFO_ENTITY(tmp->data); + + print_entity_text(entity, labels); + tmp = tmp->next; + } + + + g_list_free(entities); + ret = TRUE; + // cleanup: + return ret; +} + +gint main(gint argc, gchar **argv) +{ + GOptionContext *context; + GError *error = NULL; + gint ret = EXIT_FAILURE; + const gchar *type = NULL; + OsinfoLoader *loader = NULL; + OsinfoDb *db = NULL; + OsinfoList *entities = NULL; + OsinfoFilter *filter = NULL; + OsinfoList *results = NULL; + struct OsinfoLabel *labels = NULL; + gsize i; + const gchar *sortKey = NULL; + const gchar *fields = NULL; + + g_type_init(); + + struct OsinfoType types[] = { + { "os", + (osinfo_list_func)osinfo_db_get_os_list, + OSINFO_TYPE_OS, + OSINFO_TYPE_PRODUCTFILTER, + OSINFO_TYPE_OSLIST, + os_labels }, + { "platform", + (osinfo_list_func)osinfo_db_get_platform_list, + OSINFO_TYPE_PLATFORM, + OSINFO_TYPE_PRODUCTFILTER, + OSINFO_TYPE_PLATFORMLIST, + platform_labels }, + { "device", + (osinfo_list_func)osinfo_db_get_device_list, + OSINFO_TYPE_DEVICE, + OSINFO_TYPE_FILTER, + OSINFO_TYPE_DEVICELIST, + device_labels }, + { "deployment", + (osinfo_list_func)osinfo_db_get_deployment_list, + OSINFO_TYPE_DEPLOYMENT, + OSINFO_TYPE_FILTER, + OSINFO_TYPE_DEPLOYMENTLIST, + deployment_labels }, + }; + + GOptionEntry entries[] = { + { "sort", 's', 0, G_OPTION_ARG_STRING, &sortKey, + "Sort column", NULL }, + { "fields", 'f', 0, G_OPTION_ARG_STRING, &fields, + "Display fields", NULL }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + + context = g_option_context_new("- Query the OS info database"); + + g_option_context_add_main_entries(context, entries, NULL); + + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Error while parsing options: %s\n", error->message); + g_printerr("%s\n", g_option_context_get_help(context, FALSE, NULL)); + goto error; + } + + if (argc < 2) { + g_printerr("Missing data type parameter\n"); + goto error; + } + + type = argv[1]; + + loader = osinfo_loader_new(); + osinfo_loader_process_default_path(loader, &error); + if (error != NULL) { + g_printerr("Error loading OS data: %s\n", error->message); + goto error; + } + + db = osinfo_loader_get_db(loader); + + + for (i = 0 ; i < (sizeof(types)/sizeof(types[0])) ; i++) { + if (g_str_equal(types[i].name, type)) { + entities = types[i].listFunc(db); + filter = g_object_new(types[i].filterType, NULL); + results = g_object_new(types[i].listType, + "element-type", types[i].entityType, + NULL); + labels = types[i].labels; + } + } + + if (!entities) { + g_printerr("Unknown type '%s' requested\n", type); + goto error; + } + + if (!build_filter(labels, filter, argc-2, argv+2, &error)) { + g_printerr("Unable to construct filter: %s\n", error->message); + goto error; + } + + if (!toggle_fields(labels, fields, &error)) { + g_printerr("Unable to set field visibility: %s\n", error->message); + goto error; + } + + osinfo_list_add_filtered(results, entities, filter); + + print_results_text(results, labels, sortKey); + + ret = EXIT_SUCCESS; + + error: + g_clear_error(&error); + g_option_context_free(context); + + if (entities) + g_object_unref(entities); + if (filter) + g_object_unref(filter); + if (results) + g_object_unref(results); + if (loader) + g_object_unref(loader); + + return ret; +} + +/* +=pod + +=head1 NAME + +osinfo-query - Query information in the database + +=head1 SYNOPSIS + +osinfo-query [OPTIONS...] TYPE [CONDITION-1 [CONDITION-2 ...]] + +=head1 DESCRIPTION + +The C<osinfo-query> command allows extraction of information from the +database. B<TYPE> can be one of C<os>, C<platform>, C<device>, or +C<deployment>. With no conditions specified, all entities of the given +type will be listed. + + # List all operating systems + $ osinfo-query os + Short ID | Name ... + ----------------------+----------- + centos-6.0 | CentOS 6.0 ... + centos-6.1 | CentOS 6.1 ... + ... + +Conditions allow filtering based on specific properties of an entity. +For example, to filter only distros from the Fedora Project, use + + # List all operating systems + $ osinfo-query os vendor="Fedora Project" + Short ID | Name ... + ----------------------+-------------- + fedora1 | Fedora Core 1 ... + fedora2 | Fedora Core 2 ... + ... + +The set of fields which are printed can be controlled using the C<--fields> +command line argument: + + # List all operating systems + $ osinfo-query --fields=short-id,version os vendor="Fedora Project" + Short ID | Version + ----------------------+---------- + fedora1 | 1 + fedora2 | 2 + ... + + +=head1 OPTIONS + +=over 8 + +=item B<-s PROPERTY>, B<--sort-key PROPERTY> + +Set the data sorting key. Defaults sorting the first column + +=item B<-f PROPERTY1,PROPERTY2,...>, B<--fields PROPERTY1,PROPERTY2,...> + +Set the visibility of properties in output + +=back + +=head1 PROPERTY NAMES + +=head2 OS + +Valid property names for the C<os> type are: + +=over 4 + +=item B<short-id> + +The short OS identifier + +=item B<name> + +The long OS name + +=item B<version> + +The OS version string + +=item B<family> + +The OS kernel family + +=item B<vendor> + +The OS vendor + +=item B<release-date> + +The OS release date + +=item B<eol-date> + +The OS end-of-life date + +=item B<codename> + +The OS code name + +=item B<id> + +The OS identifier + +=back + + +=head2 PLATFORM + +Valid property names for the C<platform> type are: + +=over 4 + +=item B<short-id> + +The short platform identifier + +=item B<name> + +The long platform name + +=item B<version> + +The platform version string + +=item B<vendor> + +The platform vendor + +=item B<release-date> + +The platform release date + +=item B<eol-date> + +The platform end-of-life date + +=item B<codename> + +The platform code name + +=item B<id> + +The platform identifier + +=back + + +=head2 DEVICE + +Valid property names for the C<device> type are: + +=over 4 + +=item B<name> + +The device name + +=item B<product> + +The device product name + +=item B<product-id> + +The device product ID string + +=item B<vendor> + +The device vendor name + +=item B<vendor-id> + +The device vendor ID string + +=item B<class> + +The device type class + +=item B<bus> + +The device bus type + +=item B<id> + +The device identifier + +=back + + +=head2 DEPLOYMENT + +Valid property names for the C<deployment> type are: + +=over 4 + +=item B<id> + +The deployment identifier + +=back + + +=head1 EXIT STATUS + +The exit status will be 0 if matching entries were found, +or 1 if not matches were found + +=head1 SEE ALSO + +C<osinfo-db-validate(1)>, C<osinfo-detect(1)> + +=head1 AUTHORS + +Daniel P. Berrange <berrange@xxxxxxxxxx> + +=head1 COPYRIGHT + +Copyright (C) 2012 Red Hat, Inc. + +=head1 LICENSE + +C<osinfo-query> is distributed under the termsof the GNU LGPL v2+ +license. This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE + +=cut +*/ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * End: + */ -- 1.7.7.6