[BACK]Return to catman.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/catman.c, Revision 1.18

1.18    ! schwarze    1: /*     $Id: catman.c,v 1.17 2017/02/09 18:46:44 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 *)
                     51:                __attribute__((noreturn));
1.13      schwarze   52: ssize_t         sock_fd_write(int, int, int, int);
                     53: void    usage(void) __attribute__((noreturn));
                     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.14      schwarze  261:        fprintf(stderr, "usage: catman [-I os=name] [-T output] "
                    262:            "srcdir dstdir\n");
1.13      schwarze  263:        exit(1);
1.1       kristaps  264: }

CVSweb