1 /* $Id: catman.c,v 1.21 2017/02/18 12:24:24 schwarze Exp $ */
3 * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4 * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
24 #include <sys/types.h>
25 #include <sys/socket.h>
36 #include "compat_fts.h"
44 int process_manpage(int, int, const char *);
45 int process_tree(int, int);
46 void run_mandocd(int, const char *, const char *)
47 __attribute__((__noreturn__));
48 ssize_t sock_fd_write(int, int, int, int);
49 void usage(void) __attribute__((__noreturn__));
53 run_mandocd(int sockfd, const char *outtype, const char* defos)
57 if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
60 execlp("mandocd", "mandocd", "-T", outtype,
61 sockfdstr, (char *)NULL);
63 execlp("mandocd", "mandocd", "-T", outtype,
64 "-I", defos, sockfdstr, (char *)NULL);
69 sock_fd_write(int fd, int fd0, int fd1, int fd2)
71 const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
75 struct cmsghdr cmsghdr;
76 char control[CMSG_SPACE(3 * sizeof(int))];
81 unsigned char dummy[1] = {'\0'};
84 iov.iov_len = sizeof(dummy);
91 msg.msg_control = cmsgu.control;
92 msg.msg_controllen = sizeof(cmsgu.control);
94 cmsg = CMSG_FIRSTHDR(&msg);
95 cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
96 cmsg->cmsg_level = SOL_SOCKET;
97 cmsg->cmsg_type = SCM_RIGHTS;
99 walk = (int *)CMSG_DATA(cmsg);
105 * It appears that on some systems, sendmsg(3)
106 * may return EAGAIN even in blocking mode.
107 * Seen for example on Oracle Solaris 11.2.
108 * The sleeping time was chosen by experimentation,
109 * to neither cause more than a handful of retries
110 * in normal operation nor unnecessary delays.
113 if ((sz = sendmsg(fd, &msg, 0)) != -1 ||
116 nanosleep(&timeout, NULL);
122 process_manpage(int srv_fd, int dstdir_fd, const char *path)
127 if ((in_fd = open(path, O_RDONLY)) == -1) {
128 warn("open(%s)", path);
132 if ((out_fd = openat(dstdir_fd, path,
133 O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
134 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
135 warn("openat(%s)", path);
140 irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO);
153 process_tree(int srv_fd, int dstdir_fd)
161 argv[1] = (char *)NULL;
163 if ((ftsp = fts_open((char * const *)argv,
164 FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
169 while ((entry = fts_read(ftsp)) != NULL) {
170 path = entry->fts_path + 2;
171 switch (entry->fts_info) {
173 if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
180 mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
181 S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
183 warn("mkdirat(%s)", path);
184 (void)fts_set(ftsp, entry, FTS_SKIP);
190 warnx("%s: not a regular file", path);
200 main(int argc, char **argv)
202 const char *defos, *outtype;
210 while ((opt = getopt(argc, argv, "I:T:")) != -1) {
230 if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
231 err(1, "socketpair");
239 run_mandocd(srv_fds[1], outtype, defos);
245 if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
246 err(1, "open(%s)", argv[1]);
248 if (chdir(argv[0]) == -1)
249 err(1, "chdir(%s)", argv[0]);
251 return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
257 fprintf(stderr, "usage: %s [-I os=name] [-T output] "
258 "srcdir dstdir\n", BINM_CATMAN);