aboutsummaryrefslogtreecommitdiff
path: root/contrib/mandoc/catman.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/mandoc/catman.c')
-rw-r--r--contrib/mandoc/catman.c231
1 files changed, 205 insertions, 26 deletions
diff --git a/contrib/mandoc/catman.c b/contrib/mandoc/catman.c
index e46613eb0e8c..c9eda18bf71c 100644
--- a/contrib/mandoc/catman.c
+++ b/contrib/mandoc/catman.c
@@ -1,7 +1,7 @@
-/* $Id: catman.c,v 1.23 2021/10/15 15:04:02 schwarze Exp $ */
+/* $Id: catman.c,v 1.30 2025/07/13 14:15:26 schwarze Exp $ */
/*
+ * Copyright (c) 2017, 2025 Ingo Schwarze <schwarze@openbsd.org>
* 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
@@ -25,6 +25,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
+#include <assert.h>
#if HAVE_ERR
#include <err.h>
#endif
@@ -35,26 +36,44 @@
#else
#include "compat_fts.h"
#endif
+#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
+int verbose_flag = 0;
+sig_atomic_t got_signal = 0;
+
int process_manpage(int, int, const char *);
int process_tree(int, int);
void run_mandocd(int, const char *, const char *)
__attribute__((__noreturn__));
+void signal_handler(int);
ssize_t sock_fd_write(int, int, int, int);
void usage(void) __attribute__((__noreturn__));
void
+signal_handler(int signum)
+{
+ got_signal = signum;
+}
+
+void
run_mandocd(int sockfd, const char *outtype, const char* defos)
{
char sockfdstr[10];
+ int len;
- if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
+ len = snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd);
+ if (len >= (int)sizeof(sockfdstr)) {
+ errno = EOVERFLOW;
+ len = -1;
+ }
+ if (len < 0)
err(1, "snprintf");
if (defos == NULL)
execlp("mandocd", "mandocd", "-T", outtype,
@@ -109,10 +128,11 @@ sock_fd_write(int fd, int fd0, int fd1, int fd2)
* 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)
+ while ((sz = sendmsg(fd, &msg, 0)) == -1) {
+ if (errno != EAGAIN) {
+ warn("FATAL: sendmsg");
break;
+ }
nanosleep(&timeout, NULL);
}
return sz;
@@ -125,14 +145,16 @@ process_manpage(int srv_fd, int dstdir_fd, const char *path)
int irc;
if ((in_fd = open(path, O_RDONLY)) == -1) {
- warn("open(%s)", path);
+ warn("open %s for reading", path);
+ fflush(stderr);
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);
+ warn("openat %s for writing", path);
+ fflush(stderr);
close(in_fd);
return 0;
}
@@ -142,20 +164,22 @@ process_manpage(int srv_fd, int dstdir_fd, const char *path)
close(in_fd);
close(out_fd);
- if (irc < 0) {
- warn("sendmsg");
- return -1;
- }
- return 0;
+ return irc;
}
int
process_tree(int srv_fd, int dstdir_fd)
{
+ const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
+ const int max_inflight = 16;
+
FTS *ftsp;
FTSENT *entry;
const char *argv[2];
const char *path;
+ int inflight, irc, decr, fatal;
+ int gooddirs, baddirs, goodfiles, badfiles;
+ char dummy[1];
argv[0] = ".";
argv[1] = (char *)NULL;
@@ -166,13 +190,59 @@ process_tree(int srv_fd, int dstdir_fd)
return -1;
}
- while ((entry = fts_read(ftsp)) != NULL) {
+ if (verbose_flag >= 2) {
+ warnx("allowing up to %d files in flight", max_inflight);
+ fflush(stderr);
+ }
+ inflight = fatal = gooddirs = baddirs = goodfiles = badfiles = 0;
+ while (fatal == 0 && got_signal == 0 &&
+ (entry = fts_read(ftsp)) != NULL) {
+ if (inflight >= max_inflight) {
+ while (recv(srv_fd, dummy, sizeof(dummy), 0) == -1) {
+ if (errno != EAGAIN) {
+ warn("FATAL: recv");
+ fatal = errno;
+ break;
+ }
+ nanosleep(&timeout, NULL);
+ }
+ if (fatal != 0)
+ break;
+ decr = 1;
+ while ((irc = recv(srv_fd, dummy, sizeof(dummy),
+ MSG_DONTWAIT)) > 0)
+ decr++;
+ assert(inflight >= decr);
+ if (verbose_flag >= 2 && decr > 1) {
+ warnx("files in flight: %d - %d = %d",
+ inflight, decr, inflight - decr);
+ fflush(stderr);
+ }
+ inflight -= decr;
+ if (irc == 0) {
+ errno = ECONNRESET;
+ inflight = -1;
+ }
+ if (errno != EAGAIN) {
+ warn("FATAL: recv");
+ fatal = errno;
+ break;
+ }
+ }
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;
+ switch (process_manpage(srv_fd, dstdir_fd, path)) {
+ case -1:
+ fatal = errno;
+ break;
+ case 0:
+ badfiles++;
+ break;
+ default:
+ goodfiles++;
+ inflight++;
+ break;
}
break;
case FTS_D:
@@ -180,25 +250,96 @@ process_tree(int srv_fd, int dstdir_fd)
mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
errno != EEXIST) {
- warn("mkdirat(%s)", path);
+ warn("mkdirat %s", path);
+ fflush(stderr);
(void)fts_set(ftsp, entry, FTS_SKIP);
- }
+ baddirs++;
+ } else
+ gooddirs++;
break;
case FTS_DP:
break;
+ case FTS_DNR:
+ warnx("directory %s unreadable: %s",
+ path, strerror(entry->fts_errno));
+ fflush(stderr);
+ baddirs++;
+ break;
+ case FTS_DC:
+ warnx("directory %s causes cycle", path);
+ fflush(stderr);
+ baddirs++;
+ break;
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("file %s: %s",
+ path, strerror(entry->fts_errno));
+ fflush(stderr);
+ badfiles++;
+ break;
default:
- warnx("%s: not a regular file", path);
+ warnx("file %s: not a regular file", path);
+ fflush(stderr);
+ badfiles++;
break;
}
}
+ if (got_signal != 0) {
+ switch (got_signal) {
+ case SIGCHLD:
+ warnx("FATAL: mandocd child died: got SIGCHLD");
+ break;
+ case SIGPIPE:
+ warnx("FATAL: mandocd child died: got SIGPIPE");
+ break;
+ default:
+ warnx("FATAL: signal SIG%s", sys_signame[got_signal]);
+ break;
+ }
+ inflight = -1;
+ fatal = 1;
+ } else if (fatal == 0 && (fatal = errno) != 0)
+ warn("FATAL: fts_read");
fts_close(ftsp);
- return 0;
+ if (verbose_flag >= 2 && inflight > 0) {
+ warnx("waiting for %d files in flight", inflight);
+ fflush(stderr);
+ }
+ while (inflight > 0) {
+ irc = recv(srv_fd, dummy, sizeof(dummy), 0);
+ if (irc > 0)
+ inflight--;
+ else if (irc == -1 && errno == EAGAIN)
+ nanosleep(&timeout, NULL);
+ else {
+ if (irc == 0)
+ errno = ECONNRESET;
+ warn("recv");
+ inflight = -1;
+ }
+ }
+ if (verbose_flag)
+ warnx("processed %d files in %d directories",
+ goodfiles, gooddirs);
+ if (baddirs > 0)
+ warnx("skipped %d %s due to errors", baddirs,
+ baddirs == 1 ? "directory" : "directories");
+ if (badfiles > 0)
+ warnx("skipped %d %s due to errors", badfiles,
+ badfiles == 1 ? "file" : "files");
+ if (fatal != 0) {
+ warnx("processing aborted due to fatal error, "
+ "results are probably incomplete");
+ inflight = -1;
+ }
+ return inflight;
}
int
main(int argc, char **argv)
{
+ struct sigaction sa;
const char *defos, *outtype;
int srv_fds[2];
int dstdir_fd;
@@ -207,7 +348,7 @@ main(int argc, char **argv)
defos = NULL;
outtype = "ascii";
- while ((opt = getopt(argc, argv, "I:T:")) != -1) {
+ while ((opt = getopt(argc, argv, "I:T:v")) != -1) {
switch (opt) {
case 'I':
defos = optarg;
@@ -215,6 +356,9 @@ main(int argc, char **argv)
case 'T':
outtype = optarg;
break;
+ case 'v':
+ verbose_flag += 1;
+ break;
default:
usage();
}
@@ -224,8 +368,36 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
}
- if (argc != 2)
+ if (argc != 2) {
+ switch (argc) {
+ case 0:
+ warnx("missing arguments: srcdir and dstdir");
+ break;
+ case 1:
+ warnx("missing argument: dstdir");
+ break;
+ default:
+ warnx("too many arguments: %s", argv[2]);
+ break;
+ }
usage();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signal_handler;
+ sa.sa_flags = SA_NOCLDWAIT;
+ if (sigfillset(&sa.sa_mask) == -1)
+ err(1, "sigfillset");
+ if (sigaction(SIGHUP, &sa, NULL) == -1)
+ err(1, "sigaction(SIGHUP)");
+ if (sigaction(SIGINT, &sa, NULL) == -1)
+ err(1, "sigaction(SIGINT)");
+ if (sigaction(SIGPIPE, &sa, NULL) == -1)
+ err(1, "sigaction(SIGPIPE)");
+ if (sigaction(SIGTERM, &sa, NULL) == -1)
+ err(1, "sigaction(SIGTERM)");
+ if (sigaction(SIGCHLD, &sa, NULL) == -1)
+ err(1, "sigaction(SIGCHLD)");
if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
err(1, "socketpair");
@@ -242,11 +414,18 @@ main(int argc, char **argv)
}
close(srv_fds[1]);
- if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
- err(1, "open(%s)", argv[1]);
+ if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1) {
+ if (errno != ENOENT)
+ err(1, "open destination %s", argv[1]);
+ if (mkdir(argv[1], S_IRWXU |
+ S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
+ err(1, "mkdir destination %s", argv[1]);
+ if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
+ err(1, "open destination %s", argv[1]);
+ }
if (chdir(argv[0]) == -1)
- err(1, "chdir(%s)", argv[0]);
+ err(1, "chdir to source %s", argv[0]);
return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
}