1322249Sbapt/* $Id: mandocd.c,v 1.6 2017/06/24 14:38:32 schwarze Exp $ */ 2313956Sbapt/* 3313956Sbapt * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> 4313956Sbapt * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> 5313956Sbapt * 6313956Sbapt * Permission to use, copy, modify, and distribute this software for any 7313956Sbapt * purpose with or without fee is hereby granted, provided that the above 8313956Sbapt * copyright notice and this permission notice appear in all copies. 9313956Sbapt * 10313956Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11313956Sbapt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12313956Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13313956Sbapt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14313956Sbapt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15313956Sbapt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16313956Sbapt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17313956Sbapt */ 18313956Sbapt#include "config.h" 19313956Sbapt 20313956Sbapt#if HAVE_CMSG_XPG42 21313956Sbapt#define _XPG4_2 22313956Sbapt#endif 23313956Sbapt 24313956Sbapt#include <sys/types.h> 25313956Sbapt#include <sys/socket.h> 26313956Sbapt 27313956Sbapt#if HAVE_ERR 28313956Sbapt#include <err.h> 29313956Sbapt#endif 30313956Sbapt#include <limits.h> 31313956Sbapt#include <stdint.h> 32313956Sbapt#include <stdio.h> 33313956Sbapt#include <stdlib.h> 34313956Sbapt#include <string.h> 35313956Sbapt#include <unistd.h> 36313956Sbapt 37313956Sbapt#include "mandoc.h" 38313956Sbapt#include "roff.h" 39313956Sbapt#include "mdoc.h" 40313956Sbapt#include "man.h" 41313956Sbapt#include "main.h" 42313956Sbapt#include "manconf.h" 43313956Sbapt 44313956Sbaptenum outt { 45313956Sbapt OUTT_ASCII = 0, 46313956Sbapt OUTT_UTF8, 47313956Sbapt OUTT_HTML 48313956Sbapt}; 49313956Sbapt 50313956Sbaptstatic void process(struct mparse *, enum outt, void *); 51313956Sbaptstatic int read_fds(int, int *); 52313956Sbaptstatic void usage(void) __attribute__((__noreturn__)); 53313956Sbapt 54313956Sbapt 55313956Sbapt#define NUM_FDS 3 56313956Sbaptstatic int 57313956Sbaptread_fds(int clientfd, int *fds) 58313956Sbapt{ 59313956Sbapt struct msghdr msg; 60313956Sbapt struct iovec iov[1]; 61313956Sbapt unsigned char dummy[1]; 62313956Sbapt struct cmsghdr *cmsg; 63313956Sbapt int *walk; 64313956Sbapt int cnt; 65313956Sbapt 66313956Sbapt /* Union used for alignment. */ 67313956Sbapt union { 68313956Sbapt uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))]; 69313956Sbapt struct cmsghdr align; 70313956Sbapt } u; 71313956Sbapt 72313956Sbapt memset(&msg, '\0', sizeof(msg)); 73313956Sbapt msg.msg_control = u.controlbuf; 74313956Sbapt msg.msg_controllen = sizeof(u.controlbuf); 75313956Sbapt 76313956Sbapt /* 77313956Sbapt * Read a dummy byte - sendmsg cannot send an empty message, 78313956Sbapt * even if we are only interested in the OOB data. 79313956Sbapt */ 80313956Sbapt 81313956Sbapt iov[0].iov_base = dummy; 82313956Sbapt iov[0].iov_len = sizeof(dummy); 83313956Sbapt msg.msg_iov = iov; 84313956Sbapt msg.msg_iovlen = 1; 85313956Sbapt 86313956Sbapt switch (recvmsg(clientfd, &msg, 0)) { 87313956Sbapt case -1: 88313956Sbapt warn("recvmsg"); 89313956Sbapt return -1; 90313956Sbapt case 0: 91313956Sbapt return 0; 92313956Sbapt default: 93313956Sbapt break; 94313956Sbapt } 95313956Sbapt 96313956Sbapt if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) { 97313956Sbapt warnx("CMSG_FIRSTHDR: missing control message"); 98313956Sbapt return -1; 99313956Sbapt } 100313956Sbapt 101313956Sbapt if (cmsg->cmsg_level != SOL_SOCKET || 102313956Sbapt cmsg->cmsg_type != SCM_RIGHTS || 103313956Sbapt cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) { 104313956Sbapt warnx("CMSG_FIRSTHDR: invalid control message"); 105313956Sbapt return -1; 106313956Sbapt } 107313956Sbapt 108313956Sbapt walk = (int *)CMSG_DATA(cmsg); 109313956Sbapt for (cnt = 0; cnt < NUM_FDS; cnt++) 110313956Sbapt fds[cnt] = *walk++; 111313956Sbapt 112313956Sbapt return 1; 113313956Sbapt} 114313956Sbapt 115313956Sbaptint 116313956Sbaptmain(int argc, char *argv[]) 117313956Sbapt{ 118313956Sbapt struct manoutput options; 119313956Sbapt struct mparse *parser; 120313956Sbapt void *formatter; 121313956Sbapt const char *defos; 122313956Sbapt const char *errstr; 123313956Sbapt int clientfd; 124313956Sbapt int old_stdin; 125313956Sbapt int old_stdout; 126313956Sbapt int old_stderr; 127313956Sbapt int fds[3]; 128313956Sbapt int state, opt; 129313956Sbapt enum outt outtype; 130313956Sbapt 131313956Sbapt defos = NULL; 132313956Sbapt outtype = OUTT_ASCII; 133313956Sbapt while ((opt = getopt(argc, argv, "I:T:")) != -1) { 134313956Sbapt switch (opt) { 135313956Sbapt case 'I': 136313956Sbapt if (strncmp(optarg, "os=", 3) == 0) 137313956Sbapt defos = optarg + 3; 138313956Sbapt else { 139313956Sbapt warnx("-I %s: Bad argument", optarg); 140313956Sbapt usage(); 141313956Sbapt } 142313956Sbapt break; 143313956Sbapt case 'T': 144313956Sbapt if (strcmp(optarg, "ascii") == 0) 145313956Sbapt outtype = OUTT_ASCII; 146313956Sbapt else if (strcmp(optarg, "utf8") == 0) 147313956Sbapt outtype = OUTT_UTF8; 148313956Sbapt else if (strcmp(optarg, "html") == 0) 149313956Sbapt outtype = OUTT_HTML; 150313956Sbapt else { 151313956Sbapt warnx("-T %s: Bad argument", optarg); 152313956Sbapt usage(); 153313956Sbapt } 154313956Sbapt break; 155313956Sbapt default: 156313956Sbapt usage(); 157313956Sbapt } 158313956Sbapt } 159313956Sbapt 160313956Sbapt if (argc > 0) { 161313956Sbapt argc -= optind; 162313956Sbapt argv += optind; 163313956Sbapt } 164313956Sbapt if (argc != 1) 165313956Sbapt usage(); 166313956Sbapt 167313956Sbapt errstr = NULL; 168313956Sbapt clientfd = strtonum(argv[0], 3, INT_MAX, &errstr); 169313956Sbapt if (errstr) 170313956Sbapt errx(1, "file descriptor %s %s", argv[1], errstr); 171313956Sbapt 172313956Sbapt mchars_alloc(); 173313956Sbapt parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1, 174322249Sbapt MANDOCERR_MAX, NULL, MANDOC_OS_OTHER, defos); 175313956Sbapt 176313956Sbapt memset(&options, 0, sizeof(options)); 177313956Sbapt switch (outtype) { 178313956Sbapt case OUTT_ASCII: 179313956Sbapt formatter = ascii_alloc(&options); 180313956Sbapt break; 181313956Sbapt case OUTT_UTF8: 182313956Sbapt formatter = utf8_alloc(&options); 183313956Sbapt break; 184313956Sbapt case OUTT_HTML: 185313956Sbapt options.fragment = 1; 186313956Sbapt formatter = html_alloc(&options); 187313956Sbapt break; 188313956Sbapt } 189313956Sbapt 190313956Sbapt state = 1; /* work to do */ 191313956Sbapt fflush(stdout); 192313956Sbapt fflush(stderr); 193313956Sbapt if ((old_stdin = dup(STDIN_FILENO)) == -1 || 194313956Sbapt (old_stdout = dup(STDOUT_FILENO)) == -1 || 195313956Sbapt (old_stderr = dup(STDERR_FILENO)) == -1) { 196313956Sbapt warn("dup"); 197313956Sbapt state = -1; /* error */ 198313956Sbapt } 199313956Sbapt 200313956Sbapt while (state == 1 && (state = read_fds(clientfd, fds)) == 1) { 201313956Sbapt if (dup2(fds[0], STDIN_FILENO) == -1 || 202313956Sbapt dup2(fds[1], STDOUT_FILENO) == -1 || 203313956Sbapt dup2(fds[2], STDERR_FILENO) == -1) { 204313956Sbapt warn("dup2"); 205313956Sbapt state = -1; 206313956Sbapt break; 207313956Sbapt } 208313956Sbapt 209313956Sbapt close(fds[0]); 210313956Sbapt close(fds[1]); 211313956Sbapt close(fds[2]); 212313956Sbapt 213313956Sbapt process(parser, outtype, formatter); 214313956Sbapt mparse_reset(parser); 215313956Sbapt 216313956Sbapt fflush(stdout); 217313956Sbapt fflush(stderr); 218313956Sbapt /* Close file descriptors by restoring the old ones. */ 219313956Sbapt if (dup2(old_stderr, STDERR_FILENO) == -1 || 220313956Sbapt dup2(old_stdout, STDOUT_FILENO) == -1 || 221313956Sbapt dup2(old_stdin, STDIN_FILENO) == -1) { 222313956Sbapt warn("dup2"); 223313956Sbapt state = -1; 224313956Sbapt break; 225313956Sbapt } 226313956Sbapt } 227313956Sbapt 228313956Sbapt close(clientfd); 229313956Sbapt switch (outtype) { 230313956Sbapt case OUTT_ASCII: 231313956Sbapt case OUTT_UTF8: 232313956Sbapt ascii_free(formatter); 233313956Sbapt break; 234313956Sbapt case OUTT_HTML: 235313956Sbapt html_free(formatter); 236313956Sbapt break; 237313956Sbapt } 238313956Sbapt mparse_free(parser); 239313956Sbapt mchars_free(); 240313956Sbapt return state == -1 ? 1 : 0; 241313956Sbapt} 242313956Sbapt 243313956Sbaptstatic void 244313956Sbaptprocess(struct mparse *parser, enum outt outtype, void *formatter) 245313956Sbapt{ 246313956Sbapt struct roff_man *man; 247313956Sbapt 248313956Sbapt mparse_readfd(parser, STDIN_FILENO, "<unixfd>"); 249313956Sbapt mparse_result(parser, &man, NULL); 250313956Sbapt 251313956Sbapt if (man == NULL) 252313956Sbapt return; 253313956Sbapt 254313956Sbapt if (man->macroset == MACROSET_MDOC) { 255313956Sbapt mdoc_validate(man); 256313956Sbapt switch (outtype) { 257313956Sbapt case OUTT_ASCII: 258313956Sbapt case OUTT_UTF8: 259313956Sbapt terminal_mdoc(formatter, man); 260313956Sbapt break; 261313956Sbapt case OUTT_HTML: 262313956Sbapt html_mdoc(formatter, man); 263313956Sbapt break; 264313956Sbapt } 265313956Sbapt } 266313956Sbapt if (man->macroset == MACROSET_MAN) { 267313956Sbapt man_validate(man); 268313956Sbapt switch (outtype) { 269313956Sbapt case OUTT_ASCII: 270313956Sbapt case OUTT_UTF8: 271313956Sbapt terminal_man(formatter, man); 272313956Sbapt break; 273313956Sbapt case OUTT_HTML: 274313956Sbapt html_man(formatter, man); 275313956Sbapt break; 276313956Sbapt } 277313956Sbapt } 278313956Sbapt} 279313956Sbapt 280313956Sbaptvoid 281313956Sbaptusage(void) 282313956Sbapt{ 283313956Sbapt fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n"); 284313956Sbapt exit(1); 285313956Sbapt} 286