1*544c191cSchristos /* Id: mandocd.c,v 1.11 2019/03/03 13:02:11 schwarze Exp */
29508192eSchristos /*
39508192eSchristos * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4*544c191cSchristos * Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
59508192eSchristos *
69508192eSchristos * Permission to use, copy, modify, and distribute this software for any
79508192eSchristos * purpose with or without fee is hereby granted, provided that the above
89508192eSchristos * copyright notice and this permission notice appear in all copies.
99508192eSchristos *
109508192eSchristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
119508192eSchristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129508192eSchristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
139508192eSchristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
149508192eSchristos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
159508192eSchristos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
169508192eSchristos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
179508192eSchristos */
189508192eSchristos #include "config.h"
199508192eSchristos
209508192eSchristos #if HAVE_CMSG_XPG42
219508192eSchristos #define _XPG4_2
229508192eSchristos #endif
239508192eSchristos
249508192eSchristos #include <sys/types.h>
259508192eSchristos #include <sys/socket.h>
269508192eSchristos
279508192eSchristos #if HAVE_ERR
289508192eSchristos #include <err.h>
299508192eSchristos #endif
309508192eSchristos #include <limits.h>
319508192eSchristos #include <stdint.h>
329508192eSchristos #include <stdio.h>
339508192eSchristos #include <stdlib.h>
349508192eSchristos #include <string.h>
359508192eSchristos #include <unistd.h>
369508192eSchristos
379508192eSchristos #include "mandoc.h"
389508192eSchristos #include "roff.h"
399508192eSchristos #include "mdoc.h"
409508192eSchristos #include "man.h"
41*544c191cSchristos #include "mandoc_parse.h"
429508192eSchristos #include "main.h"
439508192eSchristos #include "manconf.h"
449508192eSchristos
459508192eSchristos enum outt {
469508192eSchristos OUTT_ASCII = 0,
479508192eSchristos OUTT_UTF8,
489508192eSchristos OUTT_HTML
499508192eSchristos };
509508192eSchristos
519508192eSchristos static void process(struct mparse *, enum outt, void *);
529508192eSchristos static int read_fds(int, int *);
539508192eSchristos static void usage(void) __attribute__((__noreturn__));
549508192eSchristos
559508192eSchristos
569508192eSchristos #define NUM_FDS 3
579508192eSchristos static int
read_fds(int clientfd,int * fds)589508192eSchristos read_fds(int clientfd, int *fds)
599508192eSchristos {
609508192eSchristos struct msghdr msg;
619508192eSchristos struct iovec iov[1];
629508192eSchristos unsigned char dummy[1];
639508192eSchristos struct cmsghdr *cmsg;
649508192eSchristos int *walk;
659508192eSchristos int cnt;
669508192eSchristos
679508192eSchristos /* Union used for alignment. */
689508192eSchristos union {
699508192eSchristos uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
709508192eSchristos struct cmsghdr align;
719508192eSchristos } u;
729508192eSchristos
739508192eSchristos memset(&msg, '\0', sizeof(msg));
749508192eSchristos msg.msg_control = u.controlbuf;
759508192eSchristos msg.msg_controllen = sizeof(u.controlbuf);
769508192eSchristos
779508192eSchristos /*
789508192eSchristos * Read a dummy byte - sendmsg cannot send an empty message,
799508192eSchristos * even if we are only interested in the OOB data.
809508192eSchristos */
819508192eSchristos
829508192eSchristos iov[0].iov_base = dummy;
839508192eSchristos iov[0].iov_len = sizeof(dummy);
849508192eSchristos msg.msg_iov = iov;
859508192eSchristos msg.msg_iovlen = 1;
869508192eSchristos
879508192eSchristos switch (recvmsg(clientfd, &msg, 0)) {
889508192eSchristos case -1:
899508192eSchristos warn("recvmsg");
909508192eSchristos return -1;
919508192eSchristos case 0:
929508192eSchristos return 0;
939508192eSchristos default:
949508192eSchristos break;
959508192eSchristos }
969508192eSchristos
979508192eSchristos if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
989508192eSchristos warnx("CMSG_FIRSTHDR: missing control message");
999508192eSchristos return -1;
1009508192eSchristos }
1019508192eSchristos
1029508192eSchristos if (cmsg->cmsg_level != SOL_SOCKET ||
1039508192eSchristos cmsg->cmsg_type != SCM_RIGHTS ||
1049508192eSchristos cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
1059508192eSchristos warnx("CMSG_FIRSTHDR: invalid control message");
1069508192eSchristos return -1;
1079508192eSchristos }
1089508192eSchristos
1099508192eSchristos walk = (int *)CMSG_DATA(cmsg);
1109508192eSchristos for (cnt = 0; cnt < NUM_FDS; cnt++)
1119508192eSchristos fds[cnt] = *walk++;
1129508192eSchristos
1139508192eSchristos return 1;
1149508192eSchristos }
1159508192eSchristos
1169508192eSchristos int
main(int argc,char * argv[])1179508192eSchristos main(int argc, char *argv[])
1189508192eSchristos {
1199508192eSchristos struct manoutput options;
1209508192eSchristos struct mparse *parser;
1219508192eSchristos void *formatter;
1229508192eSchristos const char *defos;
1239508192eSchristos const char *errstr;
1249508192eSchristos int clientfd;
1259508192eSchristos int old_stdin;
1269508192eSchristos int old_stdout;
1279508192eSchristos int old_stderr;
1289508192eSchristos int fds[3];
1299508192eSchristos int state, opt;
1309508192eSchristos enum outt outtype;
1319508192eSchristos
1329508192eSchristos defos = NULL;
1339508192eSchristos outtype = OUTT_ASCII;
1349508192eSchristos while ((opt = getopt(argc, argv, "I:T:")) != -1) {
1359508192eSchristos switch (opt) {
1369508192eSchristos case 'I':
1379508192eSchristos if (strncmp(optarg, "os=", 3) == 0)
1389508192eSchristos defos = optarg + 3;
1399508192eSchristos else {
1409508192eSchristos warnx("-I %s: Bad argument", optarg);
1419508192eSchristos usage();
1429508192eSchristos }
1439508192eSchristos break;
1449508192eSchristos case 'T':
1459508192eSchristos if (strcmp(optarg, "ascii") == 0)
1469508192eSchristos outtype = OUTT_ASCII;
1479508192eSchristos else if (strcmp(optarg, "utf8") == 0)
1489508192eSchristos outtype = OUTT_UTF8;
1499508192eSchristos else if (strcmp(optarg, "html") == 0)
1509508192eSchristos outtype = OUTT_HTML;
1519508192eSchristos else {
1529508192eSchristos warnx("-T %s: Bad argument", optarg);
1539508192eSchristos usage();
1549508192eSchristos }
1559508192eSchristos break;
1569508192eSchristos default:
1579508192eSchristos usage();
1589508192eSchristos }
1599508192eSchristos }
1609508192eSchristos
1619508192eSchristos if (argc > 0) {
1629508192eSchristos argc -= optind;
1639508192eSchristos argv += optind;
1649508192eSchristos }
1659508192eSchristos if (argc != 1)
1669508192eSchristos usage();
1679508192eSchristos
1689508192eSchristos errstr = NULL;
1699508192eSchristos clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
1709508192eSchristos if (errstr)
1719508192eSchristos errx(1, "file descriptor %s %s", argv[1], errstr);
1729508192eSchristos
1739508192eSchristos mchars_alloc();
174*544c191cSchristos parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
175*544c191cSchristos MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
1769508192eSchristos
1779508192eSchristos memset(&options, 0, sizeof(options));
1789508192eSchristos switch (outtype) {
1799508192eSchristos case OUTT_ASCII:
1809508192eSchristos formatter = ascii_alloc(&options);
1819508192eSchristos break;
1829508192eSchristos case OUTT_UTF8:
1839508192eSchristos formatter = utf8_alloc(&options);
1849508192eSchristos break;
1859508192eSchristos case OUTT_HTML:
1869508192eSchristos options.fragment = 1;
1879508192eSchristos formatter = html_alloc(&options);
1889508192eSchristos break;
1899508192eSchristos }
1909508192eSchristos
1919508192eSchristos state = 1; /* work to do */
1929508192eSchristos fflush(stdout);
1939508192eSchristos fflush(stderr);
1949508192eSchristos if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
1959508192eSchristos (old_stdout = dup(STDOUT_FILENO)) == -1 ||
1969508192eSchristos (old_stderr = dup(STDERR_FILENO)) == -1) {
1979508192eSchristos warn("dup");
1989508192eSchristos state = -1; /* error */
1999508192eSchristos }
2009508192eSchristos
2019508192eSchristos while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
2029508192eSchristos if (dup2(fds[0], STDIN_FILENO) == -1 ||
2039508192eSchristos dup2(fds[1], STDOUT_FILENO) == -1 ||
2049508192eSchristos dup2(fds[2], STDERR_FILENO) == -1) {
2059508192eSchristos warn("dup2");
2069508192eSchristos state = -1;
2079508192eSchristos break;
2089508192eSchristos }
2099508192eSchristos
2109508192eSchristos close(fds[0]);
2119508192eSchristos close(fds[1]);
2129508192eSchristos close(fds[2]);
2139508192eSchristos
2149508192eSchristos process(parser, outtype, formatter);
2159508192eSchristos mparse_reset(parser);
216*544c191cSchristos if (outtype == OUTT_HTML)
217*544c191cSchristos html_reset(formatter);
2189508192eSchristos
2199508192eSchristos fflush(stdout);
2209508192eSchristos fflush(stderr);
2219508192eSchristos /* Close file descriptors by restoring the old ones. */
2229508192eSchristos if (dup2(old_stderr, STDERR_FILENO) == -1 ||
2239508192eSchristos dup2(old_stdout, STDOUT_FILENO) == -1 ||
2249508192eSchristos dup2(old_stdin, STDIN_FILENO) == -1) {
2259508192eSchristos warn("dup2");
2269508192eSchristos state = -1;
2279508192eSchristos break;
2289508192eSchristos }
2299508192eSchristos }
2309508192eSchristos
2319508192eSchristos close(clientfd);
2329508192eSchristos switch (outtype) {
2339508192eSchristos case OUTT_ASCII:
2349508192eSchristos case OUTT_UTF8:
2359508192eSchristos ascii_free(formatter);
2369508192eSchristos break;
2379508192eSchristos case OUTT_HTML:
2389508192eSchristos html_free(formatter);
2399508192eSchristos break;
2409508192eSchristos }
2419508192eSchristos mparse_free(parser);
2429508192eSchristos mchars_free();
2439508192eSchristos return state == -1 ? 1 : 0;
2449508192eSchristos }
2459508192eSchristos
2469508192eSchristos static void
process(struct mparse * parser,enum outt outtype,void * formatter)2479508192eSchristos process(struct mparse *parser, enum outt outtype, void *formatter)
2489508192eSchristos {
249*544c191cSchristos struct roff_meta *meta;
2509508192eSchristos
2519508192eSchristos mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
252*544c191cSchristos meta = mparse_result(parser);
253*544c191cSchristos if (meta->macroset == MACROSET_MDOC) {
2549508192eSchristos switch (outtype) {
2559508192eSchristos case OUTT_ASCII:
2569508192eSchristos case OUTT_UTF8:
257*544c191cSchristos terminal_mdoc(formatter, meta);
2589508192eSchristos break;
2599508192eSchristos case OUTT_HTML:
260*544c191cSchristos html_mdoc(formatter, meta);
2619508192eSchristos break;
2629508192eSchristos }
2639508192eSchristos }
264*544c191cSchristos if (meta->macroset == MACROSET_MAN) {
2659508192eSchristos switch (outtype) {
2669508192eSchristos case OUTT_ASCII:
2679508192eSchristos case OUTT_UTF8:
268*544c191cSchristos terminal_man(formatter, meta);
2699508192eSchristos break;
2709508192eSchristos case OUTT_HTML:
271*544c191cSchristos html_man(formatter, meta);
2729508192eSchristos break;
2739508192eSchristos }
2749508192eSchristos }
2759508192eSchristos }
2769508192eSchristos
2779508192eSchristos void
usage(void)2789508192eSchristos usage(void)
2799508192eSchristos {
2809508192eSchristos fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
2819508192eSchristos exit(1);
2829508192eSchristos }
283