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