]> git.cameronkatri.com Git - mandoc.git/blob - catman.c
Remove the ENDBODY_NOSPACE flag, simplifying the code.
[mandoc.git] / catman.c
1 /* $Id: catman.c,v 1.18 2017/02/09 20:53:33 schwarze Exp $ */
2 /*
3 * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4 * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
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 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
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
20 #if HAVE_CMSG_XPG42
21 #define _XPG4_2
22 #endif
23
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27
28 #if HAVE_ERR
29 #include <err.h>
30 #endif
31 #include <errno.h>
32 #include <fcntl.h>
33 #if HAVE_FTS
34 #include <fts.h>
35 #else
36 #include "compat_fts.h"
37 #endif
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43
44 #ifndef O_DIRECTORY
45 #define O_DIRECTORY 0
46 #endif
47
48 int process_manpage(int, int, const char *);
49 int process_tree(int, int);
50 void run_mandocd(int, const char *, const char *)
51 __attribute__((noreturn));
52 ssize_t sock_fd_write(int, int, int, int);
53 void usage(void) __attribute__((noreturn));
54
55
56 void
57 run_mandocd(int sockfd, const char *outtype, const char* defos)
58 {
59 char sockfdstr[10];
60
61 if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
62 err(1, "snprintf");
63 if (defos == NULL)
64 execlp("mandocd", "mandocd", "-T", outtype,
65 sockfdstr, (char *)NULL);
66 else
67 execlp("mandocd", "mandocd", "-T", outtype,
68 "-I", defos, sockfdstr, (char *)NULL);
69 err(1, "exec");
70 }
71
72 ssize_t
73 sock_fd_write(int fd, int fd0, int fd1, int fd2)
74 {
75 const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
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;
84 ssize_t sz;
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;
107
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;
123 }
124
125 int
126 process_manpage(int srv_fd, int dstdir_fd, const char *path)
127 {
128 int in_fd, out_fd;
129 int irc;
130
131 if ((in_fd = open(path, O_RDONLY)) == -1) {
132 warn("open(%s)", path);
133 return 0;
134 }
135
136 if ((out_fd = openat(dstdir_fd, path,
137 O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
138 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
139 warn("openat(%s)", path);
140 close(in_fd);
141 return 0;
142 }
143
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) {
150 warn("sendmsg");
151 return -1;
152 }
153 return 0;
154 }
155
156 int
157 process_tree(int srv_fd, int dstdir_fd)
158 {
159 FTS *ftsp;
160 FTSENT *entry;
161 const char *argv[2];
162 const char *path;
163
164 argv[0] = ".";
165 argv[1] = (char *)NULL;
166
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:
177 if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
178 fts_close(ftsp);
179 return -1;
180 }
181 break;
182 case FTS_D:
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;
191 case FTS_DP:
192 break;
193 default:
194 warnx("%s: not a regular file", path);
195 break;
196 }
197 }
198
199 fts_close(ftsp);
200 return 0;
201 }
202
203 int
204 main(int argc, char **argv)
205 {
206 const char *defos, *outtype;
207 int srv_fds[2];
208 int dstdir_fd;
209 int opt;
210 pid_t pid;
211
212 defos = NULL;
213 outtype = "ascii";
214 while ((opt = getopt(argc, argv, "I:T:")) != -1) {
215 switch (opt) {
216 case 'I':
217 defos = optarg;
218 break;
219 case 'T':
220 outtype = optarg;
221 break;
222 default:
223 usage();
224 }
225 }
226
227 if (argc > 0) {
228 argc -= optind;
229 argv += optind;
230 }
231 if (argc != 2)
232 usage();
233
234 if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
235 err(1, "socketpair");
236
237 pid = fork();
238 switch (pid) {
239 case -1:
240 err(1, "fork");
241 case 0:
242 close(srv_fds[0]);
243 run_mandocd(srv_fds[1], outtype, defos);
244 default:
245 break;
246 }
247 close(srv_fds[1]);
248
249 if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
250 err(1, "open(%s)", argv[1]);
251
252 if (chdir(argv[0]) == -1)
253 err(1, "chdir(%s)", argv[0]);
254
255 return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
256 }
257
258 void
259 usage(void)
260 {
261 fprintf(stderr, "usage: catman [-I os=name] [-T output] "
262 "srcdir dstdir\n");
263 exit(1);
264 }