Annotation of mandoc/mandocd.c, Revision 1.13
1.13 ! schwarze 1: /* $Id: mandocd.c,v 1.12 2020/06/14 23:40:31 schwarze Exp $ */
1.1 schwarze 2: /*
3: * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
1.13 ! schwarze 4: * Copyright (c) 2017, 2019, 2021 Ingo Schwarze <schwarze@openbsd.org>
1.1 schwarze 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
30: #include <limits.h>
1.2 schwarze 31: #include <stdint.h>
1.1 schwarze 32: #include <stdio.h>
33: #include <stdlib.h>
34: #include <string.h>
35: #include <unistd.h>
36:
37: #include "mandoc.h"
1.13 ! schwarze 38: #if DEBUG_MEMORY
! 39: #define DEBUG_NODEF 1
! 40: #include "mandoc_dbg.h"
! 41: #endif
1.1 schwarze 42: #include "roff.h"
43: #include "mdoc.h"
44: #include "man.h"
1.7 schwarze 45: #include "mandoc_parse.h"
1.1 schwarze 46: #include "main.h"
47: #include "manconf.h"
48:
49: enum outt {
50: OUTT_ASCII = 0,
51: OUTT_UTF8,
52: OUTT_HTML
53: };
54:
55: static void process(struct mparse *, enum outt, void *);
56: static int read_fds(int, int *);
1.5 schwarze 57: static void usage(void) __attribute__((__noreturn__));
1.1 schwarze 58:
59:
60: #define NUM_FDS 3
61: static int
62: read_fds(int clientfd, int *fds)
63: {
64: struct msghdr msg;
65: struct iovec iov[1];
66: unsigned char dummy[1];
67: struct cmsghdr *cmsg;
68: int *walk;
69: int cnt;
70:
71: /* Union used for alignment. */
72: union {
73: uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
74: struct cmsghdr align;
75: } u;
76:
77: memset(&msg, '\0', sizeof(msg));
78: msg.msg_control = u.controlbuf;
79: msg.msg_controllen = sizeof(u.controlbuf);
80:
81: /*
82: * Read a dummy byte - sendmsg cannot send an empty message,
83: * even if we are only interested in the OOB data.
84: */
85:
86: iov[0].iov_base = dummy;
87: iov[0].iov_len = sizeof(dummy);
88: msg.msg_iov = iov;
89: msg.msg_iovlen = 1;
90:
91: switch (recvmsg(clientfd, &msg, 0)) {
92: case -1:
93: warn("recvmsg");
94: return -1;
95: case 0:
96: return 0;
97: default:
98: break;
99: }
100:
101: if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
102: warnx("CMSG_FIRSTHDR: missing control message");
103: return -1;
104: }
105:
106: if (cmsg->cmsg_level != SOL_SOCKET ||
107: cmsg->cmsg_type != SCM_RIGHTS ||
108: cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
109: warnx("CMSG_FIRSTHDR: invalid control message");
110: return -1;
111: }
112:
113: walk = (int *)CMSG_DATA(cmsg);
114: for (cnt = 0; cnt < NUM_FDS; cnt++)
115: fds[cnt] = *walk++;
116:
117: return 1;
118: }
119:
120: int
121: main(int argc, char *argv[])
122: {
123: struct manoutput options;
124: struct mparse *parser;
125: void *formatter;
1.3 schwarze 126: const char *defos;
1.1 schwarze 127: const char *errstr;
128: int clientfd;
129: int old_stdin;
130: int old_stdout;
131: int old_stderr;
132: int fds[3];
133: int state, opt;
134: enum outt outtype;
135:
1.13 ! schwarze 136: #if DEBUG_MEMORY
! 137: mandoc_dbg_init(argc, argv);
! 138: #endif
! 139:
1.3 schwarze 140: defos = NULL;
1.1 schwarze 141: outtype = OUTT_ASCII;
1.3 schwarze 142: while ((opt = getopt(argc, argv, "I:T:")) != -1) {
1.1 schwarze 143: switch (opt) {
1.3 schwarze 144: case 'I':
145: if (strncmp(optarg, "os=", 3) == 0)
146: defos = optarg + 3;
147: else {
148: warnx("-I %s: Bad argument", optarg);
149: usage();
150: }
151: break;
1.1 schwarze 152: case 'T':
153: if (strcmp(optarg, "ascii") == 0)
154: outtype = OUTT_ASCII;
155: else if (strcmp(optarg, "utf8") == 0)
156: outtype = OUTT_UTF8;
157: else if (strcmp(optarg, "html") == 0)
158: outtype = OUTT_HTML;
159: else {
160: warnx("-T %s: Bad argument", optarg);
161: usage();
162: }
163: break;
164: default:
165: usage();
166: }
167: }
168:
169: if (argc > 0) {
170: argc -= optind;
171: argv += optind;
172: }
173: if (argc != 1)
174: usage();
175:
176: errstr = NULL;
177: clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
178: if (errstr)
179: errx(1, "file descriptor %s %s", argv[1], errstr);
180:
181: mchars_alloc();
1.10 schwarze 182: parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
183: MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
1.1 schwarze 184:
185: memset(&options, 0, sizeof(options));
186: switch (outtype) {
187: case OUTT_ASCII:
188: formatter = ascii_alloc(&options);
189: break;
190: case OUTT_UTF8:
191: formatter = utf8_alloc(&options);
192: break;
193: case OUTT_HTML:
194: options.fragment = 1;
195: formatter = html_alloc(&options);
196: break;
197: }
198:
199: state = 1; /* work to do */
200: fflush(stdout);
201: fflush(stderr);
202: if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
203: (old_stdout = dup(STDOUT_FILENO)) == -1 ||
204: (old_stderr = dup(STDERR_FILENO)) == -1) {
205: warn("dup");
206: state = -1; /* error */
207: }
208:
209: while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
210: if (dup2(fds[0], STDIN_FILENO) == -1 ||
211: dup2(fds[1], STDOUT_FILENO) == -1 ||
212: dup2(fds[2], STDERR_FILENO) == -1) {
213: warn("dup2");
214: state = -1;
215: break;
216: }
217:
218: close(fds[0]);
219: close(fds[1]);
220: close(fds[2]);
221:
222: process(parser, outtype, formatter);
223: mparse_reset(parser);
1.11 schwarze 224: if (outtype == OUTT_HTML)
225: html_reset(formatter);
1.1 schwarze 226:
227: fflush(stdout);
228: fflush(stderr);
229: /* Close file descriptors by restoring the old ones. */
230: if (dup2(old_stderr, STDERR_FILENO) == -1 ||
231: dup2(old_stdout, STDOUT_FILENO) == -1 ||
232: dup2(old_stdin, STDIN_FILENO) == -1) {
233: warn("dup2");
234: state = -1;
235: break;
236: }
237: }
238:
239: close(clientfd);
240: switch (outtype) {
241: case OUTT_ASCII:
242: case OUTT_UTF8:
243: ascii_free(formatter);
244: break;
245: case OUTT_HTML:
246: html_free(formatter);
247: break;
248: }
249: mparse_free(parser);
250: mchars_free();
1.13 ! schwarze 251: #if DEBUG_MEMORY
! 252: mandoc_dbg_finish();
! 253: #endif
1.1 schwarze 254: return state == -1 ? 1 : 0;
255: }
256:
257: static void
258: process(struct mparse *parser, enum outt outtype, void *formatter)
259: {
1.9 schwarze 260: struct roff_meta *meta;
1.1 schwarze 261:
262: mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
1.9 schwarze 263: meta = mparse_result(parser);
264: if (meta->macroset == MACROSET_MDOC) {
1.1 schwarze 265: switch (outtype) {
266: case OUTT_ASCII:
267: case OUTT_UTF8:
1.9 schwarze 268: terminal_mdoc(formatter, meta);
1.1 schwarze 269: break;
270: case OUTT_HTML:
1.9 schwarze 271: html_mdoc(formatter, meta);
1.1 schwarze 272: break;
273: }
274: }
1.9 schwarze 275: if (meta->macroset == MACROSET_MAN) {
1.1 schwarze 276: switch (outtype) {
277: case OUTT_ASCII:
278: case OUTT_UTF8:
1.9 schwarze 279: terminal_man(formatter, meta);
1.1 schwarze 280: break;
281: case OUTT_HTML:
1.9 schwarze 282: html_man(formatter, meta);
1.1 schwarze 283: break;
284: }
285: }
286: }
287:
288: void
289: usage(void)
290: {
1.3 schwarze 291: fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
1.1 schwarze 292: exit(1);
293: }
CVSweb