1*61d06d6bSBaptiste Daroussin /* $Id: mandocd.c,v 1.6 2017/06/24 14:38:32 schwarze Exp $ */ 2*61d06d6bSBaptiste Daroussin /* 3*61d06d6bSBaptiste Daroussin * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> 4*61d06d6bSBaptiste Daroussin * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> 5*61d06d6bSBaptiste Daroussin * 6*61d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 7*61d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 8*61d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 9*61d06d6bSBaptiste Daroussin * 10*61d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11*61d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*61d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13*61d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*61d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*61d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*61d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*61d06d6bSBaptiste Daroussin */ 18*61d06d6bSBaptiste Daroussin #include "config.h" 19*61d06d6bSBaptiste Daroussin 20*61d06d6bSBaptiste Daroussin #if HAVE_CMSG_XPG42 21*61d06d6bSBaptiste Daroussin #define _XPG4_2 22*61d06d6bSBaptiste Daroussin #endif 23*61d06d6bSBaptiste Daroussin 24*61d06d6bSBaptiste Daroussin #include <sys/types.h> 25*61d06d6bSBaptiste Daroussin #include <sys/socket.h> 26*61d06d6bSBaptiste Daroussin 27*61d06d6bSBaptiste Daroussin #if HAVE_ERR 28*61d06d6bSBaptiste Daroussin #include <err.h> 29*61d06d6bSBaptiste Daroussin #endif 30*61d06d6bSBaptiste Daroussin #include <limits.h> 31*61d06d6bSBaptiste Daroussin #include <stdint.h> 32*61d06d6bSBaptiste Daroussin #include <stdio.h> 33*61d06d6bSBaptiste Daroussin #include <stdlib.h> 34*61d06d6bSBaptiste Daroussin #include <string.h> 35*61d06d6bSBaptiste Daroussin #include <unistd.h> 36*61d06d6bSBaptiste Daroussin 37*61d06d6bSBaptiste Daroussin #include "mandoc.h" 38*61d06d6bSBaptiste Daroussin #include "roff.h" 39*61d06d6bSBaptiste Daroussin #include "mdoc.h" 40*61d06d6bSBaptiste Daroussin #include "man.h" 41*61d06d6bSBaptiste Daroussin #include "main.h" 42*61d06d6bSBaptiste Daroussin #include "manconf.h" 43*61d06d6bSBaptiste Daroussin 44*61d06d6bSBaptiste Daroussin enum outt { 45*61d06d6bSBaptiste Daroussin OUTT_ASCII = 0, 46*61d06d6bSBaptiste Daroussin OUTT_UTF8, 47*61d06d6bSBaptiste Daroussin OUTT_HTML 48*61d06d6bSBaptiste Daroussin }; 49*61d06d6bSBaptiste Daroussin 50*61d06d6bSBaptiste Daroussin static void process(struct mparse *, enum outt, void *); 51*61d06d6bSBaptiste Daroussin static int read_fds(int, int *); 52*61d06d6bSBaptiste Daroussin static void usage(void) __attribute__((__noreturn__)); 53*61d06d6bSBaptiste Daroussin 54*61d06d6bSBaptiste Daroussin 55*61d06d6bSBaptiste Daroussin #define NUM_FDS 3 56*61d06d6bSBaptiste Daroussin static int 57*61d06d6bSBaptiste Daroussin read_fds(int clientfd, int *fds) 58*61d06d6bSBaptiste Daroussin { 59*61d06d6bSBaptiste Daroussin struct msghdr msg; 60*61d06d6bSBaptiste Daroussin struct iovec iov[1]; 61*61d06d6bSBaptiste Daroussin unsigned char dummy[1]; 62*61d06d6bSBaptiste Daroussin struct cmsghdr *cmsg; 63*61d06d6bSBaptiste Daroussin int *walk; 64*61d06d6bSBaptiste Daroussin int cnt; 65*61d06d6bSBaptiste Daroussin 66*61d06d6bSBaptiste Daroussin /* Union used for alignment. */ 67*61d06d6bSBaptiste Daroussin union { 68*61d06d6bSBaptiste Daroussin uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))]; 69*61d06d6bSBaptiste Daroussin struct cmsghdr align; 70*61d06d6bSBaptiste Daroussin } u; 71*61d06d6bSBaptiste Daroussin 72*61d06d6bSBaptiste Daroussin memset(&msg, '\0', sizeof(msg)); 73*61d06d6bSBaptiste Daroussin msg.msg_control = u.controlbuf; 74*61d06d6bSBaptiste Daroussin msg.msg_controllen = sizeof(u.controlbuf); 75*61d06d6bSBaptiste Daroussin 76*61d06d6bSBaptiste Daroussin /* 77*61d06d6bSBaptiste Daroussin * Read a dummy byte - sendmsg cannot send an empty message, 78*61d06d6bSBaptiste Daroussin * even if we are only interested in the OOB data. 79*61d06d6bSBaptiste Daroussin */ 80*61d06d6bSBaptiste Daroussin 81*61d06d6bSBaptiste Daroussin iov[0].iov_base = dummy; 82*61d06d6bSBaptiste Daroussin iov[0].iov_len = sizeof(dummy); 83*61d06d6bSBaptiste Daroussin msg.msg_iov = iov; 84*61d06d6bSBaptiste Daroussin msg.msg_iovlen = 1; 85*61d06d6bSBaptiste Daroussin 86*61d06d6bSBaptiste Daroussin switch (recvmsg(clientfd, &msg, 0)) { 87*61d06d6bSBaptiste Daroussin case -1: 88*61d06d6bSBaptiste Daroussin warn("recvmsg"); 89*61d06d6bSBaptiste Daroussin return -1; 90*61d06d6bSBaptiste Daroussin case 0: 91*61d06d6bSBaptiste Daroussin return 0; 92*61d06d6bSBaptiste Daroussin default: 93*61d06d6bSBaptiste Daroussin break; 94*61d06d6bSBaptiste Daroussin } 95*61d06d6bSBaptiste Daroussin 96*61d06d6bSBaptiste Daroussin if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) { 97*61d06d6bSBaptiste Daroussin warnx("CMSG_FIRSTHDR: missing control message"); 98*61d06d6bSBaptiste Daroussin return -1; 99*61d06d6bSBaptiste Daroussin } 100*61d06d6bSBaptiste Daroussin 101*61d06d6bSBaptiste Daroussin if (cmsg->cmsg_level != SOL_SOCKET || 102*61d06d6bSBaptiste Daroussin cmsg->cmsg_type != SCM_RIGHTS || 103*61d06d6bSBaptiste Daroussin cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) { 104*61d06d6bSBaptiste Daroussin warnx("CMSG_FIRSTHDR: invalid control message"); 105*61d06d6bSBaptiste Daroussin return -1; 106*61d06d6bSBaptiste Daroussin } 107*61d06d6bSBaptiste Daroussin 108*61d06d6bSBaptiste Daroussin walk = (int *)CMSG_DATA(cmsg); 109*61d06d6bSBaptiste Daroussin for (cnt = 0; cnt < NUM_FDS; cnt++) 110*61d06d6bSBaptiste Daroussin fds[cnt] = *walk++; 111*61d06d6bSBaptiste Daroussin 112*61d06d6bSBaptiste Daroussin return 1; 113*61d06d6bSBaptiste Daroussin } 114*61d06d6bSBaptiste Daroussin 115*61d06d6bSBaptiste Daroussin int 116*61d06d6bSBaptiste Daroussin main(int argc, char *argv[]) 117*61d06d6bSBaptiste Daroussin { 118*61d06d6bSBaptiste Daroussin struct manoutput options; 119*61d06d6bSBaptiste Daroussin struct mparse *parser; 120*61d06d6bSBaptiste Daroussin void *formatter; 121*61d06d6bSBaptiste Daroussin const char *defos; 122*61d06d6bSBaptiste Daroussin const char *errstr; 123*61d06d6bSBaptiste Daroussin int clientfd; 124*61d06d6bSBaptiste Daroussin int old_stdin; 125*61d06d6bSBaptiste Daroussin int old_stdout; 126*61d06d6bSBaptiste Daroussin int old_stderr; 127*61d06d6bSBaptiste Daroussin int fds[3]; 128*61d06d6bSBaptiste Daroussin int state, opt; 129*61d06d6bSBaptiste Daroussin enum outt outtype; 130*61d06d6bSBaptiste Daroussin 131*61d06d6bSBaptiste Daroussin defos = NULL; 132*61d06d6bSBaptiste Daroussin outtype = OUTT_ASCII; 133*61d06d6bSBaptiste Daroussin while ((opt = getopt(argc, argv, "I:T:")) != -1) { 134*61d06d6bSBaptiste Daroussin switch (opt) { 135*61d06d6bSBaptiste Daroussin case 'I': 136*61d06d6bSBaptiste Daroussin if (strncmp(optarg, "os=", 3) == 0) 137*61d06d6bSBaptiste Daroussin defos = optarg + 3; 138*61d06d6bSBaptiste Daroussin else { 139*61d06d6bSBaptiste Daroussin warnx("-I %s: Bad argument", optarg); 140*61d06d6bSBaptiste Daroussin usage(); 141*61d06d6bSBaptiste Daroussin } 142*61d06d6bSBaptiste Daroussin break; 143*61d06d6bSBaptiste Daroussin case 'T': 144*61d06d6bSBaptiste Daroussin if (strcmp(optarg, "ascii") == 0) 145*61d06d6bSBaptiste Daroussin outtype = OUTT_ASCII; 146*61d06d6bSBaptiste Daroussin else if (strcmp(optarg, "utf8") == 0) 147*61d06d6bSBaptiste Daroussin outtype = OUTT_UTF8; 148*61d06d6bSBaptiste Daroussin else if (strcmp(optarg, "html") == 0) 149*61d06d6bSBaptiste Daroussin outtype = OUTT_HTML; 150*61d06d6bSBaptiste Daroussin else { 151*61d06d6bSBaptiste Daroussin warnx("-T %s: Bad argument", optarg); 152*61d06d6bSBaptiste Daroussin usage(); 153*61d06d6bSBaptiste Daroussin } 154*61d06d6bSBaptiste Daroussin break; 155*61d06d6bSBaptiste Daroussin default: 156*61d06d6bSBaptiste Daroussin usage(); 157*61d06d6bSBaptiste Daroussin } 158*61d06d6bSBaptiste Daroussin } 159*61d06d6bSBaptiste Daroussin 160*61d06d6bSBaptiste Daroussin if (argc > 0) { 161*61d06d6bSBaptiste Daroussin argc -= optind; 162*61d06d6bSBaptiste Daroussin argv += optind; 163*61d06d6bSBaptiste Daroussin } 164*61d06d6bSBaptiste Daroussin if (argc != 1) 165*61d06d6bSBaptiste Daroussin usage(); 166*61d06d6bSBaptiste Daroussin 167*61d06d6bSBaptiste Daroussin errstr = NULL; 168*61d06d6bSBaptiste Daroussin clientfd = strtonum(argv[0], 3, INT_MAX, &errstr); 169*61d06d6bSBaptiste Daroussin if (errstr) 170*61d06d6bSBaptiste Daroussin errx(1, "file descriptor %s %s", argv[1], errstr); 171*61d06d6bSBaptiste Daroussin 172*61d06d6bSBaptiste Daroussin mchars_alloc(); 173*61d06d6bSBaptiste Daroussin parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1, 174*61d06d6bSBaptiste Daroussin MANDOCERR_MAX, NULL, MANDOC_OS_OTHER, defos); 175*61d06d6bSBaptiste Daroussin 176*61d06d6bSBaptiste Daroussin memset(&options, 0, sizeof(options)); 177*61d06d6bSBaptiste Daroussin switch (outtype) { 178*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 179*61d06d6bSBaptiste Daroussin formatter = ascii_alloc(&options); 180*61d06d6bSBaptiste Daroussin break; 181*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 182*61d06d6bSBaptiste Daroussin formatter = utf8_alloc(&options); 183*61d06d6bSBaptiste Daroussin break; 184*61d06d6bSBaptiste Daroussin case OUTT_HTML: 185*61d06d6bSBaptiste Daroussin options.fragment = 1; 186*61d06d6bSBaptiste Daroussin formatter = html_alloc(&options); 187*61d06d6bSBaptiste Daroussin break; 188*61d06d6bSBaptiste Daroussin } 189*61d06d6bSBaptiste Daroussin 190*61d06d6bSBaptiste Daroussin state = 1; /* work to do */ 191*61d06d6bSBaptiste Daroussin fflush(stdout); 192*61d06d6bSBaptiste Daroussin fflush(stderr); 193*61d06d6bSBaptiste Daroussin if ((old_stdin = dup(STDIN_FILENO)) == -1 || 194*61d06d6bSBaptiste Daroussin (old_stdout = dup(STDOUT_FILENO)) == -1 || 195*61d06d6bSBaptiste Daroussin (old_stderr = dup(STDERR_FILENO)) == -1) { 196*61d06d6bSBaptiste Daroussin warn("dup"); 197*61d06d6bSBaptiste Daroussin state = -1; /* error */ 198*61d06d6bSBaptiste Daroussin } 199*61d06d6bSBaptiste Daroussin 200*61d06d6bSBaptiste Daroussin while (state == 1 && (state = read_fds(clientfd, fds)) == 1) { 201*61d06d6bSBaptiste Daroussin if (dup2(fds[0], STDIN_FILENO) == -1 || 202*61d06d6bSBaptiste Daroussin dup2(fds[1], STDOUT_FILENO) == -1 || 203*61d06d6bSBaptiste Daroussin dup2(fds[2], STDERR_FILENO) == -1) { 204*61d06d6bSBaptiste Daroussin warn("dup2"); 205*61d06d6bSBaptiste Daroussin state = -1; 206*61d06d6bSBaptiste Daroussin break; 207*61d06d6bSBaptiste Daroussin } 208*61d06d6bSBaptiste Daroussin 209*61d06d6bSBaptiste Daroussin close(fds[0]); 210*61d06d6bSBaptiste Daroussin close(fds[1]); 211*61d06d6bSBaptiste Daroussin close(fds[2]); 212*61d06d6bSBaptiste Daroussin 213*61d06d6bSBaptiste Daroussin process(parser, outtype, formatter); 214*61d06d6bSBaptiste Daroussin mparse_reset(parser); 215*61d06d6bSBaptiste Daroussin 216*61d06d6bSBaptiste Daroussin fflush(stdout); 217*61d06d6bSBaptiste Daroussin fflush(stderr); 218*61d06d6bSBaptiste Daroussin /* Close file descriptors by restoring the old ones. */ 219*61d06d6bSBaptiste Daroussin if (dup2(old_stderr, STDERR_FILENO) == -1 || 220*61d06d6bSBaptiste Daroussin dup2(old_stdout, STDOUT_FILENO) == -1 || 221*61d06d6bSBaptiste Daroussin dup2(old_stdin, STDIN_FILENO) == -1) { 222*61d06d6bSBaptiste Daroussin warn("dup2"); 223*61d06d6bSBaptiste Daroussin state = -1; 224*61d06d6bSBaptiste Daroussin break; 225*61d06d6bSBaptiste Daroussin } 226*61d06d6bSBaptiste Daroussin } 227*61d06d6bSBaptiste Daroussin 228*61d06d6bSBaptiste Daroussin close(clientfd); 229*61d06d6bSBaptiste Daroussin switch (outtype) { 230*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 231*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 232*61d06d6bSBaptiste Daroussin ascii_free(formatter); 233*61d06d6bSBaptiste Daroussin break; 234*61d06d6bSBaptiste Daroussin case OUTT_HTML: 235*61d06d6bSBaptiste Daroussin html_free(formatter); 236*61d06d6bSBaptiste Daroussin break; 237*61d06d6bSBaptiste Daroussin } 238*61d06d6bSBaptiste Daroussin mparse_free(parser); 239*61d06d6bSBaptiste Daroussin mchars_free(); 240*61d06d6bSBaptiste Daroussin return state == -1 ? 1 : 0; 241*61d06d6bSBaptiste Daroussin } 242*61d06d6bSBaptiste Daroussin 243*61d06d6bSBaptiste Daroussin static void 244*61d06d6bSBaptiste Daroussin process(struct mparse *parser, enum outt outtype, void *formatter) 245*61d06d6bSBaptiste Daroussin { 246*61d06d6bSBaptiste Daroussin struct roff_man *man; 247*61d06d6bSBaptiste Daroussin 248*61d06d6bSBaptiste Daroussin mparse_readfd(parser, STDIN_FILENO, "<unixfd>"); 249*61d06d6bSBaptiste Daroussin mparse_result(parser, &man, NULL); 250*61d06d6bSBaptiste Daroussin 251*61d06d6bSBaptiste Daroussin if (man == NULL) 252*61d06d6bSBaptiste Daroussin return; 253*61d06d6bSBaptiste Daroussin 254*61d06d6bSBaptiste Daroussin if (man->macroset == MACROSET_MDOC) { 255*61d06d6bSBaptiste Daroussin mdoc_validate(man); 256*61d06d6bSBaptiste Daroussin switch (outtype) { 257*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 258*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 259*61d06d6bSBaptiste Daroussin terminal_mdoc(formatter, man); 260*61d06d6bSBaptiste Daroussin break; 261*61d06d6bSBaptiste Daroussin case OUTT_HTML: 262*61d06d6bSBaptiste Daroussin html_mdoc(formatter, man); 263*61d06d6bSBaptiste Daroussin break; 264*61d06d6bSBaptiste Daroussin } 265*61d06d6bSBaptiste Daroussin } 266*61d06d6bSBaptiste Daroussin if (man->macroset == MACROSET_MAN) { 267*61d06d6bSBaptiste Daroussin man_validate(man); 268*61d06d6bSBaptiste Daroussin switch (outtype) { 269*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 270*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 271*61d06d6bSBaptiste Daroussin terminal_man(formatter, man); 272*61d06d6bSBaptiste Daroussin break; 273*61d06d6bSBaptiste Daroussin case OUTT_HTML: 274*61d06d6bSBaptiste Daroussin html_man(formatter, man); 275*61d06d6bSBaptiste Daroussin break; 276*61d06d6bSBaptiste Daroussin } 277*61d06d6bSBaptiste Daroussin } 278*61d06d6bSBaptiste Daroussin } 279*61d06d6bSBaptiste Daroussin 280*61d06d6bSBaptiste Daroussin void 281*61d06d6bSBaptiste Daroussin usage(void) 282*61d06d6bSBaptiste Daroussin { 283*61d06d6bSBaptiste Daroussin fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n"); 284*61d06d6bSBaptiste Daroussin exit(1); 285*61d06d6bSBaptiste Daroussin } 286