1*592efe25SPierre Pronchery /*
2*592efe25SPierre Pronchery * SPDX-License-Identifier: BSD-2-Clause
3*592efe25SPierre Pronchery *
4*592efe25SPierre Pronchery * Copyright (c) 2025 The FreeBSD Foundation
5*592efe25SPierre Pronchery *
6*592efe25SPierre Pronchery * Portions of this software were developed by
7*592efe25SPierre Pronchery * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
8*592efe25SPierre Pronchery * the FreeBSD Foundation
9*592efe25SPierre Pronchery */
10*592efe25SPierre Pronchery
11*592efe25SPierre Pronchery #include "libpkgconf/config.h"
12*592efe25SPierre Pronchery #include <libpkgconf/stdinc.h>
13*592efe25SPierre Pronchery #include <libpkgconf/libpkgconf.h>
14*592efe25SPierre Pronchery #include "getopt_long.h"
15*592efe25SPierre Pronchery #include "util.h"
16*592efe25SPierre Pronchery #include "core.h"
17*592efe25SPierre Pronchery #include "software.h"
18*592efe25SPierre Pronchery #include "serialize.h"
19*592efe25SPierre Pronchery #include "simplelicensing.h"
20*592efe25SPierre Pronchery #include "generate.h"
21*592efe25SPierre Pronchery
22*592efe25SPierre Pronchery #define PKG_VERSION (((uint64_t) 1) << 1)
23*592efe25SPierre Pronchery #define PKG_ABOUT (((uint64_t) 1) << 2)
24*592efe25SPierre Pronchery #define PKG_HELP (((uint64_t) 1) << 3)
25*592efe25SPierre Pronchery
26*592efe25SPierre Pronchery static const char *spdx_version = "3.0.1";
27*592efe25SPierre Pronchery static const char *bom_license = "CC0-1.0";
28*592efe25SPierre Pronchery static const char *xsd_any_url_default_base = "https://github.com/pkgconf/pkgconf";
29*592efe25SPierre Pronchery static const char *xsd_any_uri_default_base = "github.com:pkgconf:pkgconf";
30*592efe25SPierre Pronchery static int maximum_traverse_depth = 2000;
31*592efe25SPierre Pronchery
32*592efe25SPierre Pronchery static pkgconf_client_t pkg_client;
33*592efe25SPierre Pronchery static uint64_t want_flags;
34*592efe25SPierre Pronchery // static int maximum_traverse_depth = 2000;
35*592efe25SPierre Pronchery static FILE *error_msgout = NULL;
36*592efe25SPierre Pronchery static FILE *sbom_out = NULL;
37*592efe25SPierre Pronchery
38*592efe25SPierre Pronchery static const char *
environ_lookup_handler(const pkgconf_client_t * client,const char * key)39*592efe25SPierre Pronchery environ_lookup_handler(const pkgconf_client_t *client, const char *key)
40*592efe25SPierre Pronchery {
41*592efe25SPierre Pronchery (void) client;
42*592efe25SPierre Pronchery
43*592efe25SPierre Pronchery return getenv(key);
44*592efe25SPierre Pronchery }
45*592efe25SPierre Pronchery
46*592efe25SPierre Pronchery static bool
error_handler(const char * msg,const pkgconf_client_t * client,void * data)47*592efe25SPierre Pronchery error_handler(const char *msg, const pkgconf_client_t *client, void *data)
48*592efe25SPierre Pronchery {
49*592efe25SPierre Pronchery (void) client;
50*592efe25SPierre Pronchery (void) data;
51*592efe25SPierre Pronchery if (!pkgconf_output_file_fmt(error_msgout, "%s", msg))
52*592efe25SPierre Pronchery {
53*592efe25SPierre Pronchery pkgconf_error(client, "spdxtool: Could not output error message: %s", strerror(errno));
54*592efe25SPierre Pronchery return false;
55*592efe25SPierre Pronchery }
56*592efe25SPierre Pronchery return true;
57*592efe25SPierre Pronchery }
58*592efe25SPierre Pronchery
59*592efe25SPierre Pronchery static int
version(void)60*592efe25SPierre Pronchery version(void)
61*592efe25SPierre Pronchery {
62*592efe25SPierre Pronchery printf("spdxtool %s\n", PACKAGE_VERSION);
63*592efe25SPierre Pronchery return EXIT_SUCCESS;
64*592efe25SPierre Pronchery }
65*592efe25SPierre Pronchery
66*592efe25SPierre Pronchery static int
about(void)67*592efe25SPierre Pronchery about(void)
68*592efe25SPierre Pronchery {
69*592efe25SPierre Pronchery printf("spdxtool (%s %s)\n\n", PACKAGE_NAME, PACKAGE_VERSION);
70*592efe25SPierre Pronchery printf("SPDX-License-Identifier: BSD-2-Clause\n\n");
71*592efe25SPierre Pronchery printf("Copyright (c) 2025 The FreeBSD Foundation\n\n");
72*592efe25SPierre Pronchery printf("Portions of this software were developed by\n");
73*592efe25SPierre Pronchery printf("Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from\n");
74*592efe25SPierre Pronchery printf("the FreeBSD Foundation\n\n");
75*592efe25SPierre Pronchery printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT);
76*592efe25SPierre Pronchery return EXIT_SUCCESS;
77*592efe25SPierre Pronchery }
78*592efe25SPierre Pronchery
79*592efe25SPierre Pronchery static int
usage(void)80*592efe25SPierre Pronchery usage(void)
81*592efe25SPierre Pronchery {
82*592efe25SPierre Pronchery printf("usage: spdxtool [modules]\n");
83*592efe25SPierre Pronchery
84*592efe25SPierre Pronchery printf("\nOptions:\n");
85*592efe25SPierre Pronchery
86*592efe25SPierre Pronchery printf(" --agent-name Set agent name [default: 'Default']\n");
87*592efe25SPierre Pronchery printf(" --creation-time Use string as creation time (Should be in ISO8601 format) [default: current time]\n");
88*592efe25SPierre Pronchery printf(" --creation-id Use string as creation id [default: '_:creationinfo_1']\n");
89*592efe25SPierre Pronchery printf(" --help this message\n");
90*592efe25SPierre Pronchery printf(" --about print bomtool version and license to stdout\n");
91*592efe25SPierre Pronchery printf(" --version print bomtool version to stdout\n");
92*592efe25SPierre Pronchery printf(" --output FILE output SBOM data to file\n");
93*592efe25SPierre Pronchery printf(" --spdx-base-id URL Uset string as base of SPDX ids [default: %s]\n", xsd_any_uri_default_base);
94*592efe25SPierre Pronchery printf(" --use-uri Use URIs not URLs as SPDX id");
95*592efe25SPierre Pronchery printf(" --define-variable=varname=value define variable global 'varname' as 'value'\n");
96*592efe25SPierre Pronchery
97*592efe25SPierre Pronchery return EXIT_SUCCESS;
98*592efe25SPierre Pronchery }
99*592efe25SPierre Pronchery
100*592efe25SPierre Pronchery int
main(int argc,char * argv[])101*592efe25SPierre Pronchery main(int argc, char *argv[])
102*592efe25SPierre Pronchery {
103*592efe25SPierre Pronchery int ret = EXIT_SUCCESS;
104*592efe25SPierre Pronchery pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER;
105*592efe25SPierre Pronchery unsigned int want_client_flags = PKGCONF_PKG_PKGF_SEARCH_PRIVATE;
106*592efe25SPierre Pronchery pkgconf_cross_personality_t *personality = pkgconf_cross_personality_default();
107*592efe25SPierre Pronchery char *creation_time = NULL;
108*592efe25SPierre Pronchery char *creation_id = NULL;
109*592efe25SPierre Pronchery char *agent_name = NULL;
110*592efe25SPierre Pronchery char world_id[] = "virtual:world";
111*592efe25SPierre Pronchery char world_realname[] = "virtual world package";
112*592efe25SPierre Pronchery const char *spdx_id_base = xsd_any_url_default_base;
113*592efe25SPierre Pronchery bool colon_sep = false;
114*592efe25SPierre Pronchery pkgconf_pkg_t world =
115*592efe25SPierre Pronchery {
116*592efe25SPierre Pronchery .id = world_id,
117*592efe25SPierre Pronchery .realname = world_realname,
118*592efe25SPierre Pronchery .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
119*592efe25SPierre Pronchery };
120*592efe25SPierre Pronchery
121*592efe25SPierre Pronchery error_msgout = stderr;
122*592efe25SPierre Pronchery sbom_out = stdout;
123*592efe25SPierre Pronchery
124*592efe25SPierre Pronchery struct pkg_option options[] =
125*592efe25SPierre Pronchery {
126*592efe25SPierre Pronchery { "agent-name", required_argument, NULL, 100, },
127*592efe25SPierre Pronchery { "creation-time", required_argument, NULL, 101, },
128*592efe25SPierre Pronchery { "creation-id", required_argument, NULL, 102, },
129*592efe25SPierre Pronchery { "version", no_argument, &want_flags, PKG_VERSION, },
130*592efe25SPierre Pronchery { "about", no_argument, &want_flags, PKG_ABOUT, },
131*592efe25SPierre Pronchery { "help", no_argument, &want_flags, PKG_HELP, },
132*592efe25SPierre Pronchery { "output", required_argument, NULL, 103, },
133*592efe25SPierre Pronchery { "spdx-base-id", required_argument, NULL, 104, },
134*592efe25SPierre Pronchery { "use-uri", no_argument, NULL, 105, },
135*592efe25SPierre Pronchery { "define-variable", required_argument, NULL, 106, },
136*592efe25SPierre Pronchery { NULL, 0, NULL, 0 }
137*592efe25SPierre Pronchery };
138*592efe25SPierre Pronchery
139*592efe25SPierre Pronchery while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1)
140*592efe25SPierre Pronchery {
141*592efe25SPierre Pronchery switch (ret)
142*592efe25SPierre Pronchery {
143*592efe25SPierre Pronchery case 100:
144*592efe25SPierre Pronchery agent_name = pkg_optarg;
145*592efe25SPierre Pronchery break;
146*592efe25SPierre Pronchery case 101:
147*592efe25SPierre Pronchery creation_time = pkg_optarg;
148*592efe25SPierre Pronchery break;
149*592efe25SPierre Pronchery case 102:
150*592efe25SPierre Pronchery creation_id = pkg_optarg;
151*592efe25SPierre Pronchery break;
152*592efe25SPierre Pronchery case 103:
153*592efe25SPierre Pronchery sbom_out = fopen(pkg_optarg, "w");
154*592efe25SPierre Pronchery if (sbom_out == NULL)
155*592efe25SPierre Pronchery {
156*592efe25SPierre Pronchery pkgconf_output_file_fmt(stderr, "unable to open %s: %s\n", pkg_optarg, strerror(errno));
157*592efe25SPierre Pronchery return EXIT_FAILURE;
158*592efe25SPierre Pronchery }
159*592efe25SPierre Pronchery break;
160*592efe25SPierre Pronchery case 104:
161*592efe25SPierre Pronchery spdx_id_base = pkg_optarg;
162*592efe25SPierre Pronchery break;
163*592efe25SPierre Pronchery case 105:
164*592efe25SPierre Pronchery // If SPDX id base have not been altered use default
165*592efe25SPierre Pronchery if (!strcmp(spdx_id_base, xsd_any_url_default_base))
166*592efe25SPierre Pronchery spdx_id_base = xsd_any_uri_default_base;
167*592efe25SPierre Pronchery colon_sep = true;
168*592efe25SPierre Pronchery break;
169*592efe25SPierre Pronchery case 106:
170*592efe25SPierre Pronchery pkgconf_tuple_define_global(&pkg_client, pkg_optarg);
171*592efe25SPierre Pronchery break;
172*592efe25SPierre Pronchery case '?':
173*592efe25SPierre Pronchery case ':':
174*592efe25SPierre Pronchery return EXIT_FAILURE;
175*592efe25SPierre Pronchery default:
176*592efe25SPierre Pronchery break;
177*592efe25SPierre Pronchery }
178*592efe25SPierre Pronchery }
179*592efe25SPierre Pronchery
180*592efe25SPierre Pronchery pkgconf_client_init(&pkg_client, error_handler, NULL, personality, NULL, environ_lookup_handler);
181*592efe25SPierre Pronchery
182*592efe25SPierre Pronchery /* we have determined what features we want most likely. in some cases, we override later. */
183*592efe25SPierre Pronchery pkgconf_client_set_flags(&pkg_client, want_client_flags);
184*592efe25SPierre Pronchery
185*592efe25SPierre Pronchery /* at this point, want_client_flags should be set, so build the dir list */
186*592efe25SPierre Pronchery pkgconf_client_dir_list_build(&pkg_client, personality);
187*592efe25SPierre Pronchery
188*592efe25SPierre Pronchery
189*592efe25SPierre Pronchery if ((want_flags & PKG_ABOUT) == PKG_ABOUT)
190*592efe25SPierre Pronchery return about();
191*592efe25SPierre Pronchery
192*592efe25SPierre Pronchery if ((want_flags & PKG_VERSION) == PKG_VERSION)
193*592efe25SPierre Pronchery return version();
194*592efe25SPierre Pronchery
195*592efe25SPierre Pronchery if ((want_flags & PKG_HELP) == PKG_HELP)
196*592efe25SPierre Pronchery return usage();
197*592efe25SPierre Pronchery
198*592efe25SPierre Pronchery /* Join the remaining arguments into a single query string, as the main
199*592efe25SPierre Pronchery * pkgconf CLI does, and let the dependency parser handle module names,
200*592efe25SPierre Pronchery * comparison operators and versions.
201*592efe25SPierre Pronchery */
202*592efe25SPierre Pronchery pkgconf_buffer_t queryparams = PKGCONF_BUFFER_INITIALIZER;
203*592efe25SPierre Pronchery
204*592efe25SPierre Pronchery while (pkg_optind < argc && argv[pkg_optind] != NULL)
205*592efe25SPierre Pronchery {
206*592efe25SPierre Pronchery if (pkgconf_buffer_len(&queryparams) > 0)
207*592efe25SPierre Pronchery pkgconf_buffer_push_byte(&queryparams, ' ');
208*592efe25SPierre Pronchery
209*592efe25SPierre Pronchery pkgconf_buffer_append(&queryparams, argv[pkg_optind]);
210*592efe25SPierre Pronchery pkg_optind++;
211*592efe25SPierre Pronchery }
212*592efe25SPierre Pronchery
213*592efe25SPierre Pronchery if (pkgconf_buffer_len(&queryparams) > 0)
214*592efe25SPierre Pronchery pkgconf_queue_push(&pkgq, pkgconf_buffer_str(&queryparams));
215*592efe25SPierre Pronchery
216*592efe25SPierre Pronchery pkgconf_buffer_finalize(&queryparams);
217*592efe25SPierre Pronchery
218*592efe25SPierre Pronchery if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth))
219*592efe25SPierre Pronchery {
220*592efe25SPierre Pronchery ret = EXIT_FAILURE;
221*592efe25SPierre Pronchery goto out;
222*592efe25SPierre Pronchery }
223*592efe25SPierre Pronchery
224*592efe25SPierre Pronchery spdxtool_util_set_uri_root(&pkg_client, spdx_id_base);
225*592efe25SPierre Pronchery spdxtool_util_set_uri_separator_colon(&pkg_client, colon_sep);
226*592efe25SPierre Pronchery spdxtool_util_set_spdx_license(&pkg_client, bom_license);
227*592efe25SPierre Pronchery spdxtool_util_set_spdx_version(&pkg_client, spdx_version);
228*592efe25SPierre Pronchery
229*592efe25SPierre Pronchery if (!spdxtool_generate(&pkg_client, &world, sbom_out, maximum_traverse_depth, creation_time, creation_id, agent_name))
230*592efe25SPierre Pronchery {
231*592efe25SPierre Pronchery ret = EXIT_FAILURE;
232*592efe25SPierre Pronchery goto out;
233*592efe25SPierre Pronchery }
234*592efe25SPierre Pronchery
235*592efe25SPierre Pronchery ret = EXIT_SUCCESS;
236*592efe25SPierre Pronchery
237*592efe25SPierre Pronchery out:
238*592efe25SPierre Pronchery pkgconf_solution_free(&pkg_client, &world);
239*592efe25SPierre Pronchery pkgconf_queue_free(&pkgq);
240*592efe25SPierre Pronchery pkgconf_cross_personality_deinit(personality);
241*592efe25SPierre Pronchery pkgconf_client_deinit(&pkg_client);
242*592efe25SPierre Pronchery
243*592efe25SPierre Pronchery return ret;
244*592efe25SPierre Pronchery }
245