/
githubmirror
/
libmodulemd
Обзор
Документация
Войти
/
githubmirror
/
libmodulemd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
libmodulemd-2.15.3
modulemd/modulemd-validator.c
523 строки
17 KB
Petr Písař
modulemd-validator: Correct a literal initialization of a structure to 0
13 сен 2021, 18:08
13 сен 2021, 18:08
eb48bdd
Код
Авторство
О чём код?
/* * This file is part of libmodulemd * Copyright 2018 Stephen Gallagher * * Fedora-License-Identifier: MIT * SPDX-2.0-License-Identifier: MIT * SPDX-3.0-License-Identifier: MIT * * This program is free software. * For more information on the license, see COPYING. * For more information on free software, see <https://www.gnu.org/philosophy/free-sw.en.html>. */ #include "modulemd.h" #include "modulemd-errors.h" #include "private/modulemd-defaults-v1-private.h" #include "private/modulemd-module-index-private.h" #include "private/modulemd-module-stream-v1-private.h" #include "private/modulemd-module-stream-v2-private.h" #include "private/modulemd-obsoletes-private.h" #include "private/modulemd-subdocument-info-private.h" #include "private/modulemd-translation-private.h" #include "private/modulemd-yaml.h" #include "private/modulemd-util.h" #include <errno.h> #include <glib.h> #include <locale.h> #include <stdlib.h> #include <stdio.h> #include <string.h> enum mmd_verbosity { MMD_QUIET = -1, MMD_DEFAULT, MMD_VERBOSE, MMD_DEBUG }; /* Identifiers for modulemd type-version documents. * We cannot use GTypes, e.g. MODULEMD_TYPE_PACKAGER_V3, because * modulemd-packager-v2 GType does not exist (the format is parsed into * a modulemd-v2 data structure). Also GTypes are not constants. They are * a run-time computed value and hence cannot be used in switch-cases. */ enum mmd_type { MMD_TYPE_INDEX, /* Untyped validation by loading into an index */ MMD_TYPE_MODULEMD_V1, MMD_TYPE_MODULEMD_V2, MMD_TYPE_MODULEMD_DEFAULTS_V1, MMD_TYPE_MODULEMD_OBSOLETES_V1, MMD_TYPE_MODULEMD_PACKAGER_V2, MMD_TYPE_MODULEMD_PACKAGER_V3, MMD_TYPE_MODULEMD_TRANSLATIONS_V1 }; struct validator_options { enum mmd_verbosity verbosity; GType type; gchar **filenames; gboolean report_version; }; struct validator_options options = { 0 }; static gboolean set_report_version (const gchar *UNUSED (option_name), const gchar *UNUSED (value), gpointer UNUSED (data), GError **UNUSED (error)) { options.report_version = TRUE; return TRUE; } static gboolean set_verbosity (const gchar *option_name, const gchar *UNUSED (value), gpointer UNUSED (data), GError **error) { g_autofree gchar *debugging_env = NULL; if (g_strcmp0 ("-v", option_name) == 0 || g_strcmp0 ("--verbose", option_name) == 0) { if (options.verbosity < MMD_VERBOSE) { options.verbosity = MMD_VERBOSE; } } else if (g_strcmp0 ("--debug", option_name) == 0) { if (options.verbosity < MMD_DEBUG) { options.verbosity = MMD_DEBUG; const gchar *old_debug = g_getenv ("G_MESSAGES_DEBUG"); if (old_debug != NULL) { debugging_env = g_strdup_printf ("%s,%s", old_debug, G_LOG_DOMAIN); } else { debugging_env = g_strdup (G_LOG_DOMAIN); } if (!g_setenv ("G_MESSAGES_DEBUG", debugging_env, TRUE)) { g_set_error ( error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Could not set G_MESSAGES_DEBUG environment variable"); return FALSE; } } } else if (g_strcmp0 ("-q", option_name) == 0 || g_strcmp0 ("--quiet", option_name) == 0) { options.verbosity = MMD_QUIET; } else { /* We shouldn't be called under any other circumstance */ g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Called for unknown option \"%s\"", option_name); return FALSE; } return TRUE; } static gboolean set_type (const gchar *UNUSED (option_name), const gchar *value, gpointer UNUSED (data), GError **error) { if (!g_strcmp0 (value, "modulemd-v1")) options.type = MMD_TYPE_MODULEMD_V1; else if (!g_strcmp0 (value, "modulemd-v2")) options.type = MMD_TYPE_MODULEMD_V2; else if (!g_strcmp0 (value, "modulemd-defaults-v1")) options.type = MMD_TYPE_MODULEMD_DEFAULTS_V1; else if (!g_strcmp0 (value, "modulemd-obsoletes-v1")) options.type = MMD_TYPE_MODULEMD_OBSOLETES_V1; else if (!g_strcmp0 (value, "modulemd-packager-v2")) options.type = MMD_TYPE_MODULEMD_PACKAGER_V2; else if (!g_strcmp0 (value, "modulemd-packager-v3")) options.type = MMD_TYPE_MODULEMD_PACKAGER_V3; else if (!g_strcmp0 (value, "modulemd-translations-v1")) options.type = MMD_TYPE_MODULEMD_TRANSLATIONS_V1; else { g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Unknown document type: %s", value); return FALSE; } return TRUE; } static const gchar * mmd_type2astring (enum mmd_type type) { switch (type) { case MMD_TYPE_INDEX: return "an index"; case MMD_TYPE_MODULEMD_V1: return "a modulemd-v1"; case MMD_TYPE_MODULEMD_V2: return "a modulemd-v2"; case MMD_TYPE_MODULEMD_DEFAULTS_V1: return "a modulemd-defaults-v1"; case MMD_TYPE_MODULEMD_OBSOLETES_V1: return "a modulemd-obsoletes-v1"; case MMD_TYPE_MODULEMD_PACKAGER_V2: return "a modulemd-packager-v2"; case MMD_TYPE_MODULEMD_PACKAGER_V3: return "a modulemd-packager-v3"; case MMD_TYPE_MODULEMD_TRANSLATIONS_V1: return "a modulemd-translations-v1"; } return "an unknown document type"; } // clang-format off static GOptionEntry entries[] = { { "debug", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, set_verbosity, "Output debugging messages", NULL }, { "type", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, set_type, "Constrain a document type (modulemd-v1, modulemd-v2, modulemd-defaults-v1, modulemd-obsoletes-v1, modulemd-packager-v2, modulemd-packager-v3, modulemd-translations-v1); by default any document type loadable into a modulemd index is acceptable; this option only supports single-document files", "TYPE" }, { "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, set_verbosity, "Print no output", NULL }, { "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, set_verbosity, "Be verbose", NULL }, { "version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, set_report_version, "Print version number, then exit", NULL }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &options.filenames, "Files to be validated", NULL }, { 0 } }; // clang-format on static const gchar * ModulemdYamlDocumentTypeEnum2string (ModulemdYamlDocumentTypeEnum type) { switch (type) { case MODULEMD_YAML_DOC_MODULESTREAM: return "modulemd"; case MODULEMD_YAML_DOC_DEFAULTS: return "modulemd-defaults"; case MODULEMD_YAML_DOC_TRANSLATIONS: return "modulemd-translations"; case MODULEMD_YAML_DOC_PACKAGER: return "modulemd-packager"; case MODULEMD_YAML_DOC_OBSOLETES: return "modulemd-obsoletes"; case MODULEMD_YAML_DOC_UNKNOWN: /* fall through */ default: return "unknown type"; } } /* We cannot load by index as it converts from old versions before * a return and as it does not provide enumeration functions for * subdocuments. We will use private modulemd_defaults_v1_parse_yaml() etc. * parsers. */ static gboolean parse_file_as_subdoc_and_validate (const gchar *filename, enum mmd_type validation_type, ModulemdYamlDocumentTypeEnum expected_type, guint64 expected_version, GError **error) { g_autoptr (FILE) file = NULL; MMD_INIT_YAML_PARSER (parser); MMD_INIT_YAML_EVENT (event); g_autoptr (ModulemdSubdocumentInfo) subdoc = NULL; const GError *subdoc_error; ModulemdYamlDocumentTypeEnum type; guint64 version; g_autoptr (GObject) object = NULL; /* Open the file and determine a document type */ file = fopen (filename, "r"); if (!file) { g_set_error (error, MODULEMD_YAML_ERROR, MMD_YAML_ERROR_OPEN, "Could not open %s file: %s", filename, strerror (errno)); return FALSE; } yaml_parser_set_input_file (&parser, file); if (!yaml_parser_parse (&parser, &event)) { MMD_YAML_ERROR_EVENT_EXIT_BOOL (error, event, "Invalid YAML"); } if (event.type != YAML_STREAM_START_EVENT) { { MMD_YAML_ERROR_EVENT_EXIT_BOOL ( error, event, "YAML parser could not find a start of a YAML stream"); } } yaml_event_delete (&event); if (!yaml_parser_parse (&parser, &event)) { MMD_YAML_ERROR_EVENT_EXIT_BOOL (error, event, "Invalid YAML"); } if (event.type != YAML_DOCUMENT_START_EVENT) { { MMD_YAML_ERROR_EVENT_EXIT_BOOL ( error, event, "YAML parser could not find a start of a YAML document"); } } yaml_event_delete (&event); subdoc = modulemd_yaml_parse_document_type (&parser); subdoc_error = modulemd_subdocument_info_get_gerror (subdoc); if (subdoc_error) { *error = g_error_copy (subdoc_error); return FALSE; } type = modulemd_subdocument_info_get_doctype (subdoc); if (type != expected_type) { g_set_error (error, MODULEMD_ERROR, 0, "Not %s document; it is %s", mmd_type2astring (validation_type), ModulemdYamlDocumentTypeEnum2string (type)); return FALSE; } version = modulemd_subdocument_info_get_mdversion (subdoc); if (version != expected_version) { g_set_error (error, MODULEMD_ERROR, 0, "Not %s document; it is %" G_GUINT64_FORMAT " version", mmd_type2astring (validation_type), version); return FALSE; } /* Parse and validate the document body */ switch (validation_type) { case MMD_TYPE_MODULEMD_V1: object = G_OBJECT (modulemd_module_stream_v1_parse_yaml (subdoc, TRUE, error)); if (object && !modulemd_module_stream_validate ( MODULEMD_MODULE_STREAM (object), error)) g_clear_object (&object); break; case MMD_TYPE_MODULEMD_DEFAULTS_V1: object = G_OBJECT (modulemd_defaults_v1_parse_yaml (subdoc, TRUE, error)); /* validated implicitly */ break; case MMD_TYPE_MODULEMD_OBSOLETES_V1: object = G_OBJECT (modulemd_obsoletes_parse_yaml (subdoc, TRUE, error)); /* validated implicitly */ break; case MMD_TYPE_MODULEMD_PACKAGER_V2: object = G_OBJECT ( modulemd_module_stream_v2_parse_yaml (subdoc, TRUE, TRUE, error)); if (object && !modulemd_module_stream_validate ( MODULEMD_MODULE_STREAM (object), error)) g_clear_object (&object); break; case MMD_TYPE_MODULEMD_TRANSLATIONS_V1: object = G_OBJECT (modulemd_translation_parse_yaml (subdoc, TRUE, error)); /* validated implicitly */ break; default: g_set_error (error, MODULEMD_ERROR, 0, "Internal error: %s type is not supported", mmd_type2astring (validation_type)); return FALSE; } if (!object) return FALSE; /* Check for a garbage past the first document */ if (!yaml_parser_parse (&parser, &event)) { MMD_YAML_ERROR_EVENT_EXIT_BOOL ( error, event, "Invalid YAML after first document"); } if (event.type != YAML_STREAM_END_EVENT) { { MMD_YAML_ERROR_EVENT_EXIT_BOOL ( error, event, "Another YAML document after the first one"); return FALSE; } } yaml_event_delete (&event); return TRUE; } static gboolean parse_file (const gchar *filename, GPtrArray **failures, GError **error) { if (options.verbosity >= MMD_VERBOSE) { g_fprintf (stdout, "Validating %s\n", filename); } switch (options.type) { case MMD_TYPE_INDEX: { g_autoptr (ModulemdModuleIndex) index = NULL; index = modulemd_module_index_new (); return modulemd_module_index_update_from_file_ext ( index, filename, TRUE, TRUE, failures, error); } case MMD_TYPE_MODULEMD_V1: return parse_file_as_subdoc_and_validate ( filename, options.type, MODULEMD_YAML_DOC_MODULESTREAM, 1u, error); case MMD_TYPE_MODULEMD_V2: { GType type; g_autoptr (GObject) object = NULL; type = modulemd_read_packager_file (filename, &object, error); if (type == G_TYPE_INVALID) return FALSE; if (type != MODULEMD_TYPE_MODULE_STREAM_V2) { g_set_error (error, MODULEMD_ERROR, 0, "Not a modulemd-v2 document; it is %s", g_type_name (type)); return FALSE; } return modulemd_module_stream_validate ( MODULEMD_MODULE_STREAM (object), error); } case MMD_TYPE_MODULEMD_DEFAULTS_V1: return parse_file_as_subdoc_and_validate ( filename, options.type, MODULEMD_YAML_DOC_DEFAULTS, 1u, error); case MMD_TYPE_MODULEMD_OBSOLETES_V1: return parse_file_as_subdoc_and_validate ( filename, options.type, MODULEMD_YAML_DOC_OBSOLETES, 1u, error); case MMD_TYPE_MODULEMD_PACKAGER_V2: return parse_file_as_subdoc_and_validate ( filename, options.type, MODULEMD_YAML_DOC_PACKAGER, 2u, error); case MMD_TYPE_MODULEMD_PACKAGER_V3: { GType type; g_autoptr (GObject) object = NULL; type = modulemd_read_packager_file (filename, &object, error); if (type == G_TYPE_INVALID) return FALSE; if (type != MODULEMD_TYPE_PACKAGER_V3) { g_set_error (error, MODULEMD_ERROR, 0, "Not a modulemd-packager-v3 document; it is %s", g_type_name (type)); return FALSE; } /* modulemd_packager_v3 is validated implicitly by * modulemd_read_packager_file (). */ return TRUE; } case MMD_TYPE_MODULEMD_TRANSLATIONS_V1: return parse_file_as_subdoc_and_validate ( filename, options.type, MODULEMD_YAML_DOC_TRANSLATIONS, 1u, error); } g_fprintf (stderr, "Internal error: unsupported document type: %s\n", mmd_type2astring (options.type)); exit (EXIT_FAILURE); } int main (int argc, char *argv[]) { const char *filename; g_autoptr (GOptionContext) context = NULL; g_autoptr (GError) error = NULL; gsize num_invalid = 0; gboolean ret; g_autoptr (GPtrArray) failures = NULL; ModulemdSubdocumentInfo *doc = NULL; setlocale (LC_ALL, ""); options.type = MMD_TYPE_INDEX; context = g_option_context_new ("FILES - Simple modulemd YAML validator"); g_option_context_add_main_entries (context, entries, "modulemd-validator"); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_fprintf (stderr, "option parsing failed: %s\n", error->message); exit (EXIT_FAILURE); } if (options.report_version) { g_fprintf (stdout, "modulemd-validator %s\n", modulemd_get_version ()); exit (EXIT_SUCCESS); } if (!(options.filenames && options.filenames[0])) { g_fprintf (stderr, "At least one file must be specified on the command-line\n"); exit (EXIT_FAILURE); } for (gsize i = 0; options.filenames[i]; i++) { filename = options.filenames[i]; ret = parse_file (filename, &failures, &error); if (!ret) { num_invalid++; if (options.verbosity >= MMD_DEFAULT) { g_fprintf (stderr, "%s failed to validate\n", filename); if (error != NULL) { /* Unparseable content */ g_fprintf (stderr, "%s could not be read in its entirety: %s\n", filename, error->message); } if (failures) { for (gsize j = 0; j < failures->len; j++) { doc = MODULEMD_SUBDOCUMENT_INFO ( g_ptr_array_index (failures, j)); g_printf ( "\nFailed subdocument (%s): \n%s\n", modulemd_subdocument_info_get_gerror (doc)->message, modulemd_subdocument_info_get_yaml (doc)); } } } } else { if (options.verbosity >= MMD_DEFAULT) { g_printf ("%s validated successfully\n", filename); } } g_clear_error (&error); g_clear_pointer (&failures, g_ptr_array_unref); } return num_invalid; }