xref: /freebsd/contrib/mandoc/catman.c (revision 6d38604fc532a3fc060788e3ce40464b46047eaf)
1*6d38604fSBaptiste Daroussin /*	$Id: catman.c,v 1.22 2020/06/14 23:40:31 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
461d06d6bSBaptiste Daroussin  * Copyright (c) 2017 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 
20*6d38604fSBaptiste 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 #include <sys/stat.h>
2761d06d6bSBaptiste Daroussin 
2861d06d6bSBaptiste Daroussin #if HAVE_ERR
2961d06d6bSBaptiste Daroussin #include <err.h>
3061d06d6bSBaptiste Daroussin #endif
3161d06d6bSBaptiste Daroussin #include <errno.h>
3261d06d6bSBaptiste Daroussin #include <fcntl.h>
3361d06d6bSBaptiste Daroussin #if HAVE_FTS
3461d06d6bSBaptiste Daroussin #include <fts.h>
3561d06d6bSBaptiste Daroussin #else
3661d06d6bSBaptiste Daroussin #include "compat_fts.h"
3761d06d6bSBaptiste Daroussin #endif
3861d06d6bSBaptiste Daroussin #include <stdio.h>
3961d06d6bSBaptiste Daroussin #include <stdlib.h>
4061d06d6bSBaptiste Daroussin #include <string.h>
4161d06d6bSBaptiste Daroussin #include <time.h>
4261d06d6bSBaptiste Daroussin #include <unistd.h>
4361d06d6bSBaptiste Daroussin 
4461d06d6bSBaptiste Daroussin int	 process_manpage(int, int, const char *);
4561d06d6bSBaptiste Daroussin int	 process_tree(int, int);
4661d06d6bSBaptiste Daroussin void	 run_mandocd(int, const char *, const char *)
4761d06d6bSBaptiste Daroussin 		__attribute__((__noreturn__));
4861d06d6bSBaptiste Daroussin ssize_t	 sock_fd_write(int, int, int, int);
4961d06d6bSBaptiste Daroussin void	 usage(void) __attribute__((__noreturn__));
5061d06d6bSBaptiste Daroussin 
5161d06d6bSBaptiste Daroussin 
5261d06d6bSBaptiste Daroussin void
run_mandocd(int sockfd,const char * outtype,const char * defos)5361d06d6bSBaptiste Daroussin run_mandocd(int sockfd, const char *outtype, const char* defos)
5461d06d6bSBaptiste Daroussin {
5561d06d6bSBaptiste Daroussin 	char	 sockfdstr[10];
5661d06d6bSBaptiste Daroussin 
5761d06d6bSBaptiste Daroussin 	if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
5861d06d6bSBaptiste Daroussin 		err(1, "snprintf");
5961d06d6bSBaptiste Daroussin 	if (defos == NULL)
6061d06d6bSBaptiste Daroussin 		execlp("mandocd", "mandocd", "-T", outtype,
6161d06d6bSBaptiste Daroussin 		    sockfdstr, (char *)NULL);
6261d06d6bSBaptiste Daroussin 	else
6361d06d6bSBaptiste Daroussin 		execlp("mandocd", "mandocd", "-T", outtype,
6461d06d6bSBaptiste Daroussin 		    "-I", defos, sockfdstr, (char *)NULL);
6561d06d6bSBaptiste Daroussin 	err(1, "exec");
6661d06d6bSBaptiste Daroussin }
6761d06d6bSBaptiste Daroussin 
6861d06d6bSBaptiste Daroussin ssize_t
sock_fd_write(int fd,int fd0,int fd1,int fd2)6961d06d6bSBaptiste Daroussin sock_fd_write(int fd, int fd0, int fd1, int fd2)
7061d06d6bSBaptiste Daroussin {
7161d06d6bSBaptiste Daroussin 	const struct timespec timeout = { 0, 10000000 };  /* 0.01 s */
7261d06d6bSBaptiste Daroussin 	struct msghdr	 msg;
7361d06d6bSBaptiste Daroussin 	struct iovec	 iov;
7461d06d6bSBaptiste Daroussin 	union {
7561d06d6bSBaptiste Daroussin 		struct cmsghdr	 cmsghdr;
7661d06d6bSBaptiste Daroussin 		char		 control[CMSG_SPACE(3 * sizeof(int))];
7761d06d6bSBaptiste Daroussin 	} cmsgu;
7861d06d6bSBaptiste Daroussin 	struct cmsghdr	*cmsg;
7961d06d6bSBaptiste Daroussin 	int		*walk;
8061d06d6bSBaptiste Daroussin 	ssize_t		 sz;
8161d06d6bSBaptiste Daroussin 	unsigned char	 dummy[1] = {'\0'};
8261d06d6bSBaptiste Daroussin 
8361d06d6bSBaptiste Daroussin 	iov.iov_base = dummy;
8461d06d6bSBaptiste Daroussin 	iov.iov_len = sizeof(dummy);
8561d06d6bSBaptiste Daroussin 
8661d06d6bSBaptiste Daroussin 	msg.msg_name = NULL;
8761d06d6bSBaptiste Daroussin 	msg.msg_namelen = 0;
8861d06d6bSBaptiste Daroussin 	msg.msg_iov = &iov;
8961d06d6bSBaptiste Daroussin 	msg.msg_iovlen = 1;
9061d06d6bSBaptiste Daroussin 
9161d06d6bSBaptiste Daroussin 	msg.msg_control = cmsgu.control;
9261d06d6bSBaptiste Daroussin 	msg.msg_controllen = sizeof(cmsgu.control);
9361d06d6bSBaptiste Daroussin 
9461d06d6bSBaptiste Daroussin 	cmsg = CMSG_FIRSTHDR(&msg);
9561d06d6bSBaptiste Daroussin 	cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
9661d06d6bSBaptiste Daroussin 	cmsg->cmsg_level = SOL_SOCKET;
9761d06d6bSBaptiste Daroussin 	cmsg->cmsg_type = SCM_RIGHTS;
9861d06d6bSBaptiste Daroussin 
9961d06d6bSBaptiste Daroussin 	walk = (int *)CMSG_DATA(cmsg);
10061d06d6bSBaptiste Daroussin 	*(walk++) = fd0;
10161d06d6bSBaptiste Daroussin 	*(walk++) = fd1;
10261d06d6bSBaptiste Daroussin 	*(walk++) = fd2;
10361d06d6bSBaptiste Daroussin 
10461d06d6bSBaptiste Daroussin 	/*
10561d06d6bSBaptiste Daroussin 	 * It appears that on some systems, sendmsg(3)
10661d06d6bSBaptiste Daroussin 	 * may return EAGAIN even in blocking mode.
10761d06d6bSBaptiste Daroussin 	 * Seen for example on Oracle Solaris 11.2.
10861d06d6bSBaptiste Daroussin 	 * The sleeping time was chosen by experimentation,
10961d06d6bSBaptiste Daroussin 	 * to neither cause more than a handful of retries
11061d06d6bSBaptiste Daroussin 	 * in normal operation nor unnecessary delays.
11161d06d6bSBaptiste Daroussin 	 */
11261d06d6bSBaptiste Daroussin 	for (;;) {
11361d06d6bSBaptiste Daroussin 		if ((sz = sendmsg(fd, &msg, 0)) != -1 ||
11461d06d6bSBaptiste Daroussin 		    errno != EAGAIN)
11561d06d6bSBaptiste Daroussin 			break;
11661d06d6bSBaptiste Daroussin 		nanosleep(&timeout, NULL);
11761d06d6bSBaptiste Daroussin 	}
11861d06d6bSBaptiste Daroussin 	return sz;
11961d06d6bSBaptiste Daroussin }
12061d06d6bSBaptiste Daroussin 
12161d06d6bSBaptiste Daroussin int
process_manpage(int srv_fd,int dstdir_fd,const char * path)12261d06d6bSBaptiste Daroussin process_manpage(int srv_fd, int dstdir_fd, const char *path)
12361d06d6bSBaptiste Daroussin {
12461d06d6bSBaptiste Daroussin 	int	 in_fd, out_fd;
12561d06d6bSBaptiste Daroussin 	int	 irc;
12661d06d6bSBaptiste Daroussin 
12761d06d6bSBaptiste Daroussin 	if ((in_fd = open(path, O_RDONLY)) == -1) {
12861d06d6bSBaptiste Daroussin 		warn("open(%s)", path);
12961d06d6bSBaptiste Daroussin 		return 0;
13061d06d6bSBaptiste Daroussin 	}
13161d06d6bSBaptiste Daroussin 
13261d06d6bSBaptiste Daroussin 	if ((out_fd = openat(dstdir_fd, path,
13361d06d6bSBaptiste Daroussin 	    O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
13461d06d6bSBaptiste Daroussin 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
13561d06d6bSBaptiste Daroussin 		warn("openat(%s)", path);
13661d06d6bSBaptiste Daroussin 		close(in_fd);
13761d06d6bSBaptiste Daroussin 		return 0;
13861d06d6bSBaptiste Daroussin 	}
13961d06d6bSBaptiste Daroussin 
14061d06d6bSBaptiste Daroussin 	irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO);
14161d06d6bSBaptiste Daroussin 
14261d06d6bSBaptiste Daroussin 	close(in_fd);
14361d06d6bSBaptiste Daroussin 	close(out_fd);
14461d06d6bSBaptiste Daroussin 
14561d06d6bSBaptiste Daroussin 	if (irc < 0) {
14661d06d6bSBaptiste Daroussin 		warn("sendmsg");
14761d06d6bSBaptiste Daroussin 		return -1;
14861d06d6bSBaptiste Daroussin 	}
14961d06d6bSBaptiste Daroussin 	return 0;
15061d06d6bSBaptiste Daroussin }
15161d06d6bSBaptiste Daroussin 
15261d06d6bSBaptiste Daroussin int
process_tree(int srv_fd,int dstdir_fd)15361d06d6bSBaptiste Daroussin process_tree(int srv_fd, int dstdir_fd)
15461d06d6bSBaptiste Daroussin {
15561d06d6bSBaptiste Daroussin 	FTS		*ftsp;
15661d06d6bSBaptiste Daroussin 	FTSENT		*entry;
15761d06d6bSBaptiste Daroussin 	const char	*argv[2];
15861d06d6bSBaptiste Daroussin 	const char	*path;
15961d06d6bSBaptiste Daroussin 
16061d06d6bSBaptiste Daroussin 	argv[0] = ".";
16161d06d6bSBaptiste Daroussin 	argv[1] = (char *)NULL;
16261d06d6bSBaptiste Daroussin 
16361d06d6bSBaptiste Daroussin 	if ((ftsp = fts_open((char * const *)argv,
16461d06d6bSBaptiste Daroussin 	    FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
16561d06d6bSBaptiste Daroussin 		warn("fts_open");
16661d06d6bSBaptiste Daroussin 		return -1;
16761d06d6bSBaptiste Daroussin 	}
16861d06d6bSBaptiste Daroussin 
16961d06d6bSBaptiste Daroussin 	while ((entry = fts_read(ftsp)) != NULL) {
17061d06d6bSBaptiste Daroussin 		path = entry->fts_path + 2;
17161d06d6bSBaptiste Daroussin 		switch (entry->fts_info) {
17261d06d6bSBaptiste Daroussin 		case FTS_F:
17361d06d6bSBaptiste Daroussin 			if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
17461d06d6bSBaptiste Daroussin 				fts_close(ftsp);
17561d06d6bSBaptiste Daroussin 				return -1;
17661d06d6bSBaptiste Daroussin 			}
17761d06d6bSBaptiste Daroussin 			break;
17861d06d6bSBaptiste Daroussin 		case FTS_D:
17961d06d6bSBaptiste Daroussin 			if (*path != '\0' &&
18061d06d6bSBaptiste Daroussin 			    mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
18161d06d6bSBaptiste Daroussin 			      S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
18261d06d6bSBaptiste Daroussin 			    errno != EEXIST) {
18361d06d6bSBaptiste Daroussin 				warn("mkdirat(%s)", path);
18461d06d6bSBaptiste Daroussin 				(void)fts_set(ftsp, entry, FTS_SKIP);
18561d06d6bSBaptiste Daroussin 			}
18661d06d6bSBaptiste Daroussin 			break;
18761d06d6bSBaptiste Daroussin 		case FTS_DP:
18861d06d6bSBaptiste Daroussin 			break;
18961d06d6bSBaptiste Daroussin 		default:
19061d06d6bSBaptiste Daroussin 			warnx("%s: not a regular file", path);
19161d06d6bSBaptiste Daroussin 			break;
19261d06d6bSBaptiste Daroussin 		}
19361d06d6bSBaptiste Daroussin 	}
19461d06d6bSBaptiste Daroussin 
19561d06d6bSBaptiste Daroussin 	fts_close(ftsp);
19661d06d6bSBaptiste Daroussin 	return 0;
19761d06d6bSBaptiste Daroussin }
19861d06d6bSBaptiste Daroussin 
19961d06d6bSBaptiste Daroussin int
main(int argc,char ** argv)20061d06d6bSBaptiste Daroussin main(int argc, char **argv)
20161d06d6bSBaptiste Daroussin {
20261d06d6bSBaptiste Daroussin 	const char	*defos, *outtype;
20361d06d6bSBaptiste Daroussin 	int		 srv_fds[2];
20461d06d6bSBaptiste Daroussin 	int		 dstdir_fd;
20561d06d6bSBaptiste Daroussin 	int		 opt;
20661d06d6bSBaptiste Daroussin 	pid_t		 pid;
20761d06d6bSBaptiste Daroussin 
20861d06d6bSBaptiste Daroussin 	defos = NULL;
20961d06d6bSBaptiste Daroussin 	outtype = "ascii";
21061d06d6bSBaptiste Daroussin 	while ((opt = getopt(argc, argv, "I:T:")) != -1) {
21161d06d6bSBaptiste Daroussin 		switch (opt) {
21261d06d6bSBaptiste Daroussin 		case 'I':
21361d06d6bSBaptiste Daroussin 			defos = optarg;
21461d06d6bSBaptiste Daroussin 			break;
21561d06d6bSBaptiste Daroussin 		case 'T':
21661d06d6bSBaptiste Daroussin 			outtype = optarg;
21761d06d6bSBaptiste Daroussin 			break;
21861d06d6bSBaptiste Daroussin 		default:
21961d06d6bSBaptiste Daroussin 			usage();
22061d06d6bSBaptiste Daroussin 		}
22161d06d6bSBaptiste Daroussin 	}
22261d06d6bSBaptiste Daroussin 
22361d06d6bSBaptiste Daroussin 	if (argc > 0) {
22461d06d6bSBaptiste Daroussin 		argc -= optind;
22561d06d6bSBaptiste Daroussin 		argv += optind;
22661d06d6bSBaptiste Daroussin 	}
22761d06d6bSBaptiste Daroussin 	if (argc != 2)
22861d06d6bSBaptiste Daroussin 		usage();
22961d06d6bSBaptiste Daroussin 
23061d06d6bSBaptiste Daroussin 	if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
23161d06d6bSBaptiste Daroussin 		err(1, "socketpair");
23261d06d6bSBaptiste Daroussin 
23361d06d6bSBaptiste Daroussin 	pid = fork();
23461d06d6bSBaptiste Daroussin 	switch (pid) {
23561d06d6bSBaptiste Daroussin 	case -1:
23661d06d6bSBaptiste Daroussin 		err(1, "fork");
23761d06d6bSBaptiste Daroussin 	case 0:
23861d06d6bSBaptiste Daroussin 		close(srv_fds[0]);
23961d06d6bSBaptiste Daroussin 		run_mandocd(srv_fds[1], outtype, defos);
24061d06d6bSBaptiste Daroussin 	default:
24161d06d6bSBaptiste Daroussin 		break;
24261d06d6bSBaptiste Daroussin 	}
24361d06d6bSBaptiste Daroussin 	close(srv_fds[1]);
24461d06d6bSBaptiste Daroussin 
24561d06d6bSBaptiste Daroussin 	if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
24661d06d6bSBaptiste Daroussin 		err(1, "open(%s)", argv[1]);
24761d06d6bSBaptiste Daroussin 
24861d06d6bSBaptiste Daroussin 	if (chdir(argv[0]) == -1)
24961d06d6bSBaptiste Daroussin 		err(1, "chdir(%s)", argv[0]);
25061d06d6bSBaptiste Daroussin 
25161d06d6bSBaptiste Daroussin 	return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
25261d06d6bSBaptiste Daroussin }
25361d06d6bSBaptiste Daroussin 
25461d06d6bSBaptiste Daroussin void
usage(void)25561d06d6bSBaptiste Daroussin usage(void)
25661d06d6bSBaptiste Daroussin {
25761d06d6bSBaptiste Daroussin 	fprintf(stderr, "usage: %s [-I os=name] [-T output] "
25861d06d6bSBaptiste Daroussin 	    "srcdir dstdir\n", BINM_CATMAN);
25961d06d6bSBaptiste Daroussin 	exit(1);
26061d06d6bSBaptiste Daroussin }
261