xref: /openbsd/usr.sbin/rpki-client/output.c (revision 73471bf0)
1 /*	$OpenBSD: output.c,v 1.24 2021/11/04 11:32:55 claudio 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 <unistd.h>
55 #include <time.h>
56 
57 #include "extern.h"
58 
59 int		 outformats;
60 
61 static char	 output_tmpname[PATH_MAX];
62 static char	 output_name[PATH_MAX];
63 
64 static const struct outputs {
65 	int	 format;
66 	char	*name;
67 	int	(*fn)(FILE *, struct vrp_tree *, struct brk_tree *,
68 		    struct stats *);
69 } outputs[] = {
70 	{ FORMAT_OPENBGPD, "openbgpd", output_bgpd },
71 	{ FORMAT_BIRD, "bird1v4", output_bird1v4 },
72 	{ FORMAT_BIRD, "bird1v6", output_bird1v6 },
73 	{ FORMAT_BIRD, "bird", output_bird2 },
74 	{ FORMAT_CSV, "csv", output_csv },
75 	{ FORMAT_JSON, "json", output_json },
76 	{ 0, NULL }
77 };
78 
79 static FILE	*output_createtmp(char *);
80 static void	 output_cleantmp(void);
81 static int	 output_finish(FILE *);
82 static void	 sig_handler(int);
83 static void	 set_signal_handler(void);
84 
85 int
86 outputfiles(struct vrp_tree *v, struct brk_tree *b, struct stats *st)
87 {
88 	int i, rc = 0;
89 
90 	atexit(output_cleantmp);
91 	set_signal_handler();
92 
93 	for (i = 0; outputs[i].name; i++) {
94 		FILE *fout;
95 
96 		if (!(outformats & outputs[i].format))
97 			continue;
98 
99 		fout = output_createtmp(outputs[i].name);
100 		if (fout == NULL) {
101 			warn("cannot create %s", outputs[i].name);
102 			rc = 1;
103 			continue;
104 		}
105 		if ((*outputs[i].fn)(fout, v, b, st) != 0) {
106 			warn("output for %s format failed", outputs[i].name);
107 			fclose(fout);
108 			output_cleantmp();
109 			rc = 1;
110 			continue;
111 		}
112 		if (output_finish(fout) != 0) {
113 			warn("finish for %s format failed", outputs[i].name);
114 			output_cleantmp();
115 			rc = 1;
116 			continue;
117 		}
118 	}
119 
120 	return rc;
121 }
122 
123 static FILE *
124 output_createtmp(char *name)
125 {
126 	FILE *f;
127 	int fd, r;
128 
129 	if (strlcpy(output_name, name, sizeof output_name) >=
130 	    sizeof output_name)
131 		err(1, "path too long");
132 	r = snprintf(output_tmpname, sizeof output_tmpname,
133 	    "%s.XXXXXXXXXXX", output_name);
134 	if (r < 0 || r > (int)sizeof(output_tmpname))
135 		err(1, "path too long");
136 	fd = mkostemp(output_tmpname, O_CLOEXEC);
137 	if (fd == -1)
138 		err(1, "mkostemp: %s", output_tmpname);
139 	(void) fchmod(fd, 0644);
140 	f = fdopen(fd, "w");
141 	if (f == NULL)
142 		err(1, "fdopen");
143 	return f;
144 }
145 
146 static int
147 output_finish(FILE *out)
148 {
149 	if (fclose(out) != 0)
150 		return -1;
151 	if (rename(output_tmpname, output_name) == -1)
152 		return -1;
153 	output_tmpname[0] = '\0';
154 	return 0;
155 }
156 
157 static void
158 output_cleantmp(void)
159 {
160 	if (*output_tmpname)
161 		unlink(output_tmpname);
162 	output_tmpname[0] = '\0';
163 }
164 
165 /*
166  * Signal handler that clears the temporary files.
167  */
168 static void
169 sig_handler(int sig)
170 {
171 	output_cleantmp();
172 	_exit(2);
173 }
174 
175 /*
176  * Set signal handler on panic signals.
177  */
178 static void
179 set_signal_handler(void)
180 {
181 	struct sigaction sa;
182 	int i, signals[] = {SIGTERM, SIGHUP, SIGINT, SIGUSR1, SIGUSR2,
183 	    SIGPIPE, SIGXCPU, SIGXFSZ, 0};
184 
185 	memset(&sa, 0, sizeof(sa));
186 	sigfillset(&sa.sa_mask);
187 	sa.sa_flags = SA_RESTART;
188 	sa.sa_handler = sig_handler;
189 
190 	for (i = 0; signals[i] != 0; i++) {
191 		if (sigaction(signals[i], &sa, NULL) == -1) {
192 			warn("sigaction(%s)", strsignal(signals[i]));
193 			continue;
194 		}
195 	}
196 }
197 
198 int
199 outputheader(FILE *out, struct stats *st)
200 {
201 	char		hn[NI_MAXHOST], tbuf[80];
202 	struct tm	*tp;
203 	time_t		t;
204 	size_t		i;
205 
206 	time(&t);
207 	setenv("TZ", "UTC", 1);
208 	tp = localtime(&t);
209 	strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Z %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: %zu (%zu failed parse, %zu invalid)\n"
217 	    "# BGPsec Router Certificates: %zu\n"
218 	    "# Certificates: %zu (%zu 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->roas, st->roas_fail, st->roas_invalid,
222 	    st->brks, st->certs, st->certs_fail) < 0)
223 		return -1;
224 
225 	if (fprintf(out,
226 	    "# Trust Anchor Locators: %zu (%zu invalid) [", st->tals,
227 	    talsz - st->tals) < 0)
228 		return -1;
229 	for (i = 0; i < talsz; i++)
230 		if (fprintf(out, " %s", tals[i]) < 0)
231 			return -1;
232 
233 	if (fprintf(out,
234 	    " ]\n"
235 	    "# Manifests: %zu (%zu failed parse, %zu stale)\n"
236 	    "# Certificate revocation lists: %zu\n"
237 	    "# Ghostbuster records: %zu\n"
238 	    "# Repositories: %zu\n"
239 	    "# VRP Entries: %zu (%zu unique)\n",
240 	    st->mfts, st->mfts_fail, st->mfts_stale,
241 	    st->crls,
242 	    st->gbrs,
243 	    st->repos,
244 	    st->vrps, st->uniqs) < 0)
245 		return -1;
246 	return 0;
247 }
248