-/* $Id: catman.c,v 1.13 2017/02/04 12:03:07 schwarze Exp $ */
+/* $Id: catman.c,v 1.22 2020/06/14 23:40:31 schwarze Exp $ */
/*
* Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
* Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
*/
#include "config.h"
+#if NEED_XPG4_2
+#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>
#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 *) __attribute__((noreturn));
+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 usage(void) __attribute__((__noreturn__));
void
-run_mandocd(int sockfd, const char *outtype)
+run_mandocd(int sockfd, const char *outtype, const char* defos)
{
char sockfdstr[10];
if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
err(1, "snprintf");
- execlp("mandocd", "mandocd", "-T", outtype, sockfdstr, NULL);
+ 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 {
} cmsgu;
struct cmsghdr *cmsg;
int *walk;
+ ssize_t sz;
unsigned char dummy[1] = {'\0'};
iov.iov_base = dummy;
*(walk++) = fd1;
*(walk++) = fd2;
- return sendmsg(fd, &msg, 0);
+ /*
+ * 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 -1;
+ return 0;
}
if ((out_fd = openat(dstdir_fd, path,
O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO)) == -1) {
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
warn("openat(%s)", path);
close(in_fd);
- return -1;
+ return 0;
}
- if (sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO) < 0) {
- warn("sendmsg");
- return -1;
- }
+ 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;
}
path = entry->fts_path + 2;
switch (entry->fts_info) {
case FTS_F:
- process_manpage(srv_fd, dstdir_fd, path);
+ 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:
int
main(int argc, char **argv)
{
- const char *outtype;
+ 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, "T:")) != -1) {
+ while ((opt = getopt(argc, argv, "I:T:")) != -1) {
switch (opt) {
+ case 'I':
+ defos = optarg;
+ break;
case 'T':
outtype = optarg;
break;
err(1, "fork");
case 0:
close(srv_fds[0]);
- run_mandocd(srv_fds[1], outtype);
+ run_mandocd(srv_fds[1], outtype, defos);
default:
break;
}
void
usage(void)
{
- fprintf(stderr, "usage: catman [-T output] srcdir dstdir\n");
+ fprintf(stderr, "usage: %s [-I os=name] [-T output] "
+ "srcdir dstdir\n", BINM_CATMAN);
exit(1);
}