]> git.cameronkatri.com Git - mandoc.git/blobdiff - catman.c
fix the section number in the <title> element for preformatted pages;
[mandoc.git] / catman.c
index c755d27057d2f849d54f9d7bbef1d0c1bdb365a8..b1bab0f68c4b90e68f9f58bbf7cff465040217b0 100644 (file)
--- a/catman.c
+++ b/catman.c
-/*     $Id: catman.c,v 1.11 2012/06/08 10:33:48 kristaps Exp $ */
+/*     $Id: catman.c,v 1.22 2020/06/14 23:40:31 schwarze Exp $ */
 /*
- * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
+ * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
+
+#if NEED_XPG4_2
+#define _XPG4_2
 #endif
 
-#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
-#include <sys/wait.h>
 
-#include <assert.h>
+#if HAVE_ERR
+#include <err.h>
+#endif
 #include <errno.h>
 #include <fcntl.h>
-#include <getopt.h>
+#if HAVE_FTS
+#include <fts.h>
+#else
+#include "compat_fts.h"
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
-#ifdef __linux__
-# include <db_185.h>
-#else
-# include <db.h>
-#endif
-
-#include "manpath.h"
-#include "mandocdb.h"
-
-#define        xstrlcpy(_dst, _src, _sz) \
-       do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \
-               fprintf(stderr, "%s: Path too long", (_dst)); \
-               exit(EXIT_FAILURE); \
-       } while (/* CONSTCOND */0)
-
-#define        xstrlcat(_dst, _src, _sz) \
-       do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \
-               fprintf(stderr, "%s: Path too long", (_dst)); \
-               exit(EXIT_FAILURE); \
-       } while (/* CONSTCOND */0)
+int     process_manpage(int, int, const char *);
+int     process_tree(int, int);
+void    run_mandocd(int, const char *, const char *)
+               __attribute__((__noreturn__));
+ssize_t         sock_fd_write(int, int, int, int);
+void    usage(void) __attribute__((__noreturn__));
 
-static int              indexhtml(char *, size_t, char *, size_t);
-static int              manup(const struct manpaths *, char *);
-static int              mkpath(char *, mode_t, mode_t);
-static int              treecpy(char *, char *);
-static int              update(char *, char *);
-static void             usage(void);
 
-static const char      *progname;
-static int              verbose;
-static int              force;
-
-int
-main(int argc, char *argv[])
+void
+run_mandocd(int sockfd, const char *outtype, const char* defos)
 {
-       int              ch;
-       char            *aux, *base, *conf_file;
-       struct manpaths  dirs;
-       char             buf[MAXPATHLEN];
-       extern char     *optarg;
-       extern int       optind;
-
-       progname = strrchr(argv[0], '/');
-       if (progname == NULL)
-               progname = argv[0];
-       else
-               ++progname;
+       char     sockfdstr[10];
 
-       aux = base = conf_file = NULL;
-       xstrlcpy(buf, "/var/www/cache/man.cgi", MAXPATHLEN);
+       if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
+               err(1, "snprintf");
+       if (defos == NULL)
+               execlp("mandocd", "mandocd", "-T", outtype,
+                   sockfdstr, (char *)NULL);
+       else
+               execlp("mandocd", "mandocd", "-T", outtype,
+                   "-I", defos, sockfdstr, (char *)NULL);
+       err(1, "exec");
+}
 
-       while (-1 != (ch = getopt(argc, argv, "C:fm:M:o:v")))
-               switch (ch) {
-               case ('C'):
-                       conf_file = optarg;
-                       break;
-               case ('f'):
-                       force = 1;
-                       break;
-               case ('m'):
-                       aux = optarg;
-                       break;
-               case ('M'):
-                       base = optarg;
-                       break;
-               case ('o'):
-                       xstrlcpy(buf, optarg, MAXPATHLEN);
-                       break;
-               case ('v'):
-                       verbose++;
+ssize_t
+sock_fd_write(int fd, int fd0, int fd1, int fd2)
+{
+       const struct timespec timeout = { 0, 10000000 };  /* 0.01 s */
+       struct msghdr    msg;
+       struct iovec     iov;
+       union {
+               struct cmsghdr   cmsghdr;
+               char             control[CMSG_SPACE(3 * sizeof(int))];
+       } cmsgu;
+       struct cmsghdr  *cmsg;
+       int             *walk;
+       ssize_t          sz;
+       unsigned char    dummy[1] = {'\0'};
+
+       iov.iov_base = dummy;
+       iov.iov_len = sizeof(dummy);
+
+       msg.msg_name = NULL;
+       msg.msg_namelen = 0;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       msg.msg_control = cmsgu.control;
+       msg.msg_controllen = sizeof(cmsgu.control);
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+
+       walk = (int *)CMSG_DATA(cmsg);
+       *(walk++) = fd0;
+       *(walk++) = fd1;
+       *(walk++) = fd2;
+
+       /*
+        * It appears that on some systems, sendmsg(3)
+        * may return EAGAIN even in blocking mode.
+        * Seen for example on Oracle Solaris 11.2.
+        * The sleeping time was chosen by experimentation,
+        * to neither cause more than a handful of retries
+        * in normal operation nor unnecessary delays.
+        */
+       for (;;) {
+               if ((sz = sendmsg(fd, &msg, 0)) != -1 ||
+                   errno != EAGAIN)
                        break;
-               default:
-                       usage();
-                       return(EXIT_FAILURE);
-               }
-
-       argc -= optind;
-       argv += optind;
-
-       if (argc > 0) {
-               usage();
-               return(EXIT_FAILURE);
+               nanosleep(&timeout, NULL);
        }
-
-       memset(&dirs, 0, sizeof(struct manpaths));
-       manpath_parse(&dirs, conf_file, base, aux);
-       ch = manup(&dirs, buf);
-       manpath_free(&dirs);
-       return(ch ? EXIT_SUCCESS : EXIT_FAILURE);
+       return sz;
 }
 
-static void
-usage(void)
+int
+process_manpage(int srv_fd, int dstdir_fd, const char *path)
 {
-       
-       fprintf(stderr, "usage: %s "
-                       "[-fv] "
-                       "[-C file] "
-                       "[-o path] "
-                       "[-m manpath] "
-                       "[-M manpath]\n",
-                       progname);
-}
+       int      in_fd, out_fd;
+       int      irc;
 
-/*
- * If "src" file doesn't exist (errors out), return -1.  Otherwise,
- * return 1 if "src" is newer (which also happens "dst" doesn't exist)
- * and 0 otherwise.
- */
-static int
-isnewer(const char *dst, const char *src)
-{
-       struct stat      s1, s2;
+       if ((in_fd = open(path, O_RDONLY)) == -1) {
+               warn("open(%s)", path);
+               return 0;
+       }
 
-       if (-1 == stat(src, &s1))
-               return(-1);
-       if (force)
-               return(1);
+       if ((out_fd = openat(dstdir_fd, path,
+           O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
+           S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
+               warn("openat(%s)", path);
+               close(in_fd);
+               return 0;
+       }
 
-       return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);
-}
+       irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO);
 
-/*
- * Copy the contents of one file into another.
- * Returns 0 on failure, 1 on success.
- */
-static int
-filecpy(const char *dst, const char *src)
-{
-       char             buf[BUFSIZ];
-       int              sfd, dfd, rc;
-       ssize_t          rsz, wsz;
-
-       sfd = dfd = -1;
-       rc = 0;
-
-       if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {
-               perror(dst);
-               goto out;
-       } else if (-1 == (sfd = open(src, O_RDONLY, 0))) {
-               perror(src);
-               goto out;
-       } 
-
-       while ((rsz = read(sfd, buf, BUFSIZ)) > 0)
-               if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {
-                       perror(dst);
-                       goto out;
-               } else if (wsz < rsz) {
-                       fprintf(stderr, "%s: Short write\n", dst);
-                       goto out;
-               }
-       
-       if (rsz < 0)
-               perror(src);
-       else
-               rc = 1;
-out:
-       if (-1 != sfd)
-               close(sfd);
-       if (-1 != dfd)
-               close(dfd);
-
-       return(rc);
+       close(in_fd);
+       close(out_fd);
+
+       if (irc < 0) {
+               warn("sendmsg");
+               return -1;
+       }
+       return 0;
 }
 
-/*
- * Pass over the recno database and re-create HTML pages if they're
- * found to be out of date.
- * Returns -1 on fatal error, 1 on success.
- */
-static int
-indexhtml(char *src, size_t ssz, char *dst, size_t dsz)
+int
+process_tree(int srv_fd, int dstdir_fd)
 {
-       DB              *idx;
-       DBT              key, val;
-       int              c, rc;
-       unsigned int     fl;
-       const char      *f;
-       char            *d;
-       char             fname[MAXPATHLEN];
-       pid_t            pid;
-
-       pid = -1;
+       FTS             *ftsp;
+       FTSENT          *entry;
+       const char      *argv[2];
+       const char      *path;
 
-       xstrlcpy(fname, dst, MAXPATHLEN);
-       xstrlcat(fname, "/", MAXPATHLEN);
-       xstrlcat(fname, MANDOC_IDX, MAXPATHLEN);
+       argv[0] = ".";
+       argv[1] = (char *)NULL;
 
-       idx = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);
-       if (NULL == idx) {
-               perror(fname);
-               return(-1);
+       if ((ftsp = fts_open((char * const *)argv,
+           FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
+               warn("fts_open");
+               return -1;
        }
 
-       fl = R_FIRST;
-       while (0 == (c = (*idx->seq)(idx, &key, &val, fl))) {
-               fl = R_NEXT;
-               /*
-                * If the record is zero-length, then it's unassigned.
-                * Skip past these.
-                */
-               if (0 == val.size)
-                       continue;
-
-               f = (const char *)val.data + 1;
-               if (NULL == memchr(f, '\0', val.size - 1))
+       while ((entry = fts_read(ftsp)) != NULL) {
+               path = entry->fts_path + 2;
+               switch (entry->fts_info) {
+               case FTS_F:
+                       if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
+                               fts_close(ftsp);
+                               return -1;
+                       }
                        break;
-
-               src[(int)ssz] = dst[(int)dsz] = '\0';
-
-               xstrlcat(dst, "/", MAXPATHLEN);
-               xstrlcat(dst, f, MAXPATHLEN);
-
-               xstrlcat(src, "/", MAXPATHLEN);
-               xstrlcat(src, f, MAXPATHLEN);
-
-               if (-1 == (rc = isnewer(dst, src))) {
-                       fprintf(stderr, "%s: File missing\n", f);
+               case FTS_D:
+                       if (*path != '\0' &&
+                           mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
+                             S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
+                           errno != EEXIST) {
+                               warn("mkdirat(%s)", path);
+                               (void)fts_set(ftsp, entry, FTS_SKIP);
+                       }
                        break;
-               } else if (0 == rc)
-                       continue;
-
-               d = strrchr(dst, '/');
-               assert(NULL != d);
-               *d = '\0';
-
-               if (-1 == mkpath(dst, 0755, 0755)) {
-                       perror(dst);
+               case FTS_DP:
                        break;
-               }
-
-               *d = '/';
-
-               if ( ! filecpy(dst, src))
+               default:
+                       warnx("%s: not a regular file", path);
                        break;
-               if (verbose)
-                       printf("%s\n", dst);
+               }
        }
 
-       (*idx->close)(idx);
-
-       if (c < 0)
-               perror(fname);
-       else if (0 == c) 
-               fprintf(stderr, "%s: Corrupt index\n", fname);
-
-       return(1 == c ? 1 : -1);
+       fts_close(ftsp);
+       return 0;
 }
 
-/*
- * Copy both recno and btree databases into the destination.
- * Call in to begin recreating HTML files.
- * Return -1 on fatal error and 1 if the update went well.
- */
-static int
-update(char *dst, char *src)
-{
-       size_t           dsz, ssz;
-
-       dsz = strlen(dst);
-       ssz = strlen(src);
-
-       xstrlcat(src, "/", MAXPATHLEN);
-       xstrlcat(dst, "/", MAXPATHLEN);
-
-       xstrlcat(src, MANDOC_DB, MAXPATHLEN);
-       xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
-
-       if ( ! filecpy(dst, src))
-               return(-1);
-       if (verbose)
-               printf("%s\n", dst);
-
-       dst[(int)dsz] = src[(int)ssz] = '\0';
-
-       xstrlcat(src, "/", MAXPATHLEN);
-       xstrlcat(dst, "/", MAXPATHLEN);
-
-       xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
-       xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
-
-       if ( ! filecpy(dst, src))
-               return(-1);
-       if (verbose)
-               printf("%s\n", dst);
-
-       dst[(int)dsz] = src[(int)ssz] = '\0';
-
-       return(indexhtml(src, ssz, dst, dsz));
-}
-
-/*
- * See if btree or recno databases in the destination are out of date
- * with respect to a single manpath component.
- * Return -1 on fatal error, 0 if the source is no longer valid (and
- * shouldn't be listed), and 1 if the update went well.
- */
-static int
-treecpy(char *dst, char *src)
-{
-       size_t           dsz, ssz;
-       int              rc;
-
-       dsz = strlen(dst);
-       ssz = strlen(src);
-
-       xstrlcat(src, "/", MAXPATHLEN);
-       xstrlcat(dst, "/", MAXPATHLEN);
-
-       xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
-       xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
-
-       if (-1 == (rc = isnewer(dst, src)))
-               return(0);
-
-       dst[(int)dsz] = src[(int)ssz] = '\0';
-
-       if (1 == rc)
-               return(update(dst, src));
-
-       xstrlcat(src, "/", MAXPATHLEN);
-       xstrlcat(dst, "/", MAXPATHLEN);
-
-       xstrlcat(src, MANDOC_DB, MAXPATHLEN);
-       xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
-
-       if (-1 == (rc = isnewer(dst, src)))
-               return(0);
-       else if (rc == 0)
-               return(1);
-
-       dst[(int)dsz] = src[(int)ssz] = '\0';
-
-       return(update(dst, src));
-}
-
-/*
- * Update the destination's file-tree with respect to changes in the
- * source manpath components.
- * "Change" is defined by an updated index or btree database.
- * Returns 1 on success, 0 on failure.
- */
-static int
-manup(const struct manpaths *dirs, char *base)
+int
+main(int argc, char **argv)
 {
-       char             dst[MAXPATHLEN],
-                        src[MAXPATHLEN];
-       const char      *path;
-       size_t           i;
-       int              c;
-       size_t           sz;
-       FILE            *f;
-
-       /* Create the path and file for the catman.conf file. */
-
-       sz = strlen(base);
-       xstrlcpy(dst, base, MAXPATHLEN);
-       xstrlcat(dst, "/etc", MAXPATHLEN);
-       if (-1 == mkpath(dst, 0755, 0755)) {
-               perror(dst);
-               return(0);
-       }
+       const char      *defos, *outtype;
+       int              srv_fds[2];
+       int              dstdir_fd;
+       int              opt;
+       pid_t            pid;
 
-       xstrlcat(dst, "/catman.conf", MAXPATHLEN);
-       if (NULL == (f = fopen(dst, "w"))) {
-               perror(dst);
-               return(0);
-       } else if (verbose)
-               printf("%s\n", dst);
-
-       for (i = 0; i < dirs->sz; i++) {
-               path = dirs->paths[i];
-               dst[(int)sz] = '\0';
-               xstrlcat(dst, path, MAXPATHLEN);
-               if (-1 == mkpath(dst, 0755, 0755)) {
-                       perror(dst);
+       defos = NULL;
+       outtype = "ascii";
+       while ((opt = getopt(argc, argv, "I:T:")) != -1) {
+               switch (opt) {
+               case 'I':
+                       defos = optarg;
                        break;
-               }
-
-               xstrlcpy(src, path, MAXPATHLEN);
-               if (-1 == (c = treecpy(dst, src)))
+               case 'T':
+                       outtype = optarg;
                        break;
-               else if (0 == c)
-                       continue;
-
-               /*
-                * We want to use a relative path here because manpath.h
-                * will realpath() when invoked with man.cgi, and we'll
-                * make sure to chdir() into the cache directory before.
-                *
-                * This allows the cache directory to be in an arbitrary
-                * place, working in both chroot() and non-chroot()
-                * "safe" modes.
-                */
-               assert('/' == path[0]);
-               fprintf(f, "_whatdb %s/whatis.db\n", path + 1);
+               default:
+                       usage();
+               }
        }
 
-       fclose(f);
-       return(i == dirs->sz);
-}
-
-/*
- * Copyright (c) 1983, 1992, 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-static int
-mkpath(char *path, mode_t mode, mode_t dir_mode)
-{
-       struct stat sb;
-       char *slash;
-       int done, exists;
-
-       slash = path;
+       if (argc > 0) {
+               argc -= optind;
+               argv += optind;
+       }
+       if (argc != 2)
+               usage();
 
-       for (;;) {
-               /* LINTED */
-               slash += strspn(slash, "/");
-               /* LINTED */
-               slash += strcspn(slash, "/");
-
-               done = (*slash == '\0');
-               *slash = '\0';
-
-               /* skip existing path components */
-               exists = !stat(path, &sb);
-               if (!done && exists && S_ISDIR(sb.st_mode)) {
-                       *slash = '/';
-                       continue;
-               }
+       if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
+               err(1, "socketpair");
+
+       pid = fork();
+       switch (pid) {
+       case -1:
+               err(1, "fork");
+       case 0:
+               close(srv_fds[0]);
+               run_mandocd(srv_fds[1], outtype, defos);
+       default:
+               break;
+       }
+       close(srv_fds[1]);
 
-               if (mkdir(path, done ? mode : dir_mode) == 0) {
-                       if (mode > 0777 && chmod(path, mode) < 0)
-                               return (-1);
-               } else {
-                       if (!exists) {
-                               /* Not there */
-                               return (-1);
-                       }
-                       if (!S_ISDIR(sb.st_mode)) {
-                               /* Is there, but isn't a directory */
-                               errno = ENOTDIR;
-                               return (-1);
-                       }
-               }
+       if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
+               err(1, "open(%s)", argv[1]);
 
-               if (done)
-                       break;
+       if (chdir(argv[0]) == -1)
+               err(1, "chdir(%s)", argv[0]);
 
-               *slash = '/';
-       }
+       return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
+}
 
-       return (0);
+void
+usage(void)
+{
+       fprintf(stderr, "usage: %s [-I os=name] [-T output] "
+           "srcdir dstdir\n", BINM_CATMAN);
+       exit(1);
 }