1*c1c95addSBrooks Davis /* $Id: mandocd.c,v 1.13 2022/04/14 16:43:44 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> 4*c1c95addSBrooks Davis * Copyright (c) 2017, 2019, 2021 Ingo Schwarze <schwarze@openbsd.org> 561d06d6bSBaptiste Daroussin * 661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 961d06d6bSBaptiste Daroussin * 1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1761d06d6bSBaptiste Daroussin */ 1861d06d6bSBaptiste Daroussin #include "config.h" 1961d06d6bSBaptiste Daroussin 206d38604fSBaptiste Daroussin #if NEED_XPG4_2 2161d06d6bSBaptiste Daroussin #define _XPG4_2 2261d06d6bSBaptiste Daroussin #endif 2361d06d6bSBaptiste Daroussin 2461d06d6bSBaptiste Daroussin #include <sys/types.h> 2561d06d6bSBaptiste Daroussin #include <sys/socket.h> 2661d06d6bSBaptiste Daroussin 2761d06d6bSBaptiste Daroussin #if HAVE_ERR 2861d06d6bSBaptiste Daroussin #include <err.h> 2961d06d6bSBaptiste Daroussin #endif 3061d06d6bSBaptiste Daroussin #include <limits.h> 3161d06d6bSBaptiste Daroussin #include <stdint.h> 3261d06d6bSBaptiste Daroussin #include <stdio.h> 3361d06d6bSBaptiste Daroussin #include <stdlib.h> 3461d06d6bSBaptiste Daroussin #include <string.h> 3561d06d6bSBaptiste Daroussin #include <unistd.h> 3661d06d6bSBaptiste Daroussin 3761d06d6bSBaptiste Daroussin #include "mandoc.h" 38*c1c95addSBrooks Davis #if DEBUG_MEMORY 39*c1c95addSBrooks Davis #define DEBUG_NODEF 1 40*c1c95addSBrooks Davis #include "mandoc_dbg.h" 41*c1c95addSBrooks Davis #endif 4261d06d6bSBaptiste Daroussin #include "roff.h" 4361d06d6bSBaptiste Daroussin #include "mdoc.h" 4461d06d6bSBaptiste Daroussin #include "man.h" 457295610fSBaptiste Daroussin #include "mandoc_parse.h" 4661d06d6bSBaptiste Daroussin #include "main.h" 4761d06d6bSBaptiste Daroussin #include "manconf.h" 4861d06d6bSBaptiste Daroussin 4961d06d6bSBaptiste Daroussin enum outt { 5061d06d6bSBaptiste Daroussin OUTT_ASCII = 0, 5161d06d6bSBaptiste Daroussin OUTT_UTF8, 5261d06d6bSBaptiste Daroussin OUTT_HTML 5361d06d6bSBaptiste Daroussin }; 5461d06d6bSBaptiste Daroussin 5561d06d6bSBaptiste Daroussin static void process(struct mparse *, enum outt, void *); 5661d06d6bSBaptiste Daroussin static int read_fds(int, int *); 5761d06d6bSBaptiste Daroussin static void usage(void) __attribute__((__noreturn__)); 5861d06d6bSBaptiste Daroussin 5961d06d6bSBaptiste Daroussin 6061d06d6bSBaptiste Daroussin #define NUM_FDS 3 6161d06d6bSBaptiste Daroussin static int 6261d06d6bSBaptiste Daroussin read_fds(int clientfd, int *fds) 6361d06d6bSBaptiste Daroussin { 6461d06d6bSBaptiste Daroussin struct msghdr msg; 6561d06d6bSBaptiste Daroussin struct iovec iov[1]; 6661d06d6bSBaptiste Daroussin unsigned char dummy[1]; 6761d06d6bSBaptiste Daroussin struct cmsghdr *cmsg; 6861d06d6bSBaptiste Daroussin int *walk; 6961d06d6bSBaptiste Daroussin int cnt; 7061d06d6bSBaptiste Daroussin 7161d06d6bSBaptiste Daroussin /* Union used for alignment. */ 7261d06d6bSBaptiste Daroussin union { 7361d06d6bSBaptiste Daroussin uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))]; 7461d06d6bSBaptiste Daroussin struct cmsghdr align; 7561d06d6bSBaptiste Daroussin } u; 7661d06d6bSBaptiste Daroussin 7761d06d6bSBaptiste Daroussin memset(&msg, '\0', sizeof(msg)); 7861d06d6bSBaptiste Daroussin msg.msg_control = u.controlbuf; 7961d06d6bSBaptiste Daroussin msg.msg_controllen = sizeof(u.controlbuf); 8061d06d6bSBaptiste Daroussin 8161d06d6bSBaptiste Daroussin /* 8261d06d6bSBaptiste Daroussin * Read a dummy byte - sendmsg cannot send an empty message, 8361d06d6bSBaptiste Daroussin * even if we are only interested in the OOB data. 8461d06d6bSBaptiste Daroussin */ 8561d06d6bSBaptiste Daroussin 8661d06d6bSBaptiste Daroussin iov[0].iov_base = dummy; 8761d06d6bSBaptiste Daroussin iov[0].iov_len = sizeof(dummy); 8861d06d6bSBaptiste Daroussin msg.msg_iov = iov; 8961d06d6bSBaptiste Daroussin msg.msg_iovlen = 1; 9061d06d6bSBaptiste Daroussin 9161d06d6bSBaptiste Daroussin switch (recvmsg(clientfd, &msg, 0)) { 9261d06d6bSBaptiste Daroussin case -1: 9361d06d6bSBaptiste Daroussin warn("recvmsg"); 9461d06d6bSBaptiste Daroussin return -1; 9561d06d6bSBaptiste Daroussin case 0: 9661d06d6bSBaptiste Daroussin return 0; 9761d06d6bSBaptiste Daroussin default: 9861d06d6bSBaptiste Daroussin break; 9961d06d6bSBaptiste Daroussin } 10061d06d6bSBaptiste Daroussin 10161d06d6bSBaptiste Daroussin if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) { 10261d06d6bSBaptiste Daroussin warnx("CMSG_FIRSTHDR: missing control message"); 10361d06d6bSBaptiste Daroussin return -1; 10461d06d6bSBaptiste Daroussin } 10561d06d6bSBaptiste Daroussin 10661d06d6bSBaptiste Daroussin if (cmsg->cmsg_level != SOL_SOCKET || 10761d06d6bSBaptiste Daroussin cmsg->cmsg_type != SCM_RIGHTS || 10861d06d6bSBaptiste Daroussin cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) { 10961d06d6bSBaptiste Daroussin warnx("CMSG_FIRSTHDR: invalid control message"); 11061d06d6bSBaptiste Daroussin return -1; 11161d06d6bSBaptiste Daroussin } 11261d06d6bSBaptiste Daroussin 11361d06d6bSBaptiste Daroussin walk = (int *)CMSG_DATA(cmsg); 11461d06d6bSBaptiste Daroussin for (cnt = 0; cnt < NUM_FDS; cnt++) 11561d06d6bSBaptiste Daroussin fds[cnt] = *walk++; 11661d06d6bSBaptiste Daroussin 11761d06d6bSBaptiste Daroussin return 1; 11861d06d6bSBaptiste Daroussin } 11961d06d6bSBaptiste Daroussin 12061d06d6bSBaptiste Daroussin int 12161d06d6bSBaptiste Daroussin main(int argc, char *argv[]) 12261d06d6bSBaptiste Daroussin { 12361d06d6bSBaptiste Daroussin struct manoutput options; 12461d06d6bSBaptiste Daroussin struct mparse *parser; 12561d06d6bSBaptiste Daroussin void *formatter; 12661d06d6bSBaptiste Daroussin const char *defos; 12761d06d6bSBaptiste Daroussin const char *errstr; 12861d06d6bSBaptiste Daroussin int clientfd; 12961d06d6bSBaptiste Daroussin int old_stdin; 13061d06d6bSBaptiste Daroussin int old_stdout; 13161d06d6bSBaptiste Daroussin int old_stderr; 13261d06d6bSBaptiste Daroussin int fds[3]; 13361d06d6bSBaptiste Daroussin int state, opt; 13461d06d6bSBaptiste Daroussin enum outt outtype; 13561d06d6bSBaptiste Daroussin 136*c1c95addSBrooks Davis #if DEBUG_MEMORY 137*c1c95addSBrooks Davis mandoc_dbg_init(argc, argv); 138*c1c95addSBrooks Davis #endif 139*c1c95addSBrooks Davis 14061d06d6bSBaptiste Daroussin defos = NULL; 14161d06d6bSBaptiste Daroussin outtype = OUTT_ASCII; 14261d06d6bSBaptiste Daroussin while ((opt = getopt(argc, argv, "I:T:")) != -1) { 14361d06d6bSBaptiste Daroussin switch (opt) { 14461d06d6bSBaptiste Daroussin case 'I': 14561d06d6bSBaptiste Daroussin if (strncmp(optarg, "os=", 3) == 0) 14661d06d6bSBaptiste Daroussin defos = optarg + 3; 14761d06d6bSBaptiste Daroussin else { 14861d06d6bSBaptiste Daroussin warnx("-I %s: Bad argument", optarg); 14961d06d6bSBaptiste Daroussin usage(); 15061d06d6bSBaptiste Daroussin } 15161d06d6bSBaptiste Daroussin break; 15261d06d6bSBaptiste Daroussin case 'T': 15361d06d6bSBaptiste Daroussin if (strcmp(optarg, "ascii") == 0) 15461d06d6bSBaptiste Daroussin outtype = OUTT_ASCII; 15561d06d6bSBaptiste Daroussin else if (strcmp(optarg, "utf8") == 0) 15661d06d6bSBaptiste Daroussin outtype = OUTT_UTF8; 15761d06d6bSBaptiste Daroussin else if (strcmp(optarg, "html") == 0) 15861d06d6bSBaptiste Daroussin outtype = OUTT_HTML; 15961d06d6bSBaptiste Daroussin else { 16061d06d6bSBaptiste Daroussin warnx("-T %s: Bad argument", optarg); 16161d06d6bSBaptiste Daroussin usage(); 16261d06d6bSBaptiste Daroussin } 16361d06d6bSBaptiste Daroussin break; 16461d06d6bSBaptiste Daroussin default: 16561d06d6bSBaptiste Daroussin usage(); 16661d06d6bSBaptiste Daroussin } 16761d06d6bSBaptiste Daroussin } 16861d06d6bSBaptiste Daroussin 16961d06d6bSBaptiste Daroussin if (argc > 0) { 17061d06d6bSBaptiste Daroussin argc -= optind; 17161d06d6bSBaptiste Daroussin argv += optind; 17261d06d6bSBaptiste Daroussin } 17361d06d6bSBaptiste Daroussin if (argc != 1) 17461d06d6bSBaptiste Daroussin usage(); 17561d06d6bSBaptiste Daroussin 17661d06d6bSBaptiste Daroussin errstr = NULL; 17761d06d6bSBaptiste Daroussin clientfd = strtonum(argv[0], 3, INT_MAX, &errstr); 17861d06d6bSBaptiste Daroussin if (errstr) 17961d06d6bSBaptiste Daroussin errx(1, "file descriptor %s %s", argv[1], errstr); 18061d06d6bSBaptiste Daroussin 18161d06d6bSBaptiste Daroussin mchars_alloc(); 1827295610fSBaptiste Daroussin parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 | 1837295610fSBaptiste Daroussin MPARSE_VALIDATE, MANDOC_OS_OTHER, defos); 18461d06d6bSBaptiste Daroussin 18561d06d6bSBaptiste Daroussin memset(&options, 0, sizeof(options)); 18661d06d6bSBaptiste Daroussin switch (outtype) { 18761d06d6bSBaptiste Daroussin case OUTT_ASCII: 18861d06d6bSBaptiste Daroussin formatter = ascii_alloc(&options); 18961d06d6bSBaptiste Daroussin break; 19061d06d6bSBaptiste Daroussin case OUTT_UTF8: 19161d06d6bSBaptiste Daroussin formatter = utf8_alloc(&options); 19261d06d6bSBaptiste Daroussin break; 19361d06d6bSBaptiste Daroussin case OUTT_HTML: 19461d06d6bSBaptiste Daroussin options.fragment = 1; 19561d06d6bSBaptiste Daroussin formatter = html_alloc(&options); 19661d06d6bSBaptiste Daroussin break; 19761d06d6bSBaptiste Daroussin } 19861d06d6bSBaptiste Daroussin 19961d06d6bSBaptiste Daroussin state = 1; /* work to do */ 20061d06d6bSBaptiste Daroussin fflush(stdout); 20161d06d6bSBaptiste Daroussin fflush(stderr); 20261d06d6bSBaptiste Daroussin if ((old_stdin = dup(STDIN_FILENO)) == -1 || 20361d06d6bSBaptiste Daroussin (old_stdout = dup(STDOUT_FILENO)) == -1 || 20461d06d6bSBaptiste Daroussin (old_stderr = dup(STDERR_FILENO)) == -1) { 20561d06d6bSBaptiste Daroussin warn("dup"); 20661d06d6bSBaptiste Daroussin state = -1; /* error */ 20761d06d6bSBaptiste Daroussin } 20861d06d6bSBaptiste Daroussin 20961d06d6bSBaptiste Daroussin while (state == 1 && (state = read_fds(clientfd, fds)) == 1) { 21061d06d6bSBaptiste Daroussin if (dup2(fds[0], STDIN_FILENO) == -1 || 21161d06d6bSBaptiste Daroussin dup2(fds[1], STDOUT_FILENO) == -1 || 21261d06d6bSBaptiste Daroussin dup2(fds[2], STDERR_FILENO) == -1) { 21361d06d6bSBaptiste Daroussin warn("dup2"); 21461d06d6bSBaptiste Daroussin state = -1; 21561d06d6bSBaptiste Daroussin break; 21661d06d6bSBaptiste Daroussin } 21761d06d6bSBaptiste Daroussin 21861d06d6bSBaptiste Daroussin close(fds[0]); 21961d06d6bSBaptiste Daroussin close(fds[1]); 22061d06d6bSBaptiste Daroussin close(fds[2]); 22161d06d6bSBaptiste Daroussin 22261d06d6bSBaptiste Daroussin process(parser, outtype, formatter); 22361d06d6bSBaptiste Daroussin mparse_reset(parser); 2247295610fSBaptiste Daroussin if (outtype == OUTT_HTML) 2257295610fSBaptiste Daroussin html_reset(formatter); 22661d06d6bSBaptiste Daroussin 22761d06d6bSBaptiste Daroussin fflush(stdout); 22861d06d6bSBaptiste Daroussin fflush(stderr); 22961d06d6bSBaptiste Daroussin /* Close file descriptors by restoring the old ones. */ 23061d06d6bSBaptiste Daroussin if (dup2(old_stderr, STDERR_FILENO) == -1 || 23161d06d6bSBaptiste Daroussin dup2(old_stdout, STDOUT_FILENO) == -1 || 23261d06d6bSBaptiste Daroussin dup2(old_stdin, STDIN_FILENO) == -1) { 23361d06d6bSBaptiste Daroussin warn("dup2"); 23461d06d6bSBaptiste Daroussin state = -1; 23561d06d6bSBaptiste Daroussin break; 23661d06d6bSBaptiste Daroussin } 23761d06d6bSBaptiste Daroussin } 23861d06d6bSBaptiste Daroussin 23961d06d6bSBaptiste Daroussin close(clientfd); 24061d06d6bSBaptiste Daroussin switch (outtype) { 24161d06d6bSBaptiste Daroussin case OUTT_ASCII: 24261d06d6bSBaptiste Daroussin case OUTT_UTF8: 24361d06d6bSBaptiste Daroussin ascii_free(formatter); 24461d06d6bSBaptiste Daroussin break; 24561d06d6bSBaptiste Daroussin case OUTT_HTML: 24661d06d6bSBaptiste Daroussin html_free(formatter); 24761d06d6bSBaptiste Daroussin break; 24861d06d6bSBaptiste Daroussin } 24961d06d6bSBaptiste Daroussin mparse_free(parser); 25061d06d6bSBaptiste Daroussin mchars_free(); 251*c1c95addSBrooks Davis #if DEBUG_MEMORY 252*c1c95addSBrooks Davis mandoc_dbg_finish(); 253*c1c95addSBrooks Davis #endif 25461d06d6bSBaptiste Daroussin return state == -1 ? 1 : 0; 25561d06d6bSBaptiste Daroussin } 25661d06d6bSBaptiste Daroussin 25761d06d6bSBaptiste Daroussin static void 25861d06d6bSBaptiste Daroussin process(struct mparse *parser, enum outt outtype, void *formatter) 25961d06d6bSBaptiste Daroussin { 2607295610fSBaptiste Daroussin struct roff_meta *meta; 26161d06d6bSBaptiste Daroussin 26261d06d6bSBaptiste Daroussin mparse_readfd(parser, STDIN_FILENO, "<unixfd>"); 2637295610fSBaptiste Daroussin meta = mparse_result(parser); 2647295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MDOC) { 26561d06d6bSBaptiste Daroussin switch (outtype) { 26661d06d6bSBaptiste Daroussin case OUTT_ASCII: 26761d06d6bSBaptiste Daroussin case OUTT_UTF8: 2687295610fSBaptiste Daroussin terminal_mdoc(formatter, meta); 26961d06d6bSBaptiste Daroussin break; 27061d06d6bSBaptiste Daroussin case OUTT_HTML: 2717295610fSBaptiste Daroussin html_mdoc(formatter, meta); 27261d06d6bSBaptiste Daroussin break; 27361d06d6bSBaptiste Daroussin } 27461d06d6bSBaptiste Daroussin } 2757295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MAN) { 27661d06d6bSBaptiste Daroussin switch (outtype) { 27761d06d6bSBaptiste Daroussin case OUTT_ASCII: 27861d06d6bSBaptiste Daroussin case OUTT_UTF8: 2797295610fSBaptiste Daroussin terminal_man(formatter, meta); 28061d06d6bSBaptiste Daroussin break; 28161d06d6bSBaptiste Daroussin case OUTT_HTML: 2827295610fSBaptiste Daroussin html_man(formatter, meta); 28361d06d6bSBaptiste Daroussin break; 28461d06d6bSBaptiste Daroussin } 28561d06d6bSBaptiste Daroussin } 28661d06d6bSBaptiste Daroussin } 28761d06d6bSBaptiste Daroussin 28861d06d6bSBaptiste Daroussin void 28961d06d6bSBaptiste Daroussin usage(void) 29061d06d6bSBaptiste Daroussin { 29161d06d6bSBaptiste Daroussin fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n"); 29261d06d6bSBaptiste Daroussin exit(1); 29361d06d6bSBaptiste Daroussin } 294