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