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