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