diff options
Diffstat (limited to 'cli/bomtool/main.c')
| -rw-r--r-- | cli/bomtool/main.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/cli/bomtool/main.c b/cli/bomtool/main.c new file mode 100644 index 000000000000..1e8391aa5ae9 --- /dev/null +++ b/cli/bomtool/main.c @@ -0,0 +1,368 @@ +/* + * bomtool/main.c + * main() routine, printer functions + * + * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + * pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include "libpkgconf/config.h" +#include <libpkgconf/stdinc.h> +#include <libpkgconf/libpkgconf.h> +#include "getopt_long.h" + +#define PKG_VERSION (((uint64_t) 1) << 1) +#define PKG_ABOUT (((uint64_t) 1) << 2) +#define PKG_HELP (((uint64_t) 1) << 3) + +static const char *spdx_version = "SPDX-2.2"; +static const char *bom_license = "CC0-1.0"; +static const char *document_ref = "SPDXRef-DOCUMENT"; + +static pkgconf_client_t pkg_client; +static uint64_t want_flags; +static size_t maximum_package_count = 0; +static int maximum_traverse_depth = 2000; +FILE *error_msgout = NULL; + +static bool +error_handler(const char *msg, const pkgconf_client_t *client, void *data) +{ + (void) client; + (void) data; + fprintf(error_msgout, "%s", msg); + return true; +} + +static const char * +sbom_spdx_identity(pkgconf_pkg_t *pkg) +{ + static char buf[PKGCONF_ITEM_SIZE]; + + snprintf(buf, sizeof buf, "%sC64%s", pkg->id, pkg->version); + + return buf; +} + +static const char * +sbom_name(pkgconf_pkg_t *world) +{ + static char buf[PKGCONF_BUFSIZE]; + pkgconf_node_t *node; + + pkgconf_strlcpy(buf, "SBOM-SPDX", sizeof buf); + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if ((dep->flags & PKGCONF_PKG_DEPF_QUERY) != PKGCONF_PKG_DEPF_QUERY) + continue; + + if (!dep->match) + continue; + + pkgconf_strlcat(buf, "-", sizeof buf); + pkgconf_strlcat(buf, sbom_spdx_identity(match), sizeof buf); + } + + return buf; +} + +static void +write_sbom_header(pkgconf_client_t *client, pkgconf_pkg_t *world) +{ + (void) client; + (void) world; + + printf("SPDXVersion: %s\n", spdx_version); + printf("DataLicense: %s\n", bom_license); + printf("SPDXID: %s\n", document_ref); + printf("DocumentName: %s\n", sbom_name(world)); + printf("DocumentNamespace: https://spdx.org/spdxdocs/bomtool-%s\n", PACKAGE_VERSION); + printf("Creator: Tool: bomtool %s\n", PACKAGE_VERSION); + + printf("\n\n"); +} + +static const char * +sbom_identity(pkgconf_pkg_t *pkg) +{ + static char buf[PKGCONF_ITEM_SIZE]; + + snprintf(buf, sizeof buf, "%s@%s", pkg->id, pkg->version); + + return buf; +} + +static void +write_sbom_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused) +{ + (void) client; + (void) unused; + + if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + printf("##### Package: %s\n\n", sbom_identity(pkg)); + + printf("PackageName: %s\n", sbom_identity(pkg)); + printf("SPDXID: SPDXRef-Package-%s\n", sbom_spdx_identity(pkg)); + printf("PackageVersion: %s\n", pkg->version); + printf("PackageDownloadLocation: NOASSERTION\n"); + printf("PackageVerificationCode: NOASSERTION\n"); + + /* XXX: What about projects? */ + if (pkg->maintainer != NULL) + printf("PackageSupplier: Person: %s\n", pkg->maintainer); + + if (pkg->url != NULL) + printf("PackageHomePage: %s\n", pkg->url); + + printf("PackageLicenseDeclared: %s\n", pkg->license != NULL ? pkg->license : "NOASSERTION"); + + if (pkg->copyright != NULL) + printf("PackageCopyrightText: <text>%s</text>\n", pkg->copyright); + + if (pkg->description != NULL) + printf("PackageSummary: <text>%s</text>\n", pkg->description); + + printf("\n\n"); +} + +static void +write_sbom_relationships(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused) +{ + (void) client; + (void) unused; + + char baseref[PKGCONF_ITEM_SIZE]; + pkgconf_node_t *node; + + if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + snprintf(baseref, sizeof baseref, "SPDXRef-Package-%sC64%s", pkg->id, pkg->version); + + PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if (!dep->match) + continue; + + printf("Relationship: %s DEPENDS_ON SPDXRef-Package-%s\n", baseref, sbom_spdx_identity(match)); + printf("Relationship: SPDXRef-Package-%s DEPENDENCY_OF %s\n", sbom_spdx_identity(match), baseref); + } + + PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if (!dep->match) + continue; + + printf("Relationship: %s DEPENDS_ON SPDXRef-Package-%s\n", baseref, sbom_spdx_identity(match)); + printf("Relationship: SPDXRef-Package-%s DEV_DEPENDENCY_OF %s\n", sbom_spdx_identity(match), baseref); + } + + if (pkg->required.head != NULL || pkg->requires_private.head != NULL) + printf("\n\n"); +} + +static bool +generate_sbom_from_world(pkgconf_client_t *client, pkgconf_pkg_t *world) +{ + int eflag; + pkgconf_node_t *node; + + write_sbom_header(client, world); + + eflag = pkgconf_pkg_traverse(client, world, write_sbom_package, NULL, maximum_traverse_depth, 0); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + eflag = pkgconf_pkg_traverse(client, world, write_sbom_relationships, NULL, maximum_traverse_depth, 0); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if (!dep->match) + continue; + + printf("Relationship: %s DESCRIBES SPDXRef-Package-%s\n", document_ref, sbom_spdx_identity(match)); + } + + return true; +} + +static int +version(void) +{ + printf("bomtool %s\n", PACKAGE_VERSION); + return EXIT_SUCCESS; +} + +static int +about(void) +{ + printf("bomtool (%s %s)\n", PACKAGE_NAME, PACKAGE_VERSION); + printf("Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021\n"); + printf(" pkgconf authors (see AUTHORS in documentation directory).\n\n"); + printf("Permission to use, copy, modify, and/or distribute this software for any\n"); + printf("purpose with or without fee is hereby granted, provided that the above\n"); + printf("copyright notice and this permission notice appear in all copies.\n\n"); + printf("This software is provided 'as is' and without any warranty, express or\n"); + printf("implied. In no event shall the authors be liable for any damages arising\n"); + printf("from the use of this software.\n\n"); + printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT); + return EXIT_SUCCESS; +} + +static int +usage(void) +{ + printf("usage: bomtool [--flags] [modules]\n"); + + printf("\nbasic options:\n\n"); + + printf(" --help this message\n"); + printf(" --about print bomtool version and license to stdout\n"); + printf(" --version print bomtool version to stdout\n"); + + return EXIT_SUCCESS; +} + +int +main(int argc, char *argv[]) +{ + int ret = EXIT_SUCCESS; + pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER; + unsigned int want_client_flags = PKGCONF_PKG_PKGF_SEARCH_PRIVATE; + pkgconf_cross_personality_t *personality = pkgconf_cross_personality_default(); + pkgconf_pkg_t world = { + .id = "virtual:world", + .realname = "virtual world package", + .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, + }; + + error_msgout = stderr; + + struct pkg_option options[] = { + { "version", no_argument, &want_flags, PKG_VERSION, }, + { "about", no_argument, &want_flags, PKG_ABOUT, }, + { "help", no_argument, &want_flags, PKG_HELP, }, + { NULL, 0, NULL, 0 } + }; + + while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1) + { + switch (ret) + { + case '?': + case ':': + return EXIT_FAILURE; + default: + break; + } + } + + pkgconf_client_init(&pkg_client, error_handler, NULL, personality); + + /* we have determined what features we want most likely. in some cases, we override later. */ + pkgconf_client_set_flags(&pkg_client, want_client_flags); + + /* at this point, want_client_flags should be set, so build the dir list */ + pkgconf_client_dir_list_build(&pkg_client, personality); + + if ((want_flags & PKG_ABOUT) == PKG_ABOUT) + return about(); + + if ((want_flags & PKG_VERSION) == PKG_VERSION) + return version(); + + if ((want_flags & PKG_HELP) == PKG_HELP) + return usage(); + + while (1) + { + const char *package = argv[pkg_optind]; + + if (package == NULL) + break; + + /* check if there is a limit to the number of packages allowed to be included, if so and we have hit + * the limit, stop adding packages to the queue. + */ + if (maximum_package_count > 0 && pkgq.length > maximum_package_count) + break; + + while (isspace((unsigned char)package[0])) + package++; + + /* skip empty packages */ + if (package[0] == '\0') { + pkg_optind++; + continue; + } + + if (argv[pkg_optind + 1] == NULL || !PKGCONF_IS_OPERATOR_CHAR(*(argv[pkg_optind + 1]))) + { + pkgconf_queue_push(&pkgq, package); + pkg_optind++; + } + else + { + char packagebuf[PKGCONF_BUFSIZE]; + + snprintf(packagebuf, sizeof packagebuf, "%s %s %s", package, argv[pkg_optind + 1], argv[pkg_optind + 2]); + pkg_optind += 3; + + pkgconf_queue_push(&pkgq, packagebuf); + } + } + + if (pkgq.head == NULL) + { + fprintf(stderr, "Please specify at least one package name on the command line.\n"); + ret = EXIT_FAILURE; + goto out; + } + + ret = EXIT_SUCCESS; + + if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth)) + { + ret = EXIT_FAILURE; + goto out; + } + + if (!generate_sbom_from_world(&pkg_client, &world)) + { + ret = EXIT_FAILURE; + goto out; + } + +out: + pkgconf_solution_free(&pkg_client, &world); + pkgconf_queue_free(&pkgq); + pkgconf_cross_personality_deinit(personality); + pkgconf_client_deinit(&pkg_client); + + return ret; +} |
