Annotation of mandoc/manup.c, Revision 1.2
1.2 ! kristaps 1: /* $Id: manup.c,v 1.1 2011/11/24 10:33:38 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:
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:
1.2 ! kristaps 394: if (-1 == (rc = isnewer(dst, src)))
1.1 kristaps 395: return(0);
1.2 ! kristaps 396: else if (rc == 0)
! 397: return(1);
1.1 kristaps 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:
1.2 ! kristaps 430: if (verbose)
! 431: printf("%s\n", dst);
! 432:
1.1 kristaps 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:
1.2 ! kristaps 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);
1.1 kristaps 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