From 460a4b2852538a75450efc394d4bfe9d625a7e80 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Wed, 16 Dec 2015 18:55:28 -0500 Subject: [PATCH] remote: Add "ostree remote summary" command Downloads and prints a remote summary file and any signatures in an easy-to-read format, or alternatively with the --raw option, prints the summary GVariant data directly. https://bugzilla.gnome.org/show_bug.cgi?id=759250 --- Makefile-ostree.am | 3 +- doc/ostree-remote.xml | 17 ++++ src/ostree/ot-builtin-remote.c | 1 + src/ostree/ot-dump.c | 99 ++++++++++++++++++++ src/ostree/ot-dump.h | 3 + src/ostree/ot-remote-builtin-summary.c | 119 +++++++++++++++++++++++++ src/ostree/ot-remote-builtins.h | 1 + tests/test-pull-summary-sigs.sh | 11 +++ 8 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 src/ostree/ot-remote-builtin-summary.c diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 987f15d9..384479da 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -82,7 +82,8 @@ ostree_SOURCES += \ src/ostree/ot-remote-builtin-gpg-import.c \ src/ostree/ot-remote-builtin-list.c \ src/ostree/ot-remote-builtin-show-url.c \ - src/ostree/ot-remote-builtin-refs.c \ + src/ostree/ot-remote-builtin-refs.c \ + src/ostree/ot-remote-builtin-summary.c \ $(NULL) src/ostree/parse-datetime.c: src/ostree/parse-datetime.y Makefile diff --git a/doc/ostree-remote.xml b/doc/ostree-remote.xml index ae9e078f..c303014e 100644 --- a/doc/ostree-remote.xml +++ b/doc/ostree-remote.xml @@ -66,6 +66,9 @@ Boston, MA 02111-1307, USA. ostree remote refs NAME + + ostree remote summary OPTIONS NAME + @@ -157,6 +160,20 @@ Boston, MA 02111-1307, USA. + + 'Summary' Options + + + + + + + Show raw variant data + + + + + Example $ ostree remote show-url local diff --git a/src/ostree/ot-builtin-remote.c b/src/ostree/ot-builtin-remote.c index 4ea8d22e..7809dd67 100644 --- a/src/ostree/ot-builtin-remote.c +++ b/src/ostree/ot-builtin-remote.c @@ -38,6 +38,7 @@ static OstreeRemoteCommand remote_subcommands[] = { { "list", ot_remote_builtin_list }, { "gpg-import", ot_remote_builtin_gpg_import }, { "refs", ot_remote_builtin_refs }, + { "summary", ot_remote_builtin_summary }, { NULL, NULL } }; diff --git a/src/ostree/ot-dump.c b/src/ostree/ot-dump.c index fd4c367c..670ccd6a 100644 --- a/src/ostree/ot-dump.c +++ b/src/ostree/ot-dump.c @@ -153,3 +153,102 @@ ot_dump_object (OstreeObjectType objtype, break; } } + +static void +dump_summary_ref (const char *ref_name, + guint64 commit_size, + GVariant *csum_v, + GVariantIter *metadata) +{ + const guchar *csum_bytes; + GError *csum_error = NULL; + g_autofree char *size = NULL; + GVariant *value; + char *key; + + g_print ("* %s\n", ref_name); + + size = g_format_size (commit_size); + g_print (" Latest Commit (%s):\n", size); + + csum_bytes = ostree_checksum_bytes_peek_validate (csum_v, &csum_error); + if (csum_error == NULL) + { + char csum[65]; + + ostree_checksum_inplace_from_bytes (csum_bytes, csum); + g_print (" %s\n", csum); + } + else + { + g_print (" %s\n", csum_error->message); + g_clear_error (&csum_error); + } + + while (g_variant_iter_loop (metadata, "{sv}", &key, &value)) + { + g_autofree char *string = g_variant_print (value, FALSE); + g_print (" %s: %s\n", key, string); + } +} + +void +ot_dump_summary_bytes (GBytes *summary_bytes, + OstreeDumpFlags flags) +{ + g_autoptr(GVariant) summary = NULL; + g_autoptr(GVariant) refs = NULL; + g_autoptr(GVariant) exts = NULL; + GVariantIter iter; + GVariant *value; + char *key; + + g_return_if_fail (summary_bytes != NULL); + + summary = g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, + summary_bytes, FALSE); + + if (flags & OSTREE_DUMP_RAW) + { + ot_dump_variant (summary); + return; + } + + refs = g_variant_get_child_value (summary, 0); + exts = g_variant_get_child_value (summary, 1); + + g_variant_iter_init (&iter, refs); + + while ((value = g_variant_iter_next_value (&iter)) != NULL) + { + const char *ref_name = NULL; + + g_variant_get_child (value, 0, "&s", &ref_name); + + if (ref_name != NULL) + { + g_autoptr(GVariant) csum_v = NULL; + g_autoptr(GVariantIter) metadata = NULL; + guint64 commit_size; + + g_variant_get_child (value, 1, "(t@aya{sv})", + &commit_size, &csum_v, &metadata); + + dump_summary_ref (ref_name, commit_size, csum_v, metadata); + + g_print ("\n"); + } + + g_variant_unref (value); + } + + g_variant_iter_init (&iter, exts); + + /* XXX Should we print something more human-friendly for + * known extension names like 'ostree.static-deltas'? */ + while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) + { + g_autofree char *string = g_variant_print (value, FALSE); + g_print ("%s: %s\n", key, string); + } +} diff --git a/src/ostree/ot-dump.h b/src/ostree/ot-dump.h index f6122655..547981e8 100644 --- a/src/ostree/ot-dump.h +++ b/src/ostree/ot-dump.h @@ -37,3 +37,6 @@ void ot_dump_object (OstreeObjectType objtype, const char *checksum, GVariant *variant, OstreeDumpFlags flags); + +void ot_dump_summary_bytes (GBytes *summary_bytes, + OstreeDumpFlags flags); diff --git a/src/ostree/ot-remote-builtin-summary.c b/src/ostree/ot-remote-builtin-summary.c new file mode 100644 index 00000000..da76017f --- /dev/null +++ b/src/ostree/ot-remote-builtin-summary.c @@ -0,0 +1,119 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "otutil.h" + +#include "ot-main.h" +#include "ot-dump.h" +#include "ot-remote-builtins.h" + +static gboolean opt_raw; + +static GOptionEntry option_entries[] = { + { "raw", 0, 0, G_OPTION_ARG_NONE, &opt_raw, "Show raw variant data", NULL }, + { NULL } +}; + +gboolean +ot_remote_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + GOptionContext *context; + glnx_unref_object OstreeRepo *repo = NULL; + const char *remote_name; + g_autoptr(GBytes) summary_bytes = NULL; + g_autoptr(GBytes) signature_bytes = NULL; + OstreeDumpFlags flags = OSTREE_DUMP_NONE; + gboolean gpg_verify_summary; + gboolean ret = FALSE; + + context = g_option_context_new ("NAME - Show remote summary"); + + if (!ostree_option_context_parse (context, option_entries, &argc, &argv, + OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) + goto out; + + if (argc < 2) + { + ot_util_usage_error (context, "NAME must be specified", error); + goto out; + } + + remote_name = argv[1]; + + if (opt_raw) + flags |= OSTREE_DUMP_RAW; + + if (!ostree_repo_remote_fetch_summary (repo, remote_name, + &summary_bytes, + &signature_bytes, + cancellable, error)) + goto out; + + if (summary_bytes == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Remote server has no summary file"); + goto out; + } + + ot_dump_summary_bytes (summary_bytes, flags); + + if (!ostree_repo_remote_get_gpg_verify_summary (repo, remote_name, + &gpg_verify_summary, + error)) + goto out; + + if (!gpg_verify_summary) + g_clear_pointer (&signature_bytes, g_bytes_unref); + + /* XXX Note we don't show signatures for "--raw". My intuition is + * if someone needs to see or parse raw summary data, including + * signatures in the output would probably just interfere. + * If there's demand for it I suppose we could introduce a new + * option for raw signature data like "--raw-signatures". */ + if (signature_bytes != NULL && !opt_raw) + { + glnx_unref_object OstreeGpgVerifyResult *result = NULL; + + /* The actual signed summary verification happens above in + * ostree_repo_remote_fetch_summary(). Here we just parse + * the signatures again for the purpose of printing. */ + result = ostree_repo_verify_summary (repo, + remote_name, + summary_bytes, + signature_bytes, + cancellable, + error); + if (result == NULL) + goto out; + + g_print ("\n"); + ostree_print_gpg_verify_result (result); + } + + ret = TRUE; + +out: + g_option_context_free (context); + + return ret; +} diff --git a/src/ostree/ot-remote-builtins.h b/src/ostree/ot-remote-builtins.h index b7974a4c..0e65092f 100644 --- a/src/ostree/ot-remote-builtins.h +++ b/src/ostree/ot-remote-builtins.h @@ -30,5 +30,6 @@ gboolean ot_remote_builtin_gpg_import (int argc, char **argv, GCancellable *canc gboolean ot_remote_builtin_list (int argc, char **argv, GCancellable *cancellable, GError **error); gboolean ot_remote_builtin_show_url (int argc, char **argv, GCancellable *cancellable, GError **error); gboolean ot_remote_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **error); +gboolean ot_remote_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error); G_END_DECLS diff --git a/tests/test-pull-summary-sigs.sh b/tests/test-pull-summary-sigs.sh index eb987c76..7afca9a3 100644 --- a/tests/test-pull-summary-sigs.sh +++ b/tests/test-pull-summary-sigs.sh @@ -103,3 +103,14 @@ cd ${test_tmpdir} repo_reinit ${OSTREE} --repo=repo pull origin main echo "ok pull delta with signed summary" + +# Verify 'ostree remote summary' output. +${OSTREE} --repo=repo remote summary origin > summary.txt +assert_file_has_content summary.txt "* main" +assert_file_has_content summary.txt "* other" +assert_file_has_content summary.txt "* yet-another" +assert_file_has_content summary.txt "found 1 signature" +assert_file_has_content summary.txt "Good signature from \"Ostree Tester \"" +grep static-deltas summary.txt > static-deltas.txt +assert_file_has_content static-deltas.txt \ + $(${OSTREE} --repo=repo rev-parse origin:main)