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