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

Annotation of mandoc/mandocd.c, Revision 1.15

1.15    ! schwarze    1: /* $Id: mandocd.c,v 1.14 2025/06/30 11:24:30 schwarze Exp $ */
1.1       schwarze    2: /*
1.14      schwarze    3:  * Copyright (c) 2017-2019, 2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
1.1       schwarze    4:  * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.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"
1.4       schwarze   19:
1.12      schwarze   20: #if NEED_XPG4_2
1.4       schwarze   21: #define _XPG4_2
                     22: #endif
1.1       schwarze   23:
                     24: #include <sys/types.h>
                     25: #include <sys/socket.h>
                     26:
                     27: #if HAVE_ERR
                     28: #include <err.h>
                     29: #endif
1.14      schwarze   30: #include <errno.h>
1.1       schwarze   31: #include <limits.h>
1.15    ! schwarze   32: #include <signal.h>
1.2       schwarze   33: #include <stdint.h>
1.1       schwarze   34: #include <stdio.h>
                     35: #include <stdlib.h>
                     36: #include <string.h>
1.14      schwarze   37: #include <time.h>
1.1       schwarze   38: #include <unistd.h>
                     39:
                     40: #include "mandoc.h"
1.13      schwarze   41: #if DEBUG_MEMORY
                     42: #define DEBUG_NODEF 1
                     43: #include "mandoc_dbg.h"
                     44: #endif
1.1       schwarze   45: #include "roff.h"
                     46: #include "mdoc.h"
                     47: #include "man.h"
1.7       schwarze   48: #include "mandoc_parse.h"
1.1       schwarze   49: #include "main.h"
                     50: #include "manconf.h"
                     51:
                     52: enum   outt {
                     53:        OUTT_ASCII = 0,
                     54:        OUTT_UTF8,
                     55:        OUTT_HTML
                     56: };
                     57:
                     58: static void      process(struct mparse *, enum outt, void *);
                     59: static int       read_fds(int, int *);
1.5       schwarze   60: static void      usage(void) __attribute__((__noreturn__));
1.1       schwarze   61:
                     62:
                     63: #define NUM_FDS 3
                     64: static int
                     65: read_fds(int clientfd, int *fds)
                     66: {
1.14      schwarze   67:        const struct timespec timeout = { 0, 10000000 };  /* 0.01 s */
1.1       schwarze   68:        struct msghdr    msg;
                     69:        struct iovec     iov[1];
                     70:        unsigned char    dummy[1];
                     71:        struct cmsghdr  *cmsg;
                     72:        int             *walk;
                     73:        int              cnt;
                     74:
                     75:        /* Union used for alignment. */
                     76:        union {
                     77:                uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
                     78:                struct cmsghdr align;
                     79:        } u;
                     80:
                     81:        memset(&msg, '\0', sizeof(msg));
                     82:        msg.msg_control = u.controlbuf;
                     83:        msg.msg_controllen = sizeof(u.controlbuf);
                     84:
                     85:        /*
                     86:         * Read a dummy byte - sendmsg cannot send an empty message,
                     87:         * even if we are only interested in the OOB data.
                     88:         */
                     89:
                     90:        iov[0].iov_base = dummy;
                     91:        iov[0].iov_len = sizeof(dummy);
                     92:        msg.msg_iov = iov;
                     93:        msg.msg_iovlen = 1;
                     94:
                     95:        switch (recvmsg(clientfd, &msg, 0)) {
                     96:        case -1:
                     97:                warn("recvmsg");
                     98:                return -1;
                     99:        case 0:
                    100:                return 0;
                    101:        default:
                    102:                break;
1.14      schwarze  103:        }
                    104:
                    105:        *dummy = '\0';
                    106:        while (send(clientfd, dummy, sizeof(dummy), 0) == -1) {
                    107:                if (errno != EAGAIN) {
                    108:                        warn("send");
                    109:                        return -1;
                    110:                }
                    111:                nanosleep(&timeout, NULL);
1.1       schwarze  112:        }
                    113:
                    114:        if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
                    115:                warnx("CMSG_FIRSTHDR: missing control message");
                    116:                return -1;
                    117:        }
                    118:
                    119:        if (cmsg->cmsg_level != SOL_SOCKET ||
                    120:            cmsg->cmsg_type != SCM_RIGHTS ||
                    121:            cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
                    122:                warnx("CMSG_FIRSTHDR: invalid control message");
                    123:                return -1;
                    124:        }
                    125:
                    126:        walk = (int *)CMSG_DATA(cmsg);
                    127:        for (cnt = 0; cnt < NUM_FDS; cnt++)
                    128:                fds[cnt] = *walk++;
                    129:
                    130:        return 1;
                    131: }
                    132:
                    133: int
                    134: main(int argc, char *argv[])
                    135: {
1.15    ! schwarze  136:        struct sigaction         sa;
1.1       schwarze  137:        struct manoutput         options;
                    138:        struct mparse           *parser;
                    139:        void                    *formatter;
1.3       schwarze  140:        const char              *defos;
1.1       schwarze  141:        const char              *errstr;
                    142:        int                      clientfd;
                    143:        int                      old_stdin;
                    144:        int                      old_stdout;
                    145:        int                      old_stderr;
                    146:        int                      fds[3];
                    147:        int                      state, opt;
                    148:        enum outt                outtype;
                    149:
1.13      schwarze  150: #if DEBUG_MEMORY
                    151:        mandoc_dbg_init(argc, argv);
                    152: #endif
                    153:
1.3       schwarze  154:        defos = NULL;
1.1       schwarze  155:        outtype = OUTT_ASCII;
1.3       schwarze  156:        while ((opt = getopt(argc, argv, "I:T:")) != -1) {
1.1       schwarze  157:                switch (opt) {
1.3       schwarze  158:                case 'I':
                    159:                        if (strncmp(optarg, "os=", 3) == 0)
                    160:                                defos = optarg + 3;
                    161:                        else {
                    162:                                warnx("-I %s: Bad argument", optarg);
                    163:                                usage();
                    164:                        }
                    165:                        break;
1.1       schwarze  166:                case 'T':
                    167:                        if (strcmp(optarg, "ascii") == 0)
                    168:                                outtype = OUTT_ASCII;
                    169:                        else if (strcmp(optarg, "utf8") == 0)
                    170:                                outtype = OUTT_UTF8;
                    171:                        else if (strcmp(optarg, "html") == 0)
                    172:                                outtype = OUTT_HTML;
                    173:                        else {
                    174:                                warnx("-T %s: Bad argument", optarg);
                    175:                                usage();
                    176:                        }
                    177:                        break;
                    178:                default:
                    179:                        usage();
                    180:                }
                    181:        }
                    182:
                    183:        if (argc > 0) {
                    184:                argc -= optind;
                    185:                argv += optind;
                    186:        }
1.15    ! schwarze  187:        if (argc != 1) {
        !           188:                if (argc == 0)
        !           189:                        warnx("missing argument: socket_fd");
        !           190:                else
        !           191:                        warnx("too many arguments: %s", argv[1]);
1.1       schwarze  192:                usage();
1.15    ! schwarze  193:        }
1.1       schwarze  194:
                    195:        errstr = NULL;
                    196:        clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
                    197:        if (errstr)
1.15    ! schwarze  198:                errx(1, "file descriptor %s is %s", argv[0], errstr);
        !           199:
        !           200:        memset(&sa, 0, sizeof(sa));
        !           201:        sa.sa_handler = SIG_IGN;
        !           202:        if (sigfillset(&sa.sa_mask) == -1)
        !           203:                err(1, "sigfillset");
        !           204:        if (sigaction(SIGPIPE, &sa, NULL) == -1)
        !           205:                err(1, "sigaction(SIGPIPE)");
1.1       schwarze  206:
                    207:        mchars_alloc();
1.10      schwarze  208:        parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
                    209:            MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
1.1       schwarze  210:
                    211:        memset(&options, 0, sizeof(options));
                    212:        switch (outtype) {
                    213:        case OUTT_ASCII:
                    214:                formatter = ascii_alloc(&options);
                    215:                break;
                    216:        case OUTT_UTF8:
                    217:                formatter = utf8_alloc(&options);
                    218:                break;
                    219:        case OUTT_HTML:
                    220:                options.fragment = 1;
                    221:                formatter = html_alloc(&options);
                    222:                break;
                    223:        }
                    224:
                    225:        state = 1;  /* work to do */
                    226:        fflush(stdout);
                    227:        fflush(stderr);
                    228:        if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
                    229:            (old_stdout = dup(STDOUT_FILENO)) == -1 ||
                    230:            (old_stderr = dup(STDERR_FILENO)) == -1) {
                    231:                warn("dup");
                    232:                state = -1;  /* error */
                    233:        }
                    234:
                    235:        while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
                    236:                if (dup2(fds[0], STDIN_FILENO) == -1 ||
                    237:                    dup2(fds[1], STDOUT_FILENO) == -1 ||
                    238:                    dup2(fds[2], STDERR_FILENO) == -1) {
                    239:                        warn("dup2");
                    240:                        state = -1;
                    241:                        break;
                    242:                }
                    243:
                    244:                close(fds[0]);
                    245:                close(fds[1]);
                    246:                close(fds[2]);
                    247:
                    248:                process(parser, outtype, formatter);
                    249:                mparse_reset(parser);
1.11      schwarze  250:                if (outtype == OUTT_HTML)
                    251:                        html_reset(formatter);
1.1       schwarze  252:
                    253:                fflush(stdout);
                    254:                fflush(stderr);
                    255:                /* Close file descriptors by restoring the old ones. */
                    256:                if (dup2(old_stderr, STDERR_FILENO) == -1 ||
                    257:                    dup2(old_stdout, STDOUT_FILENO) == -1 ||
                    258:                    dup2(old_stdin, STDIN_FILENO) == -1) {
                    259:                        warn("dup2");
                    260:                        state = -1;
                    261:                        break;
                    262:                }
                    263:        }
                    264:
                    265:        close(clientfd);
                    266:        switch (outtype) {
                    267:        case OUTT_ASCII:
                    268:        case OUTT_UTF8:
                    269:                ascii_free(formatter);
                    270:                break;
                    271:        case OUTT_HTML:
                    272:                html_free(formatter);
                    273:                break;
                    274:        }
                    275:        mparse_free(parser);
                    276:        mchars_free();
1.13      schwarze  277: #if DEBUG_MEMORY
                    278:        mandoc_dbg_finish();
                    279: #endif
1.1       schwarze  280:        return state == -1 ? 1 : 0;
                    281: }
                    282:
                    283: static void
                    284: process(struct mparse *parser, enum outt outtype, void *formatter)
                    285: {
1.9       schwarze  286:        struct roff_meta *meta;
1.1       schwarze  287:
                    288:        mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
1.9       schwarze  289:        meta = mparse_result(parser);
                    290:        if (meta->macroset == MACROSET_MDOC) {
1.1       schwarze  291:                switch (outtype) {
                    292:                case OUTT_ASCII:
                    293:                case OUTT_UTF8:
1.9       schwarze  294:                        terminal_mdoc(formatter, meta);
1.1       schwarze  295:                        break;
                    296:                case OUTT_HTML:
1.9       schwarze  297:                        html_mdoc(formatter, meta);
1.1       schwarze  298:                        break;
                    299:                }
                    300:        }
1.9       schwarze  301:        if (meta->macroset == MACROSET_MAN) {
1.1       schwarze  302:                switch (outtype) {
                    303:                case OUTT_ASCII:
                    304:                case OUTT_UTF8:
1.9       schwarze  305:                        terminal_man(formatter, meta);
1.1       schwarze  306:                        break;
                    307:                case OUTT_HTML:
1.9       schwarze  308:                        html_man(formatter, meta);
1.1       schwarze  309:                        break;
                    310:                }
                    311:        }
                    312: }
                    313:
                    314: void
                    315: usage(void)
                    316: {
1.3       schwarze  317:        fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
1.1       schwarze  318:        exit(1);
                    319: }

CVSweb