xref: /freebsd/contrib/pkgconf/cli/spdxtool/main.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
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