1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kevin Fall. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #if 0 36 #ifndef lint 37 static char const copyright[] = 38 "@(#) Copyright (c) 1989, 1993\n\ 39 The Regents of the University of California. All rights reserved.\n"; 40 #endif /* not lint */ 41 #endif 42 43 #ifndef lint 44 #if 0 45 static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95"; 46 #endif 47 #endif /* not lint */ 48 #include <sys/cdefs.h> 49 __FBSDID("$FreeBSD$"); 50 51 #include <sys/capsicum.h> 52 #include <sys/param.h> 53 #include <sys/stat.h> 54 #ifndef NO_UDOM_SUPPORT 55 #include <sys/socket.h> 56 #include <sys/un.h> 57 #include <netdb.h> 58 #endif 59 60 #include <capsicum_helpers.h> 61 #include <ctype.h> 62 #include <err.h> 63 #include <errno.h> 64 #include <fcntl.h> 65 #include <locale.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <unistd.h> 70 #include <wchar.h> 71 #include <wctype.h> 72 73 #include <libcasper.h> 74 #include <casper/cap_fileargs.h> 75 #include <casper/cap_net.h> 76 77 static int bflag, eflag, lflag, nflag, sflag, tflag, vflag; 78 static int rval; 79 static const char *filename; 80 static fileargs_t *fa; 81 82 static void usage(void) __dead2; 83 static void scanfiles(char *argv[], int cooked); 84 #ifndef BOOTSTRAP_CAT 85 static void cook_cat(FILE *); 86 #endif 87 static void raw_cat(int); 88 89 #ifndef NO_UDOM_SUPPORT 90 static cap_channel_t *capnet; 91 92 static int udom_open(const char *path, int flags); 93 #endif 94 95 /* 96 * Memory strategy threshold, in pages: if physmem is larger than this, 97 * use a large buffer. 98 */ 99 #define PHYSPAGES_THRESHOLD (32 * 1024) 100 101 /* Maximum buffer size in bytes - do not allow it to grow larger than this. */ 102 #define BUFSIZE_MAX (2 * 1024 * 1024) 103 104 /* 105 * Small (default) buffer size in bytes. It's inefficient for this to be 106 * smaller than MAXPHYS. 107 */ 108 #define BUFSIZE_SMALL (MAXPHYS) 109 110 111 /* 112 * For the bootstrapped cat binary (needed for locked appending to METALOG), we 113 * disable all flags except -l and -u to avoid non-portable function calls. 114 * In the future we may instead want to write a small portable bootstrap tool 115 * that locks the output file before writing to it. However, for now 116 * bootstrapping cat without multibyte support is the simpler solution. 117 */ 118 #ifdef BOOTSTRAP_CAT 119 #define SUPPORTED_FLAGS "lu" 120 #else 121 #define SUPPORTED_FLAGS "belnstuv" 122 #endif 123 124 #ifndef NO_UDOM_SUPPORT 125 static void 126 init_casper_net(cap_channel_t *casper) 127 { 128 cap_net_limit_t *limit; 129 int familylimit; 130 131 capnet = cap_service_open(casper, "system.net"); 132 if (capnet == NULL) 133 err(EXIT_FAILURE, "unable to create network service"); 134 135 limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR | 136 CAPNET_CONNECTDNS); 137 if (limit == NULL) 138 err(EXIT_FAILURE, "unable to create limits"); 139 140 familylimit = AF_LOCAL; 141 cap_net_limit_name2addr_family(limit, &familylimit, 1); 142 143 if (cap_net_limit(limit) < 0) 144 err(EXIT_FAILURE, "unable to apply limits"); 145 } 146 #endif 147 148 static void 149 init_casper(int argc, char *argv[]) 150 { 151 cap_channel_t *casper; 152 cap_rights_t rights; 153 154 casper = cap_init(); 155 if (casper == NULL) 156 err(EXIT_FAILURE, "unable to create Casper"); 157 158 fa = fileargs_cinit(casper, argc, argv, O_RDONLY, 0, 159 cap_rights_init(&rights, CAP_READ | CAP_FSTAT | CAP_FCNTL), 160 FA_OPEN | FA_REALPATH); 161 if (fa == NULL) 162 err(EXIT_FAILURE, "unable to create fileargs"); 163 164 #ifndef NO_UDOM_SUPPORT 165 init_casper_net(casper); 166 #endif 167 168 cap_close(casper); 169 } 170 171 int 172 main(int argc, char *argv[]) 173 { 174 int ch; 175 struct flock stdout_lock; 176 177 setlocale(LC_CTYPE, ""); 178 179 while ((ch = getopt(argc, argv, SUPPORTED_FLAGS)) != -1) 180 switch (ch) { 181 case 'b': 182 bflag = nflag = 1; /* -b implies -n */ 183 break; 184 case 'e': 185 eflag = vflag = 1; /* -e implies -v */ 186 break; 187 case 'l': 188 lflag = 1; 189 break; 190 case 'n': 191 nflag = 1; 192 break; 193 case 's': 194 sflag = 1; 195 break; 196 case 't': 197 tflag = vflag = 1; /* -t implies -v */ 198 break; 199 case 'u': 200 setbuf(stdout, NULL); 201 break; 202 case 'v': 203 vflag = 1; 204 break; 205 default: 206 usage(); 207 } 208 argv += optind; 209 argc -= optind; 210 211 if (lflag) { 212 stdout_lock.l_len = 0; 213 stdout_lock.l_start = 0; 214 stdout_lock.l_type = F_WRLCK; 215 stdout_lock.l_whence = SEEK_SET; 216 if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) 217 err(EXIT_FAILURE, "stdout"); 218 } 219 220 init_casper(argc, argv); 221 222 caph_cache_catpages(); 223 224 if (caph_enter_casper() < 0) 225 err(EXIT_FAILURE, "capsicum"); 226 227 if (bflag || eflag || nflag || sflag || tflag || vflag) 228 scanfiles(argv, 1); 229 else 230 scanfiles(argv, 0); 231 if (fclose(stdout)) 232 err(1, "stdout"); 233 exit(rval); 234 /* NOTREACHED */ 235 } 236 237 static void 238 usage(void) 239 { 240 241 fprintf(stderr, "usage: cat [-" SUPPORTED_FLAGS "] [file ...]\n"); 242 exit(1); 243 /* NOTREACHED */ 244 } 245 246 static void 247 scanfiles(char *argv[], int cooked __unused) 248 { 249 int fd, i; 250 char *path; 251 #ifndef BOOTSTRAP_CAT 252 FILE *fp; 253 #endif 254 255 i = 0; 256 fd = -1; 257 while ((path = argv[i]) != NULL || i == 0) { 258 if (path == NULL || strcmp(path, "-") == 0) { 259 filename = "stdin"; 260 fd = STDIN_FILENO; 261 } else { 262 filename = path; 263 fd = fileargs_open(fa, path); 264 #ifndef NO_UDOM_SUPPORT 265 if (fd < 0 && errno == EOPNOTSUPP) 266 fd = udom_open(path, O_RDONLY); 267 #endif 268 } 269 if (fd < 0) { 270 warn("%s", path); 271 rval = 1; 272 #ifndef BOOTSTRAP_CAT 273 } else if (cooked) { 274 if (fd == STDIN_FILENO) 275 cook_cat(stdin); 276 else { 277 fp = fdopen(fd, "r"); 278 cook_cat(fp); 279 fclose(fp); 280 } 281 #endif 282 } else { 283 raw_cat(fd); 284 if (fd != STDIN_FILENO) 285 close(fd); 286 } 287 if (path == NULL) 288 break; 289 ++i; 290 } 291 } 292 293 #ifndef BOOTSTRAP_CAT 294 static void 295 cook_cat(FILE *fp) 296 { 297 int ch, gobble, line, prev; 298 wint_t wch; 299 300 /* Reset EOF condition on stdin. */ 301 if (fp == stdin && feof(stdin)) 302 clearerr(stdin); 303 304 line = gobble = 0; 305 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 306 if (prev == '\n') { 307 if (sflag) { 308 if (ch == '\n') { 309 if (gobble) 310 continue; 311 gobble = 1; 312 } else 313 gobble = 0; 314 } 315 if (nflag) { 316 if (!bflag || ch != '\n') { 317 (void)fprintf(stdout, "%6d\t", ++line); 318 if (ferror(stdout)) 319 break; 320 } else if (eflag) { 321 (void)fprintf(stdout, "%6s\t", ""); 322 if (ferror(stdout)) 323 break; 324 } 325 } 326 } 327 if (ch == '\n') { 328 if (eflag && putchar('$') == EOF) 329 break; 330 } else if (ch == '\t') { 331 if (tflag) { 332 if (putchar('^') == EOF || putchar('I') == EOF) 333 break; 334 continue; 335 } 336 } else if (vflag) { 337 (void)ungetc(ch, fp); 338 /* 339 * Our getwc(3) doesn't change file position 340 * on error. 341 */ 342 if ((wch = getwc(fp)) == WEOF) { 343 if (ferror(fp) && errno == EILSEQ) { 344 clearerr(fp); 345 /* Resync attempt. */ 346 memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 347 if ((ch = getc(fp)) == EOF) 348 break; 349 wch = ch; 350 goto ilseq; 351 } else 352 break; 353 } 354 if (!iswascii(wch) && !iswprint(wch)) { 355 ilseq: 356 if (putchar('M') == EOF || putchar('-') == EOF) 357 break; 358 wch = toascii(wch); 359 } 360 if (iswcntrl(wch)) { 361 ch = toascii(wch); 362 ch = (ch == '\177') ? '?' : (ch | 0100); 363 if (putchar('^') == EOF || putchar(ch) == EOF) 364 break; 365 continue; 366 } 367 if (putwchar(wch) == WEOF) 368 break; 369 ch = -1; 370 continue; 371 } 372 if (putchar(ch) == EOF) 373 break; 374 } 375 if (ferror(fp)) { 376 warn("%s", filename); 377 rval = 1; 378 clearerr(fp); 379 } 380 if (ferror(stdout)) 381 err(1, "stdout"); 382 } 383 #endif /* BOOTSTRAP_CAT */ 384 385 static void 386 raw_cat(int rfd) 387 { 388 long pagesize; 389 int off, wfd; 390 ssize_t nr, nw; 391 static size_t bsize; 392 static char *buf = NULL; 393 struct stat sbuf; 394 395 wfd = fileno(stdout); 396 if (buf == NULL) { 397 if (fstat(wfd, &sbuf)) 398 err(1, "stdout"); 399 if (S_ISREG(sbuf.st_mode)) { 400 /* If there's plenty of RAM, use a large copy buffer */ 401 if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) 402 bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 403 else 404 bsize = BUFSIZE_SMALL; 405 } else { 406 bsize = sbuf.st_blksize; 407 pagesize = sysconf(_SC_PAGESIZE); 408 if (pagesize > 0) 409 bsize = MAX(bsize, (size_t)pagesize); 410 } 411 if ((buf = malloc(bsize)) == NULL) 412 err(1, "malloc() failure of IO buffer"); 413 } 414 while ((nr = read(rfd, buf, bsize)) > 0) 415 for (off = 0; nr; nr -= nw, off += nw) 416 if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) 417 err(1, "stdout"); 418 if (nr < 0) { 419 warn("%s", filename); 420 rval = 1; 421 } 422 } 423 424 #ifndef NO_UDOM_SUPPORT 425 426 static int 427 udom_open(const char *path, int flags) 428 { 429 struct addrinfo hints, *res, *res0; 430 char rpath[PATH_MAX]; 431 int fd, error, serrno; 432 cap_rights_t rights; 433 434 /* 435 * Construct the unix domain socket address and attempt to connect. 436 */ 437 bzero(&hints, sizeof(hints)); 438 hints.ai_family = AF_LOCAL; 439 fd = -1; 440 441 if (fileargs_realpath(fa, path, rpath) == NULL) 442 return (-1); 443 444 error = cap_getaddrinfo(capnet, rpath, NULL, &hints, &res0); 445 if (error) { 446 warn("%s", gai_strerror(error)); 447 errno = EINVAL; 448 return (-1); 449 } 450 cap_rights_init(&rights, CAP_CONNECT, CAP_READ, CAP_WRITE, 451 CAP_SHUTDOWN, CAP_FSTAT, CAP_FCNTL); 452 for (res = res0; res != NULL; res = res->ai_next) { 453 fd = socket(res->ai_family, res->ai_socktype, 454 res->ai_protocol); 455 if (fd < 0) { 456 serrno = errno; 457 freeaddrinfo(res0); 458 errno = serrno; 459 return (-1); 460 } 461 if (caph_rights_limit(fd, &rights) < 0) { 462 serrno = errno; 463 close(fd); 464 freeaddrinfo(res0); 465 errno = serrno; 466 return (-1); 467 } 468 error = cap_connect(capnet, fd, res->ai_addr, res->ai_addrlen); 469 if (error == 0) 470 break; 471 else { 472 serrno = errno; 473 close(fd); 474 fd = -1; 475 } 476 } 477 freeaddrinfo(res0); 478 479 /* 480 * handle the open flags by shutting down appropriate directions 481 */ 482 if (fd >= 0) { 483 switch(flags & O_ACCMODE) { 484 case O_RDONLY: 485 cap_rights_clear(&rights, CAP_WRITE); 486 if (shutdown(fd, SHUT_WR) == -1) 487 warn(NULL); 488 break; 489 case O_WRONLY: 490 cap_rights_clear(&rights, CAP_READ); 491 if (shutdown(fd, SHUT_RD) == -1) 492 warn(NULL); 493 break; 494 default: 495 break; 496 } 497 498 cap_rights_clear(&rights, CAP_CONNECT, CAP_SHUTDOWN); 499 if (caph_rights_limit(fd, &rights) < 0) { 500 serrno = errno; 501 close(fd); 502 errno = serrno; 503 return (-1); 504 } 505 } else { 506 errno = serrno; 507 } 508 return (fd); 509 } 510 511 #endif 512