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
read_fds(int clientfd,int * fds)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
main(int argc,char * argv[])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
process(struct mparse * parser,enum outt outtype,void * formatter)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
usage(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