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