output.c revision 1.33
1/*	$OpenBSD: output.c,v 1.33 2024/02/22 12:49:42 job Exp $ */
2/*
3 * Copyright (c) 2019 Theo de Raadt <deraadt@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*-
19 * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
20 * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 *    notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 *    notice, this list of conditions and the following disclaimer in the
30 *    documentation and/or other materials provided with the distribution.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 */
44
45#include <sys/stat.h>
46
47#include <err.h>
48#include <fcntl.h>
49#include <unistd.h>
50#include <netdb.h>
51#include <signal.h>
52#include <string.h>
53#include <limits.h>
54#include <time.h>
55
56#include "extern.h"
57
58int		 outformats;
59
60static char	 output_tmpname[PATH_MAX];
61static char	 output_name[PATH_MAX];
62
63static const struct outputs {
64	int	 format;
65	char	*name;
66	int	(*fn)(FILE *, struct vrp_tree *, struct brk_tree *,
67		    struct vap_tree *, struct vsp_tree *, struct stats *);
68} outputs[] = {
69	{ FORMAT_OPENBGPD, "openbgpd", output_bgpd },
70	{ FORMAT_BIRD, "bird1v4", output_bird1v4 },
71	{ FORMAT_BIRD, "bird1v6", output_bird1v6 },
72	{ FORMAT_BIRD, "bird", output_bird2 },
73	{ FORMAT_CSV, "csv", output_csv },
74	{ FORMAT_JSON, "json", output_json },
75	{ FORMAT_OMETRIC, "metrics", output_ometric },
76	{ 0, NULL, NULL }
77};
78
79static FILE	*output_createtmp(char *);
80static void	 output_cleantmp(void);
81static int	 output_finish(FILE *);
82static void	 sig_handler(int);
83static void	 set_signal_handler(void);
84
85int
86outputfiles(struct vrp_tree *v, struct brk_tree *b, struct vap_tree *a,
87    struct vsp_tree *p, struct stats *st)
88{
89	int i, rc = 0;
90
91	atexit(output_cleantmp);
92	set_signal_handler();
93
94	for (i = 0; outputs[i].name; i++) {
95		FILE *fout;
96
97		if (!(outformats & outputs[i].format))
98			continue;
99
100		fout = output_createtmp(outputs[i].name);
101		if (fout == NULL) {
102			warn("cannot create %s", outputs[i].name);
103			rc = 1;
104			continue;
105		}
106		if ((*outputs[i].fn)(fout, v, b, a, p, st) != 0) {
107			warn("output for %s format failed", outputs[i].name);
108			fclose(fout);
109			output_cleantmp();
110			rc = 1;
111			continue;
112		}
113		if (output_finish(fout) != 0) {
114			warn("finish for %s format failed", outputs[i].name);
115			output_cleantmp();
116			rc = 1;
117			continue;
118		}
119	}
120
121	return rc;
122}
123
124static FILE *
125output_createtmp(char *name)
126{
127	FILE *f;
128	int fd, r;
129
130	if (strlcpy(output_name, name, sizeof output_name) >=
131	    sizeof output_name)
132		err(1, "path too long");
133	r = snprintf(output_tmpname, sizeof output_tmpname,
134	    "%s.XXXXXXXXXXX", output_name);
135	if (r < 0 || r > (int)sizeof(output_tmpname))
136		err(1, "path too long");
137	fd = mkostemp(output_tmpname, O_CLOEXEC);
138	if (fd == -1)
139		err(1, "mkostemp: %s", output_tmpname);
140	(void) fchmod(fd, 0644);
141	f = fdopen(fd, "w");
142	if (f == NULL)
143		err(1, "fdopen");
144	return f;
145}
146
147static int
148output_finish(FILE *out)
149{
150	if (fclose(out) != 0)
151		return -1;
152	if (rename(output_tmpname, output_name) == -1)
153		return -1;
154	output_tmpname[0] = '\0';
155	return 0;
156}
157
158static void
159output_cleantmp(void)
160{
161	if (*output_tmpname)
162		unlink(output_tmpname);
163	output_tmpname[0] = '\0';
164}
165
166/*
167 * Signal handler that clears the temporary files.
168 */
169static void
170sig_handler(int sig)
171{
172	output_cleantmp();
173	_exit(2);
174}
175
176/*
177 * Set signal handler on panic signals.
178 */
179static void
180set_signal_handler(void)
181{
182	struct sigaction sa;
183	int i, signals[] = {SIGTERM, SIGHUP, SIGINT, SIGUSR1, SIGUSR2,
184	    SIGPIPE, SIGXCPU, SIGXFSZ, 0};
185
186	memset(&sa, 0, sizeof(sa));
187	sigfillset(&sa.sa_mask);
188	sa.sa_flags = SA_RESTART;
189	sa.sa_handler = sig_handler;
190
191	for (i = 0; signals[i] != 0; i++) {
192		if (sigaction(signals[i], &sa, NULL) == -1) {
193			warn("sigaction(%s)", strsignal(signals[i]));
194			continue;
195		}
196	}
197}
198
199int
200outputheader(FILE *out, struct stats *st)
201{
202	char		hn[NI_MAXHOST], tbuf[80];
203	struct tm	*tp;
204	time_t		t;
205	int		i;
206
207	time(&t);
208	tp = gmtime(&t);
209	strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S UTC %Y", tp);
210
211	gethostname(hn, sizeof hn);
212
213	if (fprintf(out,
214	    "# Generated on host %s at %s\n"
215	    "# Processing time %lld seconds (%llds user, %llds system)\n"
216	    "# Route Origin Authorizations: %u (%u failed parse, %u invalid)\n"
217	    "# BGPsec Router Certificates: %u\n"
218	    "# Certificates: %u (%u invalid)\n",
219	    hn, tbuf, (long long)st->elapsed_time.tv_sec,
220	    (long long)st->user_time.tv_sec, (long long)st->system_time.tv_sec,
221	    st->repo_tal_stats.roas, st->repo_tal_stats.roas_fail,
222	    st->repo_tal_stats.roas_invalid, st->repo_tal_stats.brks,
223	    st->repo_tal_stats.certs, st->repo_tal_stats.certs_fail) < 0)
224		return -1;
225
226	if (fprintf(out,
227	    "# Trust Anchor Locators: %u (%u invalid) [", st->tals,
228	    talsz - st->tals) < 0)
229		return -1;
230	for (i = 0; i < talsz; i++)
231		if (fprintf(out, " %s", tals[i]) < 0)
232			return -1;
233
234	if (fprintf(out,
235	    " ]\n"
236	    "# Manifests: %u (%u failed parse)\n"
237	    "# Certificate revocation lists: %u\n"
238	    "# Ghostbuster records: %u\n"
239	    "# Repositories: %u\n"
240	    "# VRP Entries: %u (%u unique)\n",
241	    st->repo_tal_stats.mfts, st->repo_tal_stats.mfts_fail,
242	    st->repo_tal_stats.crls,
243	    st->repo_tal_stats.gbrs,
244	    st->repos,
245	    st->repo_tal_stats.vrps, st->repo_tal_stats.vrps_uniqs) < 0)
246		return -1;
247	return 0;
248}
249