commit d0519fb4de3209440707333ce6b49d0f2d7c7f42
parent ef4ca38c2c1690a176c16ad85b07087adda25611
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:31:37 +0100
1.41
Diffstat:
13 files changed, 508 insertions(+), 17 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,6 +1,10 @@
ChangeLog for Crossroads
------------------------------------------------------------------------------
+1.41 [KK 2007-05-18]
+ - Included c-conf 1.07 (caching provisions - way faster build)
+ - Fixed deliverable, version 1.40 lacked files (DISTRO WAS BROKEN)
+
1.40 [KK 2007-05-16]
- Added flag -m to control shared memory access mode.
- Implemented flag -x for XML report of status.
diff --git a/Makefile b/Makefile
@@ -21,6 +21,7 @@ clean:
BASE=$(BASE) $(MAKE) -C doc clean
find $(BASE) -type f -name '*~' -exec rm {} \;
find $(BASE) -type f -name .gdb_history -exec rm {} \;
+ rm -f src/c-conf.cache
distclean:
BASE=$(BASE) $(MAKE) -C src distclean
diff --git a/etc/Makefile.conf b/etc/Makefile.conf
@@ -1,8 +1,9 @@
# The used C compiler.
-CC := $(shell $(BASE)/tools/c-conf c-compiler)
+CC := $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache c-compiler)
# Link time libraries.
-LIBS := $(shell $(BASE)/tools/c-conf lib ucb nsl pthread socket 2>/dev/null)
+LIBS := $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache lib \
+ ucb nsl pthread socket 2>/dev/null)
# Compiletime defines. Remember to update show_config() when changing
# this list!
@@ -13,11 +14,19 @@ DEFS := -DDEFAULT_CONF='"$(DEFAULT_CONF)"' -DMAX_BACKEND=$(MAX_BACKEND) \
-DTCP_BUFSZ=$(TCP_BUFSZ) -DRETRY_WAIT=$(RETRY_WAIT) \
-DREVVER='"$(REVVER)"' -DBINDIR='"$(BINDIR)"' \
-DEXTRALIBS='"$(EXTRALIBS)"' -DLIBS='"$(LIBS)"' \
- $(shell $(BASE)/tools/c-conf ifheader01 malloc.h HAVE_MALLOC_H) \
- $(shell $(BASE)/tools/c-conf ifheader01 stdint.h HAVE_STDINT_H) \
- $(shell $(BASE)/tools/c-conf libfunction01 flock HAVE_FLOCK) \
- $(shell $(BASE)/tools/c-conf libfunction01 lockf HAVE_LOCKF) \
- $(shell $(BASE)/tools/c-conf libfunction01 strlcat HAVE_STRLCAT) \
- $(shell $(BASE)/tools/c-conf libfunction01 sranddev HAVE_SRANDDEV) \
- $(shell $(BASE)/tools/c-conf libfunction01 vsyslog HAVE_VSYSLOG) \
- $(shell $(BASE)/tools/c-conf libfunction01 strcasestr HAVE_STRCASESTR)
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ ifheader01 malloc.h HAVE_MALLOC_H) \
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ ifheader01 stdint.h HAVE_STDINT_H) \
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ libfunction01 flock HAVE_FLOCK) \
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ libfunction01 lockf HAVE_LOCKF) \
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ libfunction01 strlcat HAVE_STRLCAT) \
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ libfunction01 sranddev HAVE_SRANDDEV) \
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ libfunction01 vsyslog HAVE_VSYSLOG) \
+ $(shell $(BASE)/tools/c-conf -c $(BASE)/src/c-conf.cache \
+ libfunction01 strcasestr HAVE_STRCASESTR)
diff --git a/etc/Makefile.def b/etc/Makefile.def
@@ -6,7 +6,7 @@
# Versioning. This defines the overall version ID and must match the topmost
# entry in the ChangeLog.
-VER = 1.40
+VER = 1.41
# Revision version, auto-detected.
REVVER = $(shell $(BASE)/etc/svnrev $(BASE)/ChangeLog $(BASE)/etc/svnrev.txt)
diff --git a/etc/svnrev.txt b/etc/svnrev.txt
@@ -1 +1 @@
-152
+153
diff --git a/src/Makefile b/src/Makefile
@@ -23,7 +23,7 @@ clean:
BASE=$(BASE) make -C $$d clean || exit 1; \
done
-distclean:
+distclean: clean
for d in ${DIRS}; do \
echo "Making distclean in $$d"; \
echo BASE=$(BASE) make -C $$d distclean; \
diff --git a/src/crossroads-daemon/Makefile b/src/crossroads-daemon/Makefile
@@ -0,0 +1,23 @@
+include $(BASE)/etc/Makefile.def
+include $(BASE)/etc/Makefile.conf
+
+BIN = crossroads-daemon
+all: $(BIN)
+
+install: all $(BINDIR)/$(BIN)
+
+clean:
+ rm -f *.o $(BIN)
+
+$(BINDIR)/$(BIN): $(BIN) ../lib/libcrossroads.a
+ cp $(BIN) $(BINDIR)/$(BIN)
+ strip $(BINDIR)/$(BIN)
+
+main.o: main.c
+ $(CC) -c -g -W -Wall $(DEFS) main.c
+
+$(BIN): main.o
+ $(CC) -o $(BIN) main.o -L$(BASE)/src/lib -lcrossroads
+
+# Extra deps:
+main.o: main.c $(BASE)/src/crossroads.h $(BASE)/etc/Makefile.def
diff --git a/src/crossroads-daemon/main.c b/src/crossroads-daemon/main.c
@@ -0,0 +1,90 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.40, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
+#include "../crossroads.h"
+
+int main (int argc, char **argv) {
+ int opt, i;
+
+ /* Remember original ac/av/ep */
+ org_argc = argc;
+ org_argv = argv;
+
+ /* Parse options. */
+ config_file = DEFAULT_CONF;
+ while ( (opt = getopt (argc, argv, "?c:fhvi:Val:stCm:x")) > 0 )
+ switch (opt) {
+ case 'a':
+ log_activity++;
+ break;
+ case 'c':
+ config_file = optarg;
+ break;
+ case 'v':
+ flag_verbose++;
+ break;
+ case 'i':
+ iflag_present++;
+ break;
+ case 'l':
+ switch (atoi (optarg)) {
+ case 0:
+ log_facility = LOG_LOCAL0;
+ break;
+ case 1:
+ log_facility = LOG_LOCAL1;
+ break;
+ case 2:
+ log_facility = LOG_LOCAL2;
+ break;
+ case 3:
+ log_facility = LOG_LOCAL3;
+ break;
+ case 4:
+ log_facility = LOG_LOCAL4;
+ break;
+ case 5:
+ log_facility = LOG_LOCAL5;
+ break;
+ case 6:
+ log_facility = LOG_LOCAL6;
+ break;
+ case 7:
+ log_facility = LOG_LOCAL7;
+ break;
+ default:
+ error ("Bad invocation of crossroads-daemon");
+ }
+ break;
+ case 'm':
+ shmperm = strtol (optarg, 0, 8);
+ break;
+ case 's':
+ sloppyportbind++;
+ break;
+ case 't':
+ tabular_status++;
+ break;
+ case 'x':
+ xml_status++;
+ break;
+ default:
+ error ("Bad invocation of crossroads-daemon");
+ }
+
+ /* We're the daemon executable. We know what to do -- serve. */
+ create_commandline_space();
+ config_read (0);
+ msg ("Starting services");
+ for (i = 0; i < nservice; i++) {
+ /* Start the service. */
+ activeservice = service + i;
+ runservice ();
+ }
+
+ /* All done. */
+ return (0);
+}
diff --git a/src/lib/configread.c b/src/lib/configread.c
@@ -0,0 +1,159 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.40, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
+#include "../crossroads.h"
+
+static void rd (int fd, void *buf, unsigned sz) {
+ int res;
+ int toread = (int) sz;
+
+ if ( (res = read (fd, buf, sz)) != toread )
+ error ("Failed to fetch %d bytes (%d)", toread, res);
+}
+
+static int rd_int (int fd) {
+ int res;
+
+ rd (fd, &res, sizeof(int));
+ return (res);
+}
+
+static int rd_uns (int fd) {
+ unsigned res;
+
+ rd (fd, &res, sizeof(unsigned));
+ return (res);
+}
+
+static char *rd_str (int fd) {
+ char *buf;
+ int len;
+
+ len = rd_int (fd);
+ if (!len)
+ return (0);
+ if (len > 1024)
+ error ("Maximum string length exceeded, attempt for %d", len);
+ buf = xcalloc (len + 1);
+ rd (fd, buf, len);
+ return (buf);
+}
+
+
+void config_read (int fd) {
+ int i, j, k;
+
+ msg ("Reading binary service definition from fd %d", fd);
+
+ nservice = rd_int (fd);
+ msg ("Configured services: %d", nservice);
+ service = xcalloc (nservice * sizeof(Service));
+ for (i = 0; i < nservice; i++) {
+ /* Basic stuff */
+ service[i].name = rd_str (fd);
+ msg (" Scanning service %d (%s)", i + 1, service[i].name);
+ service[i].bind = rd_str (fd);
+ service[i].port = rd_uns (fd);
+ service[i].verbosity = rd_uns (fd);
+ service[i].dispatchtype = (Dispatchtype)rd_int (fd);
+ service[i].dispatchover = rd_uns (fd);
+ service[i].dispatchext = rd_str (fd);
+ service[i].rev_interval = rd_uns (fd);
+ service[i].backlog = rd_uns (fd);
+ service[i].shmkey = rd_uns (fd);
+ service[i].connectiontimeout = rd_uns (fd);
+ service[i].maxconnections = rd_uns (fd);
+ service[i].type = (Dispatchtype)rd_int (fd);
+ service[i].allowfile = rd_str (fd);
+ service[i].denyfile = rd_str (fd);
+ service[i].uid = rd_int (fd);
+ service[i].gid = rd_int (fd);
+
+ /* Deny chain */
+ service[i].ndenychain = rd_int (fd);
+ service[i].denychain = xcalloc (service[i].ndenychain *
+ sizeof(IpFilter));
+ for (j = 0; j < service[i].ndenychain; j++) {
+ service[i].denychain[j].ip = rd_int (fd);
+ service[i].denychain[j].mask = rd_int (fd);
+ }
+
+ /* Allow chain */
+ service[i].nallowchain = rd_int (fd);
+ service[i].allowchain = xcalloc (service[i].nallowchain *
+ sizeof(IpFilter));
+ for (j = 0; j < service[i].nallowchain; j++) {
+ service[i].allowchain[j].ip = rd_int (fd);
+ service[i].allowchain[j].mask = rd_int (fd);
+ }
+
+ /* Back ends */
+ service[i].nbackend = rd_int (fd);
+ msg (" Configured back ends: %d", service[i].nbackend);
+ service[i].backend = xcalloc (service[i].nbackend *
+ sizeof(Backend));
+ for (j = 0; j < service[i].nbackend; j++) {
+ service[i].backend[j].name = rd_str (fd);
+ msg (" Backend %d: %s", j + i, service[i].backend[j].name);
+ service[i].backend[j].port = rd_int (fd);
+ service[i].backend[j].server = rd_str (fd);
+ service[i].backend[j].verbosity = rd_int (fd);
+ service[i].backend[j].weight = rd_int (fd);
+ service[i].backend[j].decay = rd_int (fd);
+ service[i].backend[j].onstart = rd_str (fd);
+ service[i].backend[j].onend = rd_str (fd);
+ service[i].backend[j].dumpfile = rd_str (fd);
+ service[i].backend[j].thruputfile = rd_str (fd);
+ service[i].backend[j].maxconnections = rd_uns (fd);
+ service[i].backend[j].stickycookie = rd_str (fd);
+ service[i].backend[j].retries = rd_int (fd);
+
+ service[i].backend[j].naddclientheader = rd_int (fd);
+ service[i].backend[j].addclientheader =
+ xcalloc (service[i].backend[j].naddclientheader *
+ sizeof(char*));
+ for (k = 0; k < service[i].backend[j].naddclientheader; k++)
+ service[i].backend[j].addclientheader[k] = rd_str (fd);
+
+ service[i].backend[j].nsetclientheader = rd_int (fd);
+ service[i].backend[j].setclientheader =
+ xcalloc (service[i].backend[j].nsetclientheader *
+ sizeof(char*));
+ for (k = 0; k < service[i].backend[j].nsetclientheader; k++)
+ service[i].backend[j].setclientheader[k] = rd_str (fd);
+
+ service[i].backend[j].nappendclientheader = rd_int (fd);
+ service[i].backend[j].appendclientheader =
+ xcalloc (service[i].backend[j].nappendclientheader *
+ sizeof(char*));
+ for (k = 0; k < service[i].backend[j].nappendclientheader; k++)
+ service[i].backend[j].appendclientheader[k] = rd_str (fd);
+
+ service[i].backend[j].naddserverheader = rd_int (fd);
+ service[i].backend[j].addserverheader =
+ xcalloc (service[i].backend[j].naddserverheader *
+ sizeof(char*));
+ for (k = 0; k < service[i].backend[j].naddserverheader; k++)
+ service[i].backend[j].addserverheader[k] = rd_str (fd);
+
+ service[i].backend[j].nsetserverheader = rd_int (fd);
+ service[i].backend[j].setserverheader =
+ xcalloc (service[i].backend[j].nsetserverheader *
+ sizeof(char*));
+ for (k = 0; k < service[i].backend[j].nsetserverheader; k++)
+ service[i].backend[j].setserverheader[k] = rd_str (fd);
+
+ service[i].backend[j].nappendserverheader = rd_int (fd);
+ service[i].backend[j].appendserverheader =
+ xcalloc (service[i].backend[j].nappendserverheader *
+ sizeof(char*));
+ for (k = 0; k < service[i].backend[j].nappendserverheader; k++)
+ service[i].backend[j].appendserverheader[k] = rd_str (fd);
+ }
+ }
+
+ msg ("Configuration scanned.");
+}
diff --git a/src/lib/configwrite.c b/src/lib/configwrite.c
@@ -0,0 +1,111 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.40, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
+#include "../crossroads.h"
+
+static void wr (int fd, void const *buf, unsigned len) {
+ int res;
+ int towrite = (int)len;
+
+ if ( (res = write (fd, buf, len)) != towrite )
+ error ("Failed to transmit %d bytes (%d)", towrite, res);
+}
+
+static void wr_int (int fd, int x) {
+ wr (fd, &x, sizeof(int));
+}
+
+static void wr_uns (int fd, unsigned x) {
+ wr (fd, &x, sizeof(unsigned));
+}
+
+static void wr_str (int fd, char const *s) {
+ if (!s || !*s)
+ wr_int (fd, 0);
+ else {
+ wr_int (fd, strlen(s));
+ wr (fd, s, strlen(s));
+ }
+}
+
+void config_write (int fd) {
+ int i, j, k;
+
+ msg ("Writing binary service definition to fd %d", fd);
+ wr_int (fd, nservice);
+ for (i = 0; i < nservice; i++) {
+ /* Basic stuff */
+ wr_str (fd, service[i].name);
+ wr_str (fd, service[i].bind);
+ wr_uns (fd, service[i].port);
+ wr_uns (fd, service[i].verbosity);
+ wr_int (fd, (int)service[i].dispatchtype);
+ wr_uns (fd, service[i].dispatchover);
+ wr_str (fd, service[i].dispatchext);
+ wr_uns (fd, service[i].rev_interval);
+ wr_uns (fd, service[i].backlog);
+ wr_uns (fd, service[i].shmkey);
+ wr_uns (fd, service[i].connectiontimeout);
+ wr_uns (fd, service[i].maxconnections);
+ wr_int (fd, (int)service[i].type);
+ wr_str (fd, service[i].allowfile);
+ wr_str (fd, service[i].denyfile);
+ wr_int (fd, service[i].uid);
+ wr_int (fd, service[i].gid);
+
+ /* Deny chain */
+ wr_int (fd, service[i].ndenychain);
+ for (j = 0; j < service[i].ndenychain; j++) {
+ wr_uns (fd, service[i].denychain[j].ip);
+ wr_uns (fd, service[i].denychain[j].mask);
+ }
+
+ /* Allow chain */
+ wr_int (fd, service[i].nallowchain);
+ for (j = 0; j < service[i].nallowchain; j++) {
+ wr_uns (fd, service[i].allowchain[j].ip);
+ wr_uns (fd, service[i].allowchain[j].mask);
+ }
+
+ /* Back ends */
+ wr_int (fd, service[i].nbackend);
+ for (j = 0; j < service[i].nbackend; j++) {
+ wr_str (fd, service[i].backend[j].name);
+ wr_int (fd, service[i].backend[j].port);
+ wr_str (fd, service[i].backend[j].server);
+ wr_int (fd, service[i].backend[j].verbosity);
+ wr_int (fd, service[i].backend[j].weight);
+ wr_int (fd, service[i].backend[j].decay);
+ wr_str (fd, service[i].backend[j].onstart);
+ wr_str (fd, service[i].backend[j].onend);
+ wr_str (fd, service[i].backend[j].dumpfile);
+ wr_str (fd, service[i].backend[j].thruputfile);
+ wr_uns (fd, service[i].backend[j].maxconnections);
+ wr_str (fd, service[i].backend[j].stickycookie);
+ wr_int (fd, service[i].backend[j].retries);
+
+ wr_int (fd, service[i].backend[j].naddclientheader);
+ for (k = 0; k < service[i].backend[j].naddclientheader; k++)
+ wr_str (fd, service[i].backend[j].addclientheader[k]);
+ wr_int (fd, service[i].backend[j].nsetclientheader);
+ for (k = 0; k < service[i].backend[j].nsetclientheader; k++)
+ wr_str (fd, service[i].backend[j].setclientheader[k]);
+ wr_int (fd, service[i].backend[j].nappendclientheader);
+ for (k = 0; k < service[i].backend[j].nappendclientheader; k++)
+ wr_str (fd, service[i].backend[j].appendclientheader[k]);
+
+ wr_int (fd, service[i].backend[j].naddserverheader);
+ for (k = 0; k < service[i].backend[j].naddserverheader; k++)
+ wr_str (fd, service[i].backend[j].addserverheader[k]);
+ wr_int (fd, service[i].backend[j].nsetserverheader);
+ for (k = 0; k < service[i].backend[j].nsetserverheader; k++)
+ wr_str (fd, service[i].backend[j].setserverheader[k]);
+ wr_int (fd, service[i].backend[j].nappendserverheader);
+ for (k = 0; k < service[i].backend[j].nappendserverheader; k++)
+ wr_str (fd, service[i].backend[j].appendserverheader[k]);
+ }
+ }
+}
diff --git a/src/lib/data.c b/src/lib/data.c
@@ -0,0 +1,35 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.40, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
+#define EXTERN
+#include "../crossroads.h"
+
+/* Global data for all Crossroads programs */
+
+int log_facility = LOG_DAEMON;
+int shmperm = 0644;
+
+StateStringMap statestringmap[] = {
+ { st_available, "available" },
+ { st_available, "up" },
+ { st_unavailable, "UNAVAILABLE" },
+ { st_down, "DOWN" },
+ { st_waking, "WAKING" },
+ { st_intermediate, "INTERMEDIATE" },
+ { st_unknown, "UNKNOWN" },
+ { st_unknown, 0 }
+};
+
+int relevant_sigs[] = { /* signals relevant to this app */
+ SIGHUP, /* either caught or ignored, */
+ SIGINT, /* depending on the stage */
+ SIGQUIT,
+ SIGABRT,
+ SIGKILL,
+ SIGPIPE,
+ SIGTERM,
+ 0,
+};
diff --git a/src/lib/xcalloc.c b/src/lib/xcalloc.c
@@ -0,0 +1,18 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.40, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
+#include "../crossroads.h"
+
+void *xcalloc (unsigned sz) {
+ void *ret;
+
+ if (!sz)
+ return (0);
+
+ ret = xmalloc (sz);
+ memset (ret, 0, sz);
+ return (ret);
+}
diff --git a/tools/c-conf b/tools/c-conf
@@ -4,7 +4,8 @@ use strict;
use Getopt::Std;
# Globals
-my $VER = "1.06";
+my $VER = "1.07";
+# 1.07 [KK 2007-05-18] Flag -c for caching implemented
# 1.06 [KK 2007-04-27] Added ifheader01 and libfunction01
# 1.05 [KK 2006-09-28] Flag -s (silent) implemented. Usage text updated.
# 1.04 [KK 2006-09-05] C-compilers: gcc/g++ get selected first, instead of
@@ -36,6 +37,8 @@ my @warnings;
my $printed;
my @headerdirs;
my @libdirs;
+my $cachekey;
+my $cacheval;
# Show usage and croak
sub usage {
@@ -70,6 +73,8 @@ Usage:
$base [flags] c++-compiler: Returns name of C++ compiler
Optional flags:
+ -c CACHE: settings will be dynamically determined unless present in
+ CACHE; new settings will be added to CACHE
-h: to show short help for an action, e.g. try '$base -h so-name'
-s: to suppress showing of warnings
-v: to show verbose messages
@@ -187,10 +192,16 @@ sub subfiles ($$$) {
}
}
-# Output stuff
+# Output stuff, update $cacheval incase we'll add it to the cache later.
sub output {
- print (' ') if ($printed++);
+ if ($printed++) {
+ print (' ');
+ $cacheval .= ' ';
+ }
print (@_);
+ for my $a (@_) {
+ $cacheval .= $a;
+ }
}
# Find a header, output a define if found.
@@ -506,7 +517,29 @@ sub test_libfunction {
$base = $0;
$base =~ s{.*/}{};
-usage () unless (getopts ('vhI:L:s', \%opts));
+usage () unless (getopts ('vhI:L:sc:', \%opts));
+
+# See if we got a cache with the requests in it.
+# If we can match the request, return the result.
+if ($opts{c}) {
+ $cachekey = '';
+ for my $a (@ARGV) {
+ $cachekey .= ' ' if ($cachekey ne '');
+ $cachekey .= $a;
+ }
+ if (open (my $if, $opts{c})) {
+ while (my $line = <$if>) {
+ chomp ($line);
+ my ($key, $val) = split (/\t/, $line);
+ if ($key eq $cachekey) {
+ output ($val);
+ print ("\n");
+ exit (0);
+ }
+ }
+ }
+}
+
foreach my $d (split (/,/, $opts{L})) {
push (@libdirs, $d);
}
@@ -547,6 +580,14 @@ if ($action eq 'header') {
}
print ("\n") if ($printed);
+
+# Add to the cache if necessary.
+if ($opts{c}) {
+ open (my $of, ">>$opts{c}")
+ or die ("Cannot append to cache file $opts{c}: $!\n");
+ print $of ("$cachekey\t$cacheval\n");
+}
+
if ($#warnings > -1) {
foreach my $w (@warnings) {
print STDERR ("$base WARNING: $w") unless ($opts{s});