Annotation of mandoc/catman.c, Revision 1.8
1.8 ! kristaps 1: /* $Id: catman.c,v 1.7 2011/12/16 12:06:35 kristaps Exp $ */
1.1 kristaps 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:
1.6 kristaps 54: static int indexhtml(char *, size_t, char *, size_t);
1.4 kristaps 55: static int manup(const struct manpaths *, char *);
1.1 kristaps 56: static int mkpath(char *, mode_t, mode_t);
1.6 kristaps 57: static int treecpy(char *, char *);
58: static int update(char *, char *);
1.1 kristaps 59: static void usage(void);
60:
61: static const char *progname;
62: static int verbose;
63: static int force;
64:
65: int
66: main(int argc, char *argv[])
67: {
68: int ch;
1.8 ! kristaps 69: char *aux, *base, *conf_file;
1.1 kristaps 70: struct manpaths dirs;
1.4 kristaps 71: char buf[MAXPATHLEN];
1.1 kristaps 72: extern char *optarg;
73: extern int optind;
74:
75: progname = strrchr(argv[0], '/');
76: if (progname == NULL)
77: progname = argv[0];
78: else
79: ++progname;
80:
81: aux = base = NULL;
1.4 kristaps 82: xstrlcpy(buf, "/var/www/cache/man.cgi", MAXPATHLEN);
1.1 kristaps 83:
1.8 ! kristaps 84: while (-1 != (ch = getopt(argc, argv, "C:fm:M:o:v")))
1.1 kristaps 85: switch (ch) {
1.8 ! kristaps 86: case ('C'):
! 87: conf_file = optarg;
! 88: break;
1.1 kristaps 89: case ('f'):
90: force = 1;
91: break;
92: case ('m'):
93: aux = optarg;
94: break;
95: case ('M'):
96: base = optarg;
97: break;
98: case ('o'):
1.4 kristaps 99: xstrlcpy(buf, optarg, MAXPATHLEN);
1.1 kristaps 100: break;
101: case ('v'):
102: verbose++;
103: break;
104: default:
105: usage();
106: return(EXIT_FAILURE);
107: }
108:
109: argc -= optind;
110: argv += optind;
111:
112: if (argc > 0) {
113: usage();
114: return(EXIT_FAILURE);
115: }
116:
117: memset(&dirs, 0, sizeof(struct manpaths));
1.8 ! kristaps 118: manpath_parse(&dirs, conf_file, base, aux);
1.4 kristaps 119: ch = manup(&dirs, buf);
1.1 kristaps 120: manpath_free(&dirs);
121: return(ch ? EXIT_SUCCESS : EXIT_FAILURE);
122: }
123:
124: static void
125: usage(void)
126: {
127:
128: fprintf(stderr, "usage: %s "
129: "[-fv] "
1.8 ! kristaps 130: "[-C file] "
1.1 kristaps 131: "[-o path] "
132: "[-m manpath] "
133: "[-M manpath]\n",
134: progname);
135: }
136:
137: /*
138: * If "src" file doesn't exist (errors out), return -1. Otherwise,
139: * return 1 if "src" is newer (which also happens "dst" doesn't exist)
140: * and 0 otherwise.
141: */
142: static int
143: isnewer(const char *dst, const char *src)
144: {
145: struct stat s1, s2;
146:
147: if (-1 == stat(src, &s1))
148: return(-1);
149: if (force)
150: return(1);
151:
152: return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);
153: }
154:
155: /*
156: * Copy the contents of one file into another.
157: * Returns 0 on failure, 1 on success.
158: */
159: static int
160: filecpy(const char *dst, const char *src)
161: {
162: char buf[BUFSIZ];
163: int sfd, dfd, rc;
164: ssize_t rsz, wsz;
165:
166: sfd = dfd = -1;
167: rc = 0;
168:
169: if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {
170: perror(dst);
171: goto out;
172: } else if (-1 == (sfd = open(src, O_RDONLY, 0))) {
173: perror(src);
174: goto out;
175: }
176:
177: while ((rsz = read(sfd, buf, BUFSIZ)) > 0)
178: if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {
179: perror(dst);
180: goto out;
181: } else if (wsz < rsz) {
182: fprintf(stderr, "%s: Short write\n", dst);
183: goto out;
184: }
185:
186: if (rsz < 0)
187: perror(src);
188: else
189: rc = 1;
190: out:
191: if (-1 != sfd)
192: close(sfd);
193: if (-1 != dfd)
194: close(dfd);
195:
196: return(rc);
197: }
198:
199: /*
200: * Pass over the recno database and re-create HTML pages if they're
201: * found to be out of date.
202: * Returns -1 on fatal error, 1 on success.
203: */
204: static int
1.6 kristaps 205: indexhtml(char *src, size_t ssz, char *dst, size_t dsz)
1.1 kristaps 206: {
1.3 kristaps 207: DB *idx;
1.1 kristaps 208: DBT key, val;
209: int c, rc;
210: unsigned int fl;
1.7 kristaps 211: const char *f;
1.1 kristaps 212: char *d;
213: char fname[MAXPATHLEN];
214: pid_t pid;
215:
216: pid = -1;
217:
218: xstrlcpy(fname, dst, MAXPATHLEN);
219: xstrlcat(fname, "/mandoc.index", MAXPATHLEN);
220:
1.3 kristaps 221: idx = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);
222: if (NULL == idx) {
1.1 kristaps 223: perror(fname);
224: return(-1);
225: }
226:
227: fl = R_FIRST;
1.3 kristaps 228: while (0 == (c = (*idx->seq)(idx, &key, &val, fl))) {
1.1 kristaps 229: fl = R_NEXT;
1.7 kristaps 230: /*
231: * If the record is zero-length, then it's unassigned.
232: * Skip past these.
233: */
1.3 kristaps 234: if (0 == val.size)
235: continue;
1.7 kristaps 236:
237: f = (const char *)val.data + 1;
238: if (NULL == memchr(f, '\0', val.size - 1))
1.3 kristaps 239: break;
1.1 kristaps 240:
1.6 kristaps 241: src[(int)ssz] = dst[(int)dsz] = '\0';
242:
243: xstrlcat(dst, "/", MAXPATHLEN);
244: xstrlcat(dst, f, MAXPATHLEN);
1.1 kristaps 245:
1.6 kristaps 246: xstrlcat(src, "/", MAXPATHLEN);
247: xstrlcat(src, f, MAXPATHLEN);
1.1 kristaps 248:
1.6 kristaps 249: if (-1 == (rc = isnewer(dst, src))) {
1.3 kristaps 250: fprintf(stderr, "%s: File missing\n", f);
1.1 kristaps 251: break;
252: } else if (0 == rc)
253: continue;
254:
1.6 kristaps 255: d = strrchr(dst, '/');
1.1 kristaps 256: assert(NULL != d);
257: *d = '\0';
258:
1.6 kristaps 259: if (-1 == mkpath(dst, 0755, 0755)) {
260: perror(dst);
1.1 kristaps 261: break;
262: }
263:
264: *d = '/';
1.2 kristaps 265:
1.6 kristaps 266: if ( ! filecpy(dst, src))
1.3 kristaps 267: break;
1.1 kristaps 268: if (verbose)
1.6 kristaps 269: printf("%s\n", dst);
1.1 kristaps 270: }
271:
1.3 kristaps 272: (*idx->close)(idx);
1.1 kristaps 273:
274: if (c < 0)
275: perror(fname);
1.3 kristaps 276: else if (0 == c)
277: fprintf(stderr, "%s: Corrupt index\n", fname);
1.1 kristaps 278:
279: return(1 == c ? 1 : -1);
280: }
281:
282: /*
283: * Copy both recno and btree databases into the destination.
284: * Call in to begin recreating HTML files.
285: * Return -1 on fatal error and 1 if the update went well.
286: */
287: static int
1.6 kristaps 288: update(char *dst, char *src)
1.1 kristaps 289: {
290: size_t dsz, ssz;
291:
292: dsz = strlen(dst);
293: ssz = strlen(src);
294:
295: xstrlcat(src, "/mandoc.db", MAXPATHLEN);
296: xstrlcat(dst, "/mandoc.db", MAXPATHLEN);
297:
298: if ( ! filecpy(dst, src))
299: return(-1);
300: if (verbose)
301: printf("%s\n", dst);
302:
303: dst[(int)dsz] = src[(int)ssz] = '\0';
304:
305: xstrlcat(src, "/mandoc.index", MAXPATHLEN);
306: xstrlcat(dst, "/mandoc.index", MAXPATHLEN);
307:
308: if ( ! filecpy(dst, src))
309: return(-1);
310: if (verbose)
311: printf("%s\n", dst);
312:
1.6 kristaps 313: dst[(int)dsz] = src[(int)ssz] = '\0';
1.1 kristaps 314:
1.6 kristaps 315: return(indexhtml(src, ssz, dst, dsz));
1.1 kristaps 316: }
317:
318: /*
319: * See if btree or recno databases in the destination are out of date
320: * with respect to a single manpath component.
321: * Return -1 on fatal error, 0 if the source is no longer valid (and
322: * shouldn't be listed), and 1 if the update went well.
323: */
324: static int
1.6 kristaps 325: treecpy(char *dst, char *src)
1.1 kristaps 326: {
327: size_t dsz, ssz;
328: int rc;
329:
330: dsz = strlen(dst);
331: ssz = strlen(src);
332:
333: xstrlcat(src, "/mandoc.index", MAXPATHLEN);
334: xstrlcat(dst, "/mandoc.index", MAXPATHLEN);
335:
336: if (-1 == (rc = isnewer(dst, src)))
337: return(0);
338:
339: dst[(int)dsz] = src[(int)ssz] = '\0';
340:
341: if (1 == rc)
1.6 kristaps 342: return(update(dst, src));
1.1 kristaps 343:
344: xstrlcat(src, "/mandoc.db", MAXPATHLEN);
345: xstrlcat(dst, "/mandoc.db", MAXPATHLEN);
346:
347: if (-1 == (rc = isnewer(dst, src)))
348: return(0);
349: else if (rc == 0)
350: return(1);
351:
352: dst[(int)dsz] = src[(int)ssz] = '\0';
353:
1.6 kristaps 354: return(update(dst, src));
1.1 kristaps 355: }
356:
357: /*
358: * Update the destination's file-tree with respect to changes in the
359: * source manpath components.
360: * "Change" is defined by an updated index or btree database.
361: * Returns 1 on success, 0 on failure.
362: */
363: static int
1.4 kristaps 364: manup(const struct manpaths *dirs, char *base)
1.1 kristaps 365: {
366: char dst[MAXPATHLEN],
367: src[MAXPATHLEN];
368: const char *path;
369: int i, c;
370: size_t sz;
371: FILE *f;
372:
1.4 kristaps 373: /* Create the path and file for the catman.conf file. */
374:
375: sz = strlen(base);
376: xstrlcpy(dst, base, MAXPATHLEN);
1.1 kristaps 377: xstrlcat(dst, "/etc", MAXPATHLEN);
378: if (-1 == mkpath(dst, 0755, 0755)) {
379: perror(dst);
380: return(0);
381: }
382:
1.3 kristaps 383: xstrlcat(dst, "/catman.conf", MAXPATHLEN);
1.1 kristaps 384: if (NULL == (f = fopen(dst, "w"))) {
385: perror(dst);
386: return(0);
1.4 kristaps 387: } else if (verbose)
388: printf("%s\n", dst);
1.1 kristaps 389:
390: for (i = 0; i < dirs->sz; i++) {
391: path = dirs->paths[i];
1.6 kristaps 392: dst[(int)sz] = '\0';
1.1 kristaps 393: xstrlcat(dst, path, MAXPATHLEN);
394: if (-1 == mkpath(dst, 0755, 0755)) {
395: perror(dst);
396: break;
397: }
398:
399: xstrlcpy(src, path, MAXPATHLEN);
1.6 kristaps 400: if (-1 == (c = treecpy(dst, src)))
1.1 kristaps 401: break;
402: else if (0 == c)
403: continue;
404:
405: /*
406: * We want to use a relative path here because manpath.h
407: * will realpath() when invoked with man.cgi, and we'll
408: * make sure to chdir() into the cache directory before.
409: *
410: * This allows the cache directory to be in an arbitrary
411: * place, working in both chroot() and non-chroot()
412: * "safe" modes.
413: */
414: assert('/' == path[0]);
415: fprintf(f, "_whatdb %s/whatis.db\n", path + 1);
416: }
417:
418: fclose(f);
419: return(i == dirs->sz);
420: }
421:
422: /*
423: * Copyright (c) 1983, 1992, 1993
424: * The Regents of the University of California. All rights reserved.
425: *
426: * Redistribution and use in source and binary forms, with or without
427: * modification, are permitted provided that the following conditions
428: * are met:
429: * 1. Redistributions of source code must retain the above copyright
430: * notice, this list of conditions and the following disclaimer.
431: * 2. Redistributions in binary form must reproduce the above copyright
432: * notice, this list of conditions and the following disclaimer in the
433: * documentation and/or other materials provided with the distribution.
434: * 3. Neither the name of the University nor the names of its contributors
435: * may be used to endorse or promote products derived from this software
436: * without specific prior written permission.
437: *
438: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
439: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
440: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
441: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
442: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
443: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
444: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
445: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
446: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
447: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
448: * SUCH DAMAGE.
449: */
450: static int
451: mkpath(char *path, mode_t mode, mode_t dir_mode)
452: {
453: struct stat sb;
454: char *slash;
455: int done, exists;
456:
457: slash = path;
458:
459: for (;;) {
460: /* LINTED */
461: slash += strspn(slash, "/");
462: /* LINTED */
463: slash += strcspn(slash, "/");
464:
465: done = (*slash == '\0');
466: *slash = '\0';
467:
468: /* skip existing path components */
469: exists = !stat(path, &sb);
470: if (!done && exists && S_ISDIR(sb.st_mode)) {
471: *slash = '/';
472: continue;
473: }
474:
475: if (mkdir(path, done ? mode : dir_mode) == 0) {
476: if (mode > 0777 && chmod(path, mode) < 0)
477: return (-1);
478: } else {
479: if (!exists) {
480: /* Not there */
481: return (-1);
482: }
483: if (!S_ISDIR(sb.st_mode)) {
484: /* Is there, but isn't a directory */
485: errno = ENOTDIR;
486: return (-1);
487: }
488: }
489:
490: if (done)
491: break;
492:
493: *slash = '/';
494: }
495:
496: return (0);
497: }
CVSweb