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