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

Annotation of mandoc/catman.c, Revision 1.1

1.1     ! kristaps    1: /*     $Id: manup.c,v 1.2 2011/11/24 12:22:53 kristaps Exp $ */
        !             2: /*
        !             3:  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
        !             4:  *
        !             5:  * Permission to use, copy, modify, and distribute this software for any
        !             6:  * purpose with or without fee is hereby granted, provided that the above
        !             7:  * copyright notice and this permission notice appear in all copies.
        !             8:  *
        !             9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            16:  */
        !            17: #ifdef HAVE_CONFIG_H
        !            18: #include "config.h"
        !            19: #endif
        !            20:
        !            21: #include <sys/param.h>
        !            22: #include <sys/stat.h>
        !            23: #include <sys/wait.h>
        !            24:
        !            25: #include <assert.h>
        !            26: #include <errno.h>
        !            27: #include <fcntl.h>
        !            28: #include <getopt.h>
        !            29: #include <stdio.h>
        !            30: #include <stdlib.h>
        !            31: #include <string.h>
        !            32: #include <unistd.h>
        !            33:
        !            34: #ifdef __linux__
        !            35: # include <db_185.h>
        !            36: #else
        !            37: # include <db.h>
        !            38: #endif
        !            39:
        !            40: #include "manpath.h"
        !            41:
        !            42: #define        xstrlcpy(_dst, _src, _sz) \
        !            43:        do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \
        !            44:                fprintf(stderr, "%s: Path too long", (_dst)); \
        !            45:                exit(EXIT_FAILURE); \
        !            46:        } while (/* CONSTCOND */0)
        !            47:
        !            48: #define        xstrlcat(_dst, _src, _sz) \
        !            49:        do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \
        !            50:                fprintf(stderr, "%s: Path too long", (_dst)); \
        !            51:                exit(EXIT_FAILURE); \
        !            52:        } while (/* CONSTCOND */0)
        !            53:
        !            54: static int              indexhtml(char *);
        !            55: static int              jobstart(const char *, const char *, pid_t *);
        !            56: static int              jobwait(pid_t);
        !            57: static int              manup(const struct manpaths *, const char *);
        !            58: static int              mkpath(char *, mode_t, mode_t);
        !            59: static int              treecpy(char *, char *);
        !            60: static int              update(char *, char *);
        !            61: static void             usage(void);
        !            62:
        !            63: static const char      *progname;
        !            64: static int              verbose;
        !            65: static int              force;
        !            66:
        !            67: int
        !            68: main(int argc, char *argv[])
        !            69: {
        !            70:        int              ch;
        !            71:        char            *aux, *base;
        !            72:        const char      *dir;
        !            73:        struct manpaths  dirs;
        !            74:        extern char     *optarg;
        !            75:        extern int       optind;
        !            76:
        !            77:        progname = strrchr(argv[0], '/');
        !            78:        if (progname == NULL)
        !            79:                progname = argv[0];
        !            80:        else
        !            81:                ++progname;
        !            82:
        !            83:        aux = base = NULL;
        !            84:        dir = "/var/www/cache/man.cgi";
        !            85:
        !            86:        while (-1 != (ch = getopt(argc, argv, "fm:M:o:v")))
        !            87:                switch (ch) {
        !            88:                case ('f'):
        !            89:                        force = 1;
        !            90:                        break;
        !            91:                case ('m'):
        !            92:                        aux = optarg;
        !            93:                        break;
        !            94:                case ('M'):
        !            95:                        base = optarg;
        !            96:                        break;
        !            97:                case ('o'):
        !            98:                        dir = optarg;
        !            99:                        break;
        !           100:                case ('v'):
        !           101:                        verbose++;
        !           102:                        break;
        !           103:                default:
        !           104:                        usage();
        !           105:                        return(EXIT_FAILURE);
        !           106:                }
        !           107:
        !           108:        argc -= optind;
        !           109:        argv += optind;
        !           110:
        !           111:        if (argc > 0) {
        !           112:                usage();
        !           113:                return(EXIT_FAILURE);
        !           114:        }
        !           115:
        !           116:        memset(&dirs, 0, sizeof(struct manpaths));
        !           117:        manpath_parse(&dirs, base, aux);
        !           118:        ch = manup(&dirs, dir);
        !           119:        manpath_free(&dirs);
        !           120:        return(ch ? EXIT_SUCCESS : EXIT_FAILURE);
        !           121: }
        !           122:
        !           123: static void
        !           124: usage(void)
        !           125: {
        !           126:
        !           127:        fprintf(stderr, "usage: %s "
        !           128:                        "[-fv] "
        !           129:                        "[-o path] "
        !           130:                        "[-m manpath] "
        !           131:                        "[-M manpath]\n",
        !           132:                        progname);
        !           133: }
        !           134:
        !           135: /*
        !           136:  * If "src" file doesn't exist (errors out), return -1.  Otherwise,
        !           137:  * return 1 if "src" is newer (which also happens "dst" doesn't exist)
        !           138:  * and 0 otherwise.
        !           139:  */
        !           140: static int
        !           141: isnewer(const char *dst, const char *src)
        !           142: {
        !           143:        struct stat      s1, s2;
        !           144:
        !           145:        if (-1 == stat(src, &s1))
        !           146:                return(-1);
        !           147:        if (force)
        !           148:                return(1);
        !           149:
        !           150:        return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);
        !           151: }
        !           152:
        !           153: /*
        !           154:  * Copy the contents of one file into another.
        !           155:  * Returns 0 on failure, 1 on success.
        !           156:  */
        !           157: static int
        !           158: filecpy(const char *dst, const char *src)
        !           159: {
        !           160:        char             buf[BUFSIZ];
        !           161:        int              sfd, dfd, rc;
        !           162:        ssize_t          rsz, wsz;
        !           163:
        !           164:        sfd = dfd = -1;
        !           165:        rc = 0;
        !           166:
        !           167:        if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {
        !           168:                perror(dst);
        !           169:                goto out;
        !           170:        } else if (-1 == (sfd = open(src, O_RDONLY, 0))) {
        !           171:                perror(src);
        !           172:                goto out;
        !           173:        }
        !           174:
        !           175:        while ((rsz = read(sfd, buf, BUFSIZ)) > 0)
        !           176:                if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {
        !           177:                        perror(dst);
        !           178:                        goto out;
        !           179:                } else if (wsz < rsz) {
        !           180:                        fprintf(stderr, "%s: Short write\n", dst);
        !           181:                        goto out;
        !           182:                }
        !           183:
        !           184:        if (rsz < 0)
        !           185:                perror(src);
        !           186:        else
        !           187:                rc = 1;
        !           188: out:
        !           189:        if (-1 != sfd)
        !           190:                close(sfd);
        !           191:        if (-1 != dfd)
        !           192:                close(dfd);
        !           193:
        !           194:        return(rc);
        !           195: }
        !           196:
        !           197: /*
        !           198:  * Clean up existing child.
        !           199:  * Return 1 if cleaned up fine (or none was started) and 0 otherwise.
        !           200:  */
        !           201: static int
        !           202: jobwait(pid_t pid)
        !           203: {
        !           204:        int              st;
        !           205:
        !           206:        if (-1 == pid)
        !           207:                return(1);
        !           208:
        !           209:        if (-1 == waitpid(pid, &st, 0)) {
        !           210:                perror(NULL);
        !           211:                exit(EXIT_FAILURE);
        !           212:        }
        !           213:
        !           214:        return(WIFEXITED(st) && 0 == WEXITSTATUS(st));
        !           215: }
        !           216:
        !           217: /*
        !           218:  * Start a job (child process), first making sure that the prior one has
        !           219:  * finished.
        !           220:  * Return 1 if the prior child exited and the new one started, else 0.
        !           221:  */
        !           222: static int
        !           223: jobstart(const char *dst, const char *src, pid_t *pid)
        !           224: {
        !           225:        int              fd;
        !           226:
        !           227:        if ( ! jobwait(*pid))
        !           228:                return(0);
        !           229:
        !           230:        if (-1 == (*pid = fork())) {
        !           231:                perror(NULL);
        !           232:                exit(EXIT_FAILURE);
        !           233:        } else if (*pid > 0)
        !           234:                return(1);
        !           235:
        !           236:        if (-1 == (fd = open(dst, O_WRONLY|O_TRUNC|O_CREAT, 0644))) {
        !           237:                perror(dst);
        !           238:                exit(EXIT_FAILURE);
        !           239:        }
        !           240:
        !           241:        if (-1 == dup2(fd, STDOUT_FILENO)) {
        !           242:                perror(NULL);
        !           243:                exit(EXIT_FAILURE);
        !           244:        }
        !           245:
        !           246:        execlp("mandoc", "mandoc", "-T", "html",
        !           247:                        "-O", "fragment",
        !           248:                        "-O", "man=man.cgi?expr=%N&sec=%S",
        !           249:                        src, (char *)NULL);
        !           250:
        !           251:        perror("mandoc");
        !           252:        exit(EXIT_FAILURE);
        !           253:        /* NOTREACHED */
        !           254: }
        !           255:
        !           256: /*
        !           257:  * Pass over the recno database and re-create HTML pages if they're
        !           258:  * found to be out of date.
        !           259:  * Returns -1 on fatal error, 1 on success.
        !           260:  */
        !           261: static int
        !           262: indexhtml(char *dst)
        !           263: {
        !           264:        DB              *db;
        !           265:        DBT              key, val;
        !           266:        size_t           sz;
        !           267:        int              c, rc;
        !           268:        unsigned int     fl;
        !           269:        const char      *f;
        !           270:        char            *d;
        !           271:        char             fname[MAXPATHLEN];
        !           272:        pid_t            pid;
        !           273:
        !           274:        sz = strlen(dst);
        !           275:        pid = -1;
        !           276:
        !           277:        xstrlcpy(fname, dst, MAXPATHLEN);
        !           278:        xstrlcat(fname, "/mandoc.index", MAXPATHLEN);
        !           279:
        !           280:        db = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);
        !           281:        if (NULL == db) {
        !           282:                perror(fname);
        !           283:                return(-1);
        !           284:        }
        !           285:
        !           286:        fl = R_FIRST;
        !           287:        while (0 == (c = (*db->seq)(db, &key, &val, fl))) {
        !           288:                fl = R_NEXT;
        !           289:                f = (const char *)val.data;
        !           290:
        !           291:                dst[(int)sz] = '\0';
        !           292:
        !           293:                xstrlcat(dst, "/", MAXPATHLEN);
        !           294:                xstrlcat(dst, f, MAXPATHLEN);
        !           295:                xstrlcat(dst, ".html", MAXPATHLEN);
        !           296:
        !           297:                if (-1 == (rc = isnewer(dst, f))) {
        !           298:                        fprintf(stderr, "%s: Manpage missing\n", f);
        !           299:                        break;
        !           300:                } else if (0 == rc)
        !           301:                        continue;
        !           302:
        !           303:                d = strrchr(dst, '/');
        !           304:                assert(NULL != d);
        !           305:                *d = '\0';
        !           306:
        !           307:                if (-1 == mkpath(dst, 0755, 0755)) {
        !           308:                        perror(dst);
        !           309:                        break;
        !           310:                }
        !           311:
        !           312:                *d = '/';
        !           313:                if ( ! jobstart(dst, f, &pid))
        !           314:                        break;
        !           315:                if (verbose)
        !           316:                        printf("%s\n", dst);
        !           317:        }
        !           318:
        !           319:        (*db->close)(db);
        !           320:
        !           321:        if (c < 0)
        !           322:                perror(fname);
        !           323:        if ( ! jobwait(pid))
        !           324:                c = -1;
        !           325:
        !           326:        return(1 == c ? 1 : -1);
        !           327: }
        !           328:
        !           329: /*
        !           330:  * Copy both recno and btree databases into the destination.
        !           331:  * Call in to begin recreating HTML files.
        !           332:  * Return -1 on fatal error and 1 if the update went well.
        !           333:  */
        !           334: static int
        !           335: update(char *dst, char *src)
        !           336: {
        !           337:        size_t           dsz, ssz;
        !           338:
        !           339:        dsz = strlen(dst);
        !           340:        ssz = strlen(src);
        !           341:
        !           342:        xstrlcat(src, "/mandoc.db", MAXPATHLEN);
        !           343:        xstrlcat(dst, "/mandoc.db", MAXPATHLEN);
        !           344:
        !           345:        if ( ! filecpy(dst, src))
        !           346:                return(-1);
        !           347:        if (verbose)
        !           348:                printf("%s\n", dst);
        !           349:
        !           350:        dst[(int)dsz] = src[(int)ssz] = '\0';
        !           351:
        !           352:        xstrlcat(src, "/mandoc.index", MAXPATHLEN);
        !           353:        xstrlcat(dst, "/mandoc.index", MAXPATHLEN);
        !           354:
        !           355:        if ( ! filecpy(dst, src))
        !           356:                return(-1);
        !           357:        if (verbose)
        !           358:                printf("%s\n", dst);
        !           359:
        !           360:        dst[(int)dsz] = '\0';
        !           361:
        !           362:        return(indexhtml(dst));
        !           363: }
        !           364:
        !           365: /*
        !           366:  * See if btree or recno databases in the destination are out of date
        !           367:  * with respect to a single manpath component.
        !           368:  * Return -1 on fatal error, 0 if the source is no longer valid (and
        !           369:  * shouldn't be listed), and 1 if the update went well.
        !           370:  */
        !           371: static int
        !           372: treecpy(char *dst, char *src)
        !           373: {
        !           374:        size_t           dsz, ssz;
        !           375:        int              rc;
        !           376:
        !           377:        dsz = strlen(dst);
        !           378:        ssz = strlen(src);
        !           379:
        !           380:        xstrlcat(src, "/mandoc.index", MAXPATHLEN);
        !           381:        xstrlcat(dst, "/mandoc.index", MAXPATHLEN);
        !           382:
        !           383:        if (-1 == (rc = isnewer(dst, src)))
        !           384:                return(0);
        !           385:
        !           386:        dst[(int)dsz] = src[(int)ssz] = '\0';
        !           387:
        !           388:        if (1 == rc)
        !           389:                return(update(dst, src));
        !           390:
        !           391:        xstrlcat(src, "/mandoc.db", MAXPATHLEN);
        !           392:        xstrlcat(dst, "/mandoc.db", MAXPATHLEN);
        !           393:
        !           394:        if (-1 == (rc = isnewer(dst, src)))
        !           395:                return(0);
        !           396:        else if (rc == 0)
        !           397:                return(1);
        !           398:
        !           399:        dst[(int)dsz] = src[(int)ssz] = '\0';
        !           400:
        !           401:        return(update(dst, src));
        !           402: }
        !           403:
        !           404: /*
        !           405:  * Update the destination's file-tree with respect to changes in the
        !           406:  * source manpath components.
        !           407:  * "Change" is defined by an updated index or btree database.
        !           408:  * Returns 1 on success, 0 on failure.
        !           409:  */
        !           410: static int
        !           411: manup(const struct manpaths *dirs, const char *dir)
        !           412: {
        !           413:        char             dst[MAXPATHLEN],
        !           414:                         src[MAXPATHLEN];
        !           415:        const char      *path;
        !           416:        int              i, c;
        !           417:        size_t           sz;
        !           418:        FILE            *f;
        !           419:
        !           420:        xstrlcpy(dst, dir, MAXPATHLEN);
        !           421:        xstrlcat(dst, "/etc", MAXPATHLEN);
        !           422:
        !           423:        if (-1 == mkpath(dst, 0755, 0755)) {
        !           424:                perror(dst);
        !           425:                return(0);
        !           426:        }
        !           427:
        !           428:        xstrlcat(dst, "/man.conf", MAXPATHLEN);
        !           429:
        !           430:        if (verbose)
        !           431:                printf("%s\n", dst);
        !           432:
        !           433:        if (NULL == (f = fopen(dst, "w"))) {
        !           434:                perror(dst);
        !           435:                return(0);
        !           436:        }
        !           437:
        !           438:        xstrlcpy(dst, dir, MAXPATHLEN);
        !           439:        sz = strlen(dst);
        !           440:
        !           441:        for (i = 0; i < dirs->sz; i++) {
        !           442:                path = dirs->paths[i];
        !           443:
        !           444:                dst[(int)sz] = '\0';
        !           445:                xstrlcat(dst, path, MAXPATHLEN);
        !           446:
        !           447:                if (-1 == mkpath(dst, 0755, 0755)) {
        !           448:                        perror(dst);
        !           449:                        break;
        !           450:                }
        !           451:
        !           452:                xstrlcpy(src, path, MAXPATHLEN);
        !           453:
        !           454:                if (-1 == (c = treecpy(dst, src)))
        !           455:                        break;
        !           456:                else if (0 == c)
        !           457:                        continue;
        !           458:
        !           459:                /*
        !           460:                 * We want to use a relative path here because manpath.h
        !           461:                 * will realpath() when invoked with man.cgi, and we'll
        !           462:                 * make sure to chdir() into the cache directory before.
        !           463:                 *
        !           464:                 * This allows the cache directory to be in an arbitrary
        !           465:                 * place, working in both chroot() and non-chroot()
        !           466:                 * "safe" modes.
        !           467:                 */
        !           468:                assert('/' == path[0]);
        !           469:                fprintf(f, "_whatdb %s/whatis.db\n", path + 1);
        !           470:        }
        !           471:
        !           472:        fclose(f);
        !           473:        return(i == dirs->sz);
        !           474: }
        !           475:
        !           476: /*
        !           477:  * Copyright (c) 1983, 1992, 1993
        !           478:  *     The Regents of the University of California.  All rights reserved.
        !           479:  *
        !           480:  * Redistribution and use in source and binary forms, with or without
        !           481:  * modification, are permitted provided that the following conditions
        !           482:  * are met:
        !           483:  * 1. Redistributions of source code must retain the above copyright
        !           484:  *    notice, this list of conditions and the following disclaimer.
        !           485:  * 2. Redistributions in binary form must reproduce the above copyright
        !           486:  *    notice, this list of conditions and the following disclaimer in the
        !           487:  *    documentation and/or other materials provided with the distribution.
        !           488:  * 3. Neither the name of the University nor the names of its contributors
        !           489:  *    may be used to endorse or promote products derived from this software
        !           490:  *    without specific prior written permission.
        !           491:  *
        !           492:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !           493:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !           494:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !           495:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !           496:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !           497:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !           498:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !           499:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !           500:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !           501:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !           502:  * SUCH DAMAGE.
        !           503:  */
        !           504: static int
        !           505: mkpath(char *path, mode_t mode, mode_t dir_mode)
        !           506: {
        !           507:        struct stat sb;
        !           508:        char *slash;
        !           509:        int done, exists;
        !           510:
        !           511:        slash = path;
        !           512:
        !           513:        for (;;) {
        !           514:                /* LINTED */
        !           515:                slash += strspn(slash, "/");
        !           516:                /* LINTED */
        !           517:                slash += strcspn(slash, "/");
        !           518:
        !           519:                done = (*slash == '\0');
        !           520:                *slash = '\0';
        !           521:
        !           522:                /* skip existing path components */
        !           523:                exists = !stat(path, &sb);
        !           524:                if (!done && exists && S_ISDIR(sb.st_mode)) {
        !           525:                        *slash = '/';
        !           526:                        continue;
        !           527:                }
        !           528:
        !           529:                if (mkdir(path, done ? mode : dir_mode) == 0) {
        !           530:                        if (mode > 0777 && chmod(path, mode) < 0)
        !           531:                                return (-1);
        !           532:                } else {
        !           533:                        if (!exists) {
        !           534:                                /* Not there */
        !           535:                                return (-1);
        !           536:                        }
        !           537:                        if (!S_ISDIR(sb.st_mode)) {
        !           538:                                /* Is there, but isn't a directory */
        !           539:                                errno = ENOTDIR;
        !           540:                                return (-1);
        !           541:                        }
        !           542:                }
        !           543:
        !           544:                if (done)
        !           545:                        break;
        !           546:
        !           547:                *slash = '/';
        !           548:        }
        !           549:
        !           550:        return (0);
        !           551: }

CVSweb