Annotation of mandoc/catman.c, Revision 1.15
1.15 ! schwarze 1: /* $Id: catman.c,v 1.14 2017/02/06 19:02:37 schwarze Exp $ */
1.1 kristaps 2: /*
1.13 schwarze 3: * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4: * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
1.1 kristaps 5: *
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.
9: *
1.13 schwarze 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.1 kristaps 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.13 schwarze 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.1 kristaps 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.
17: */
18: #include "config.h"
19:
1.13 schwarze 20: #include <sys/types.h>
21: #include <sys/socket.h>
1.1 kristaps 22: #include <sys/stat.h>
23:
1.13 schwarze 24: #if HAVE_ERR
25: #include <err.h>
26: #endif
1.14 schwarze 27: #include <errno.h>
1.1 kristaps 28: #include <fcntl.h>
1.13 schwarze 29: #if HAVE_FTS
30: #include <fts.h>
31: #else
32: #include "compat_fts.h"
33: #endif
1.1 kristaps 34: #include <stdio.h>
35: #include <stdlib.h>
36: #include <string.h>
1.15 ! schwarze 37: #include <time.h>
1.1 kristaps 38: #include <unistd.h>
39:
1.13 schwarze 40: int process_manpage(int, int, const char *);
41: int process_tree(int, int);
1.14 schwarze 42: void run_mandocd(int, const char *, const char *)
43: __attribute__((noreturn));
1.13 schwarze 44: ssize_t sock_fd_write(int, int, int, int);
45: void usage(void) __attribute__((noreturn));
46:
47:
48: void
1.14 schwarze 49: run_mandocd(int sockfd, const char *outtype, const char* defos)
1.13 schwarze 50: {
51: char sockfdstr[10];
52:
53: if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
54: err(1, "snprintf");
1.14 schwarze 55: if (defos == NULL)
56: execlp("mandocd", "mandocd", "-T", outtype, sockfdstr, NULL);
57: else
58: execlp("mandocd", "mandocd", "-T", outtype,
59: "-I", defos, sockfdstr, NULL);
1.13 schwarze 60: err(1, "exec");
61: }
62:
63: ssize_t
64: sock_fd_write(int fd, int fd0, int fd1, int fd2)
65: {
1.15 ! schwarze 66: const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
1.13 schwarze 67: struct msghdr msg;
68: struct iovec iov;
69: union {
70: struct cmsghdr cmsghdr;
71: char control[CMSG_SPACE(3 * sizeof(int))];
72: } cmsgu;
73: struct cmsghdr *cmsg;
74: int *walk;
1.15 ! schwarze 75: ssize_t sz;
1.13 schwarze 76: unsigned char dummy[1] = {'\0'};
77:
78: iov.iov_base = dummy;
79: iov.iov_len = sizeof(dummy);
80:
81: msg.msg_name = NULL;
82: msg.msg_namelen = 0;
83: msg.msg_iov = &iov;
84: msg.msg_iovlen = 1;
85:
86: msg.msg_control = cmsgu.control;
87: msg.msg_controllen = sizeof(cmsgu.control);
88:
89: cmsg = CMSG_FIRSTHDR(&msg);
90: cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
91: cmsg->cmsg_level = SOL_SOCKET;
92: cmsg->cmsg_type = SCM_RIGHTS;
93:
94: walk = (int *)CMSG_DATA(cmsg);
95: *(walk++) = fd0;
96: *(walk++) = fd1;
97: *(walk++) = fd2;
1.1 kristaps 98:
1.15 ! schwarze 99: /*
! 100: * It appears that on some systems, sendmsg(3)
! 101: * may return EAGAIN even in blocking mode.
! 102: * Seen for example on Oracle Solaris 11.2.
! 103: * The sleeping time was chosen by experimentation,
! 104: * to neither cause more than a handful of retries
! 105: * in normal operation nor unnecessary delays.
! 106: */
! 107: for (;;) {
! 108: if ((sz = sendmsg(fd, &msg, 0)) != -1 ||
! 109: errno != EAGAIN)
! 110: break;
! 111: nanosleep(&timeout, NULL);
! 112: }
! 113: return sz;
1.13 schwarze 114: }
1.1 kristaps 115:
116: int
1.13 schwarze 117: process_manpage(int srv_fd, int dstdir_fd, const char *path)
1.1 kristaps 118: {
1.13 schwarze 119: int in_fd, out_fd;
1.14 schwarze 120: int irc;
1.1 kristaps 121:
1.13 schwarze 122: if ((in_fd = open(path, O_RDONLY)) == -1) {
123: warn("open(%s)", path);
1.14 schwarze 124: return 0;
1.13 schwarze 125: }
1.1 kristaps 126:
1.13 schwarze 127: if ((out_fd = openat(dstdir_fd, path,
128: O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
1.14 schwarze 129: S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
1.13 schwarze 130: warn("openat(%s)", path);
131: close(in_fd);
1.14 schwarze 132: return 0;
1.1 kristaps 133: }
134:
1.14 schwarze 135: irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO);
136:
137: close(in_fd);
138: close(out_fd);
139:
140: if (irc < 0) {
1.13 schwarze 141: warn("sendmsg");
142: return -1;
143: }
144: return 0;
1.1 kristaps 145: }
146:
1.13 schwarze 147: int
148: process_tree(int srv_fd, int dstdir_fd)
1.1 kristaps 149: {
1.13 schwarze 150: FTS *ftsp;
151: FTSENT *entry;
152: const char *argv[2];
153: const char *path;
1.1 kristaps 154:
1.13 schwarze 155: argv[0] = ".";
156: argv[1] = (char *)NULL;
1.1 kristaps 157:
1.13 schwarze 158: if ((ftsp = fts_open((char * const *)argv,
159: FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
160: warn("fts_open");
161: return -1;
162: }
163:
164: while ((entry = fts_read(ftsp)) != NULL) {
165: path = entry->fts_path + 2;
166: switch (entry->fts_info) {
167: case FTS_F:
1.14 schwarze 168: if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
169: fts_close(ftsp);
170: return -1;
171: }
1.13 schwarze 172: break;
173: case FTS_D:
1.14 schwarze 174: if (*path != '\0' &&
175: mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
176: S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
177: errno != EEXIST) {
178: warn("mkdirat(%s)", path);
179: (void)fts_set(ftsp, entry, FTS_SKIP);
180: }
181: break;
1.13 schwarze 182: case FTS_DP:
183: break;
184: default:
185: warnx("%s: not a regular file", path);
186: break;
1.1 kristaps 187: }
1.13 schwarze 188: }
1.1 kristaps 189:
1.13 schwarze 190: fts_close(ftsp);
191: return 0;
1.1 kristaps 192: }
193:
1.13 schwarze 194: int
195: main(int argc, char **argv)
1.1 kristaps 196: {
1.14 schwarze 197: const char *defos, *outtype;
1.13 schwarze 198: int srv_fds[2];
199: int dstdir_fd;
200: int opt;
1.1 kristaps 201: pid_t pid;
202:
1.14 schwarze 203: defos = NULL;
1.13 schwarze 204: outtype = "ascii";
1.14 schwarze 205: while ((opt = getopt(argc, argv, "I:T:")) != -1) {
1.13 schwarze 206: switch (opt) {
1.14 schwarze 207: case 'I':
208: defos = optarg;
209: break;
1.13 schwarze 210: case 'T':
211: outtype = optarg;
1.1 kristaps 212: break;
1.13 schwarze 213: default:
214: usage();
1.1 kristaps 215: }
1.13 schwarze 216: }
1.1 kristaps 217:
1.13 schwarze 218: if (argc > 0) {
219: argc -= optind;
220: argv += optind;
1.1 kristaps 221: }
1.13 schwarze 222: if (argc != 2)
223: usage();
1.1 kristaps 224:
1.13 schwarze 225: if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
226: err(1, "socketpair");
1.1 kristaps 227:
1.13 schwarze 228: pid = fork();
229: switch (pid) {
230: case -1:
231: err(1, "fork");
232: case 0:
233: close(srv_fds[0]);
1.14 schwarze 234: run_mandocd(srv_fds[1], outtype, defos);
1.13 schwarze 235: default:
236: break;
1.1 kristaps 237: }
1.13 schwarze 238: close(srv_fds[1]);
1.1 kristaps 239:
1.13 schwarze 240: if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
241: err(1, "open(%s)", argv[1]);
1.1 kristaps 242:
1.13 schwarze 243: if (chdir(argv[0]) == -1)
244: err(1, "chdir(%s)", argv[0]);
1.1 kristaps 245:
1.13 schwarze 246: return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
1.1 kristaps 247: }
248:
1.13 schwarze 249: void
250: usage(void)
1.1 kristaps 251: {
1.14 schwarze 252: fprintf(stderr, "usage: catman [-I os=name] [-T output] "
253: "srcdir dstdir\n");
1.13 schwarze 254: exit(1);
1.1 kristaps 255: }
CVSweb