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