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