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