1 /* $Id: catman.c,v 1.11 2012/06/08 10:33:48 kristaps Exp $ */
3 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
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.
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.
21 #include <sys/param.h>
43 #define xstrlcpy(_dst, _src, _sz) \
44 do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \
45 fprintf(stderr, "%s: Path too long", (_dst)); \
47 } while (/* CONSTCOND */0)
49 #define xstrlcat(_dst, _src, _sz) \
50 do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \
51 fprintf(stderr, "%s: Path too long", (_dst)); \
53 } while (/* CONSTCOND */0)
55 static int indexhtml(char *, size_t, char *, size_t);
56 static int manup(const struct manpaths *, char *);
57 static int mkpath(char *, mode_t, mode_t);
58 static int treecpy(char *, char *);
59 static int update(char *, char *);
60 static void usage(void);
62 static const char *progname;
67 main(int argc, char *argv[])
70 char *aux, *base, *conf_file;
76 progname = strrchr(argv[0], '/');
82 aux = base = conf_file = NULL;
83 xstrlcpy(buf, "/var/www/cache/man.cgi", MAXPATHLEN);
85 while (-1 != (ch = getopt(argc, argv, "C:fm:M:o:v")))
100 xstrlcpy(buf, optarg, MAXPATHLEN);
107 return(EXIT_FAILURE);
115 return(EXIT_FAILURE);
118 memset(&dirs, 0, sizeof(struct manpaths));
119 manpath_parse(&dirs, conf_file, base, aux);
120 ch = manup(&dirs, buf);
122 return(ch ? EXIT_SUCCESS : EXIT_FAILURE);
129 fprintf(stderr, "usage: %s "
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)
144 isnewer(const char *dst, const char *src)
148 if (-1 == stat(src, &s1))
153 return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);
157 * Copy the contents of one file into another.
158 * Returns 0 on failure, 1 on success.
161 filecpy(const char *dst, const char *src)
170 if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {
173 } else if (-1 == (sfd = open(src, O_RDONLY, 0))) {
178 while ((rsz = read(sfd, buf, BUFSIZ)) > 0)
179 if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {
182 } else if (wsz < rsz) {
183 fprintf(stderr, "%s: Short write\n", dst);
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.
206 indexhtml(char *src, size_t ssz, char *dst, size_t dsz)
214 char fname[MAXPATHLEN];
219 xstrlcpy(fname, dst, MAXPATHLEN);
220 xstrlcat(fname, "/", MAXPATHLEN);
221 xstrlcat(fname, MANDOC_IDX, MAXPATHLEN);
223 idx = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);
230 while (0 == (c = (*idx->seq)(idx, &key, &val, fl))) {
233 * If the record is zero-length, then it's unassigned.
239 f = (const char *)val.data + 1;
240 if (NULL == memchr(f, '\0', val.size - 1))
243 src[(int)ssz] = dst[(int)dsz] = '\0';
245 xstrlcat(dst, "/", MAXPATHLEN);
246 xstrlcat(dst, f, MAXPATHLEN);
248 xstrlcat(src, "/", MAXPATHLEN);
249 xstrlcat(src, f, MAXPATHLEN);
251 if (-1 == (rc = isnewer(dst, src))) {
252 fprintf(stderr, "%s: File missing\n", f);
257 d = strrchr(dst, '/');
261 if (-1 == mkpath(dst, 0755, 0755)) {
268 if ( ! filecpy(dst, src))
279 fprintf(stderr, "%s: Corrupt index\n", fname);
281 return(1 == c ? 1 : -1);
285 * Copy both recno and btree databases into the destination.
286 * Call in to begin recreating HTML files.
287 * Return -1 on fatal error and 1 if the update went well.
290 update(char *dst, char *src)
297 xstrlcat(src, "/", MAXPATHLEN);
298 xstrlcat(dst, "/", MAXPATHLEN);
300 xstrlcat(src, MANDOC_DB, MAXPATHLEN);
301 xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
303 if ( ! filecpy(dst, src))
308 dst[(int)dsz] = src[(int)ssz] = '\0';
310 xstrlcat(src, "/", MAXPATHLEN);
311 xstrlcat(dst, "/", MAXPATHLEN);
313 xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
314 xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
316 if ( ! filecpy(dst, src))
321 dst[(int)dsz] = src[(int)ssz] = '\0';
323 return(indexhtml(src, ssz, dst, dsz));
327 * See if btree or recno databases in the destination are out of date
328 * with respect to a single manpath component.
329 * Return -1 on fatal error, 0 if the source is no longer valid (and
330 * shouldn't be listed), and 1 if the update went well.
333 treecpy(char *dst, char *src)
341 xstrlcat(src, "/", MAXPATHLEN);
342 xstrlcat(dst, "/", MAXPATHLEN);
344 xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
345 xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
347 if (-1 == (rc = isnewer(dst, src)))
350 dst[(int)dsz] = src[(int)ssz] = '\0';
353 return(update(dst, src));
355 xstrlcat(src, "/", MAXPATHLEN);
356 xstrlcat(dst, "/", MAXPATHLEN);
358 xstrlcat(src, MANDOC_DB, MAXPATHLEN);
359 xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
361 if (-1 == (rc = isnewer(dst, src)))
366 dst[(int)dsz] = src[(int)ssz] = '\0';
368 return(update(dst, src));
372 * Update the destination's file-tree with respect to changes in the
373 * source manpath components.
374 * "Change" is defined by an updated index or btree database.
375 * Returns 1 on success, 0 on failure.
378 manup(const struct manpaths *dirs, char *base)
380 char dst[MAXPATHLEN],
388 /* Create the path and file for the catman.conf file. */
391 xstrlcpy(dst, base, MAXPATHLEN);
392 xstrlcat(dst, "/etc", MAXPATHLEN);
393 if (-1 == mkpath(dst, 0755, 0755)) {
398 xstrlcat(dst, "/catman.conf", MAXPATHLEN);
399 if (NULL == (f = fopen(dst, "w"))) {
405 for (i = 0; i < dirs->sz; i++) {
406 path = dirs->paths[i];
408 xstrlcat(dst, path, MAXPATHLEN);
409 if (-1 == mkpath(dst, 0755, 0755)) {
414 xstrlcpy(src, path, MAXPATHLEN);
415 if (-1 == (c = treecpy(dst, src)))
421 * We want to use a relative path here because manpath.h
422 * will realpath() when invoked with man.cgi, and we'll
423 * make sure to chdir() into the cache directory before.
425 * This allows the cache directory to be in an arbitrary
426 * place, working in both chroot() and non-chroot()
429 assert('/' == path[0]);
430 fprintf(f, "_whatdb %s/whatis.db\n", path + 1);
434 return(i == dirs->sz);
438 * Copyright (c) 1983, 1992, 1993
439 * The Regents of the University of California. All rights reserved.
441 * Redistribution and use in source and binary forms, with or without
442 * modification, are permitted provided that the following conditions
444 * 1. Redistributions of source code must retain the above copyright
445 * notice, this list of conditions and the following disclaimer.
446 * 2. Redistributions in binary form must reproduce the above copyright
447 * notice, this list of conditions and the following disclaimer in the
448 * documentation and/or other materials provided with the distribution.
449 * 3. Neither the name of the University nor the names of its contributors
450 * may be used to endorse or promote products derived from this software
451 * without specific prior written permission.
453 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
454 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
455 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
456 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
457 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
458 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
459 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
460 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
461 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
462 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
466 mkpath(char *path, mode_t mode, mode_t dir_mode)
476 slash += strspn(slash, "/");
478 slash += strcspn(slash, "/");
480 done = (*slash == '\0');
483 /* skip existing path components */
484 exists = !stat(path, &sb);
485 if (!done && exists && S_ISDIR(sb.st_mode)) {
490 if (mkdir(path, done ? mode : dir_mode) == 0) {
491 if (mode > 0777 && chmod(path, mode) < 0)
498 if (!S_ISDIR(sb.st_mode)) {
499 /* Is there, but isn't a directory */