summaryrefslogtreecommitdiff
path: root/catman.c
diff options
context:
space:
mode:
Diffstat (limited to 'catman.c')
-rw-r--r--catman.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/catman.c b/catman.c
new file mode 100644
index 0000000000000..7d62c0e74addd
--- /dev/null
+++ b/catman.c
@@ -0,0 +1,260 @@
+/* $Id: catman.c,v 1.21 2017/02/18 12:24:24 schwarze Exp $ */
+/*
+ * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
+ * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+
+#if HAVE_CMSG_XPG42
+#define _XPG4_2
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#if HAVE_ERR
+#include <err.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#if HAVE_FTS
+#include <fts.h>
+#else
+#include "compat_fts.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+int process_manpage(int, int, const char *);
+int process_tree(int, int);
+void run_mandocd(int, const char *, const char *)
+ __attribute__((__noreturn__));
+ssize_t sock_fd_write(int, int, int, int);
+void usage(void) __attribute__((__noreturn__));
+
+
+void
+run_mandocd(int sockfd, const char *outtype, const char* defos)
+{
+ char sockfdstr[10];
+
+ if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
+ err(1, "snprintf");
+ if (defos == NULL)
+ execlp("mandocd", "mandocd", "-T", outtype,
+ sockfdstr, (char *)NULL);
+ else
+ execlp("mandocd", "mandocd", "-T", outtype,
+ "-I", defos, sockfdstr, (char *)NULL);
+ err(1, "exec");
+}
+
+ssize_t
+sock_fd_write(int fd, int fd0, int fd1, int fd2)
+{
+ const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ struct cmsghdr cmsghdr;
+ char control[CMSG_SPACE(3 * sizeof(int))];
+ } cmsgu;
+ struct cmsghdr *cmsg;
+ int *walk;
+ ssize_t sz;
+ unsigned char dummy[1] = {'\0'};
+
+ iov.iov_base = dummy;
+ iov.iov_len = sizeof(dummy);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ walk = (int *)CMSG_DATA(cmsg);
+ *(walk++) = fd0;
+ *(walk++) = fd1;
+ *(walk++) = fd2;
+
+ /*
+ * It appears that on some systems, sendmsg(3)
+ * may return EAGAIN even in blocking mode.
+ * Seen for example on Oracle Solaris 11.2.
+ * The sleeping time was chosen by experimentation,
+ * to neither cause more than a handful of retries
+ * in normal operation nor unnecessary delays.
+ */
+ for (;;) {
+ if ((sz = sendmsg(fd, &msg, 0)) != -1 ||
+ errno != EAGAIN)
+ break;
+ nanosleep(&timeout, NULL);
+ }
+ return sz;
+}
+
+int
+process_manpage(int srv_fd, int dstdir_fd, const char *path)
+{
+ int in_fd, out_fd;
+ int irc;
+
+ if ((in_fd = open(path, O_RDONLY)) == -1) {
+ warn("open(%s)", path);
+ return 0;
+ }
+
+ if ((out_fd = openat(dstdir_fd, path,
+ O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
+ warn("openat(%s)", path);
+ close(in_fd);
+ return 0;
+ }
+
+ irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO);
+
+ close(in_fd);
+ close(out_fd);
+
+ if (irc < 0) {
+ warn("sendmsg");
+ return -1;
+ }
+ return 0;
+}
+
+int
+process_tree(int srv_fd, int dstdir_fd)
+{
+ FTS *ftsp;
+ FTSENT *entry;
+ const char *argv[2];
+ const char *path;
+
+ argv[0] = ".";
+ argv[1] = (char *)NULL;
+
+ if ((ftsp = fts_open((char * const *)argv,
+ FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
+ warn("fts_open");
+ return -1;
+ }
+
+ while ((entry = fts_read(ftsp)) != NULL) {
+ path = entry->fts_path + 2;
+ switch (entry->fts_info) {
+ case FTS_F:
+ if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
+ fts_close(ftsp);
+ return -1;
+ }
+ break;
+ case FTS_D:
+ if (*path != '\0' &&
+ mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
+ S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
+ errno != EEXIST) {
+ warn("mkdirat(%s)", path);
+ (void)fts_set(ftsp, entry, FTS_SKIP);
+ }
+ break;
+ case FTS_DP:
+ break;
+ default:
+ warnx("%s: not a regular file", path);
+ break;
+ }
+ }
+
+ fts_close(ftsp);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *defos, *outtype;
+ int srv_fds[2];
+ int dstdir_fd;
+ int opt;
+ pid_t pid;
+
+ defos = NULL;
+ outtype = "ascii";
+ while ((opt = getopt(argc, argv, "I:T:")) != -1) {
+ switch (opt) {
+ case 'I':
+ defos = optarg;
+ break;
+ case 'T':
+ outtype = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc > 0) {
+ argc -= optind;
+ argv += optind;
+ }
+ if (argc != 2)
+ usage();
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
+ err(1, "socketpair");
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "fork");
+ case 0:
+ close(srv_fds[0]);
+ run_mandocd(srv_fds[1], outtype, defos);
+ default:
+ break;
+ }
+ close(srv_fds[1]);
+
+ if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
+ err(1, "open(%s)", argv[1]);
+
+ if (chdir(argv[0]) == -1)
+ err(1, "chdir(%s)", argv[0]);
+
+ return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-I os=name] [-T output] "
+ "srcdir dstdir\n", BINM_CATMAN);
+ exit(1);
+}