Various mods for nntpcache 1.0.6.5

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




I am evaluating nntpcache for use as a front end to our main news server 
set here.  As part of this I have been hacking the code a bit, and decided 
it was a reasonable stage to contribute my changes back.

There are 2 patches attached to this mail.  Both are relative to 1.0.6.5, 
they apply in sequence (I doubt that 2 will work without 1, However, the 
changes in 2 are slightly more specific to what I want than generally 
applicable).

nntpcache-1.0.6.5-planet1.patch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 - hack to allow stats to be obtained by getting a magic article ID
   so "article <statistics@nntpcache>" will get the current stats
 - Complete removal of the ftell method of client byte counts (it
   not only failed under linux, it repositioned the read stream which
   broke clients that streamed commands - like Emacs/Gnus)
 - now does byte counts the hard way - in emit*().  This will cost
   some CPU
 - slight mod to remove %m from syslog messages which had no sys error.
 - minor setproctitle changes to make work under linux
 - fixes for SIGHUP brokenness (managed to listen to no ports at all
   after SIGHUP).
 - stats stuff is now emit-ed rather than fprint-ed

nntpcache-1.0.6.5-planet1.patch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 - parameterised Exit(), all calls previously existing now changed to 
Exit(1)
 - Normal (end of client) exit also calls Exit() (but with parameter 0)
 - Stats handling of articles (missed the way that they are specially
   written previously).
 - Adding of nnrpd like log messages to allow the same scripts to process
   them.
 - Adding of per group article count for above.
 - Adding of CPU time totalling for log messages, also put into stats
   structure and report.

The CPU time usage uses times() only.  It could be rehashed like the code 
in nnrpd to be more portable, but currently its hard coded.

I don't think I have broken any other portability stuff...

Hope this is useful.  So far it looks good on our testbed.  I am going to 
now run some serious tests on it and if those are as positive as I expect 
I am going to license and run it all over our site.

	Nigel.

Index: nntpcache/article.c
diff -u nntpcache/article.c:1.1.1.1 nntpcache/article.c:1.2
--- nntpcache/article.c:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/article.c	Wed Jul  9 13:51:15 1997
@@ -3,6 +3,8 @@
 #include "nntpcache.h"
 #include "mmap.h"
 
+#define STATS_MSGID "statistics@nntpcache"
+
 X (void rfc822lower (char *s))
 {
 	for (; *s != '@'; s++)
@@ -381,6 +383,31 @@
 	return TRUE;
 }
 
+
+/* This routine just shoves out a dummy art with the stats in it */
+
+static bool dummyStatsArticle(struct command *cmd)
+{
+	enum cmds type=cmd->val;
+
+/*	if (!PutStats(Stats))
+ *		loge (("couldn't put statistics via IPC"));
+ */
+	emit_follows(type, 0, STATS_MSGID, 1);
+	if (type==c_head || type == c_article) {
+		emit("Newsgroups: nntpcache.statistics\r\n");
+		emitf("Message-ID: <%s>\r\n", STATS_MSGID);
+		emitf("Date: %s\r\n", NewsDate(time(NULL)));	
+		emit("Subject: NNTPCACHE Statistics\r\n");
+		emit("From: NNTPCACHE Statistics <nntpcache>\r\n");
+	}
+	if (type==c_body || type == c_article) {
+		PrintStats();
+	}
+	emit(".\r\n");
+	return TRUE;
+}
+
 /*
  * handle article, head, body and stat commands
  * this routine is insane. most of the insanity is
@@ -481,6 +508,8 @@
 					Stats->msgid_cache++;
 				}
 			}
+			else if (!strcmp(msgid, STATS_MSGID))
+				return(dummyStatsArticle(cmd));
 		} else /* ahh, the simple alternative (!getbyid) */
 		{
 			if (type == c_head || type == c_stat)
Index: nntpcache/ipc.c
diff -u nntpcache/ipc.c:1.1.1.1 nntpcache/ipc.c:1.2
--- nntpcache/ipc.c:1.1.1.1	Thu Jun 19 12:23:57 1997
+++ nntpcache/ipc.c	Wed Jul  9 13:51:15 1997
@@ -103,22 +103,11 @@
 	return TRUE;
 }
 
-X (void updateFtell ())
-{
-	int t;
-	t = ftell (clientin);
-	if (t > 0)
-		Stats->client_read_bytes = t;
-	t = ftell (clientout);
-	if (t > 0)
-		Stats->client_write_bytes = t;
-}
 
 X (bool PutStats (struct stats *s))
 {
 	s->type = IPC_STATS;
 	s->client_elapsed = time (NULL) - s->time_client_started;
-	updateFtell ();
 	if (write (Daddy, (char *) s, sizeof *s) != sizeof *s)
 		return FALSE;
 	return TRUE;
Index: nntpcache/log.h
diff -u nntpcache/log.h:1.1.1.1 nntpcache/log.h:1.2
--- nntpcache/log.h:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/log.h	Wed Jul  9 13:51:15 1997
@@ -35,7 +35,7 @@
 	{\
 		char _buf[4096]; /* security - don't let this overflow */ \
 		logPrintP = _buf; \
-		syslog(LOG_ERR, "%s:%d:%m: %s", __FILE__, __LINE__, logPrint x);\
+		syslog(LOG_ERR, (errno ? "%s:%d:%m: %s" : "%s:%d: %s"), __FILE__, __LINE__, logPrint x);\
 	}\
 } while(0)
 
@@ -46,7 +46,7 @@
 	{\
 		char _buf[4096]; /* security - don't let this overflow */ \
 		logPrintP = _buf; \
-		syslog(LOG_WARNING, "%s:%d:%m: %s", __FILE__, __LINE__, logPrint x);\
+		syslog(LOG_WARNING, (errno ? "%s:%d:%m: %s" : "%s:%d: %s"), __FILE__, __LINE__, logPrint x);\
 	}\
 } while(0)
 
Index: nntpcache/nntpcache.c
diff -u nntpcache/nntpcache.c:1.1.1.1 nntpcache/nntpcache.c:1.4
--- nntpcache/nntpcache.c:1.1.1.1	Thu Jun 19 12:23:57 1997
+++ nntpcache/nntpcache.c	Wed Jul  9 13:51:16 1997
@@ -85,8 +85,10 @@
 {
 	signal (SIGTERM, sigterm);
 	signal (SIGINT, sigterm);
-	if (!Daemon || InDaemon)
+	if (!Daemon || InDaemon) {
+		errno = 0;
 		logw (("caught SIGTERM or SIGINT..syncing database...syncing disks...exiting"));
+	}
 	Exit ();
 }
 
@@ -605,6 +607,7 @@
 	struct hostent *hp;
 	bool f_swap_child = FALSE;
 	struct authent *gauth = NULL; /* stop gcc complaining */
+	int daemon_port = -1;	/* keep -Wall quiet */
 #ifdef MMALLOC
 	/* init_mmalloc(NULL); */
 #endif
@@ -620,8 +623,6 @@
 \n\
 	-nntpcache development team\n");
 
-	initsetproctitle(argc, argv, envp);
-	setproctitle("initialising");
 	while ((c = getopt (argc, argv, "ehinb:rc:s")) != -1)
 		switch (c)
 		{
@@ -657,6 +658,8 @@
 		default:
 			usage (argv[0]);
 		}
+	initsetproctitle(argc, argv, envp);
+	setproctitle("initialising");
 	umask (022);
 	if (!sig_hup && Daemon && !nodetach)
 	{
@@ -794,7 +797,6 @@
 		char *io_buf;
 #endif
 		struct sockaddr_in *in;
-		int daemon_port = -1; /* keep -Wall quiet */
 		int high_fd = -1; /* keep -Wall quiet */
 		fd_set r_set, rt_set, et_set;
 		FD_ZERO (&r_set);
@@ -841,7 +843,11 @@
 				fclose (fh);
 			}
 		}
-		else FD_SET (daemon_port, &r_set);
+		else 
+		{
+			FD_SET (daemon_port, &r_set);
+			high_fd = daemon_port;
+		}
 		open_mmap();
 		set_rebuild_time();
 		sig_hup = FALSE;
@@ -878,7 +884,7 @@
 				}
 				if (sig_hup)
 				{
-					errno = s_errno;
+					errno = 0;
 					logw (("caught SIGHUP, restarting..."));
 					goto reload;
 				}
@@ -901,6 +907,7 @@
 				}
 				if (sig_hup)
 				{
+					errno = 0;
 					logw (("caught SIGHUP, restarting..."));
 					goto reload;
 				}
@@ -1049,7 +1056,8 @@
 			Exit ();
 		}
 		if (con.statistics)
-			updateFtell ();
+			Stats->client_read_bytes += strlen(buf);
+
 		sscanf(buf, "%31[^\r\n\t ]%*[\r\n\t ]%127[^\r\n]", a0, a1);
 		if (!*a0)
 			continue;
Index: nntpcache/sockets.c
diff -u nntpcache/sockets.c:1.1.1.1 nntpcache/sockets.c:1.2
--- nntpcache/sockets.c:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/sockets.c	Wed Jul  9 13:51:16 1997
@@ -405,6 +405,8 @@
 X (int emit (char *s))
 {
 	logToClient (s);
+	if (con.statistics)
+		Stats->client_write_bytes += strlen(s);
 	return fputs (s, clientout)!=EOF;
 }
 
@@ -412,6 +414,8 @@
 {
 	fputs (s, clientout);
 	logToClient (s);
+	if (con.statistics)
+		Stats->client_write_bytes += strlen(s) + 2;
 	return fputs ("\r\n", clientout);
 }
 
@@ -428,6 +432,8 @@
 	}
 	i=vfprintf(clientout, fmt, ap);
 	va_end(ap);
+	if (con.statistics)
+		Stats->client_write_bytes += i;
 	return i;
 }
 
@@ -448,6 +454,7 @@
 	}
 	i=vfprintf(scfg->fh, fmt, ap);
 	va_end(ap);
+	Stats->server_write_bytes += i;
 	return i;
 }
 
@@ -467,6 +474,7 @@
 	}
 	i=vfprintf(scfg->fh, fmt, ap);
 	va_end(ap);
+	Stats->server_write_bytes += i;
 	return i;
 }
 
@@ -491,6 +499,7 @@
 	if (!cf)
 		return 0;
 	logToServer (cf->host, s);
+	Stats->server_write_bytes += strlen(s);
 	return fputs(s, cf->fh)!=EOF;
 }
 
@@ -498,6 +507,7 @@
 {
 	if (Cfemit(cf, s)==0)
 		return 0;
+	Stats->server_write_bytes += strlen(s) + 2;
 	return Cfemit(cf, "\r\n");
 }
 
Index: nntpcache/stats.c
diff -u nntpcache/stats.c:1.1.1.1 nntpcache/stats.c:1.2
--- nntpcache/stats.c:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/stats.c	Wed Jul  9 13:51:16 1997
@@ -10,68 +10,68 @@
 	time_t ti=time(NULL);
 	int dif=ti-s->time_statistics_started;
 	/* keep initial \r\n -- used for head/body sep */
-	fprintf(clientout, "\r\nNNTPCACHE %s statistics on %s, %s\r\n", VERSION, Host, con.bindAddr);
-	fprintf(clientout, "\r\nOVERALL:\r\n\r\n");
-	fprintf(clientout, "\tStatistics gathering commenced:         %24s\r\n", NewsDate(s->time_statistics_started));
-	fprintf(clientout, "\tCurrent nntpcached started:             %24s\r\n", NewsDate(s->time_server_started));
-	fprintf(clientout, "\tServers run during statistical period:  %24d\r\n", s->servers_run);
-	fprintf(clientout, "\tClients active:                         %24d\r\n", s->clients_active);
-	fprintf(clientout, "\tClient connects:                        %24d\r\n", s->client_connects);
-	fprintf(clientout, "\tOuting server connections:              %24d\r\n", s->server_connects);
-	fprintf(clientout, "\tOuting server connections (failed):     %24d\r\n", s->server_connects_failed);
-	fprintf(clientout, "\tTotal data from remote servers (bytes): %24s\r\n", c(s->server_read_bytes));
-	fprintf(clientout, "\tTotal data to remote servers (bytes):   %24s\r\n", c(s->server_write_bytes));
-	fprintf(clientout, "\tTotal data from clients (bytes):        %24s\r\n", c(s->client_read_bytes));
-	fprintf(clientout, "\tTotal data to clients (bytes):          %24s\r\n", c(s->client_write_bytes));
-	fprintf(clientout, "\tTotal cache cache efficiency:           %23.2f%%\r\n", 1.0-((float)(s->server_read_bytes+s->server_write_bytes+1)/(float)(s->client_write_bytes+s->client_read_bytes+1)));
-	fprintf(clientout, "\tIPC messages to children (bytes):       %24s\r\n", c(s->ipc_messages_out_bytes));
-	fprintf(clientout, "\tIPC messages to children:               %24d\r\n", s->ipc_messages_out);
-	fprintf(clientout, "\tIPC messages to children (bytes):       %24s\r\n", c(s->ipc_messages_out_bytes));
-	fprintf(clientout, "\tIPC messages from children:             %24d\r\n", s->ipc_messages_in);
-	fprintf(clientout, "\tIPC messages from children (bytes):     %24s\r\n", c(s->ipc_messages_in_bytes));
-	fprintf(clientout, "\tTotal time elapsed in children:         %24s\r\n", nnitod(s->client_elapsed));
-	fprintf(clientout, "\tDatabase stores:                        %24d\r\n", s->dbz_stores);
-	fprintf(clientout, "\tDatabase fetches:                       %24d\r\n", s->dbz_fetches);
-	fprintf(clientout, "\tDatabase size:                          %24s\r\n", c(s->dbz_len));
-	fprintf(clientout, "\t\"group\" commands:                       %24d\r\n", s->group);
-	fprintf(clientout, "\t\"listgroup\" commands:                   %24d\r\n", s->listgroup);
-	fprintf(clientout, "\tGroups cached with > 0 articles:        %24d\r\n", s->groups_cached);
-	fprintf(clientout, "\tGroups expired:                         %24d\r\n", s->expire_groups);
-	fprintf(clientout, "\tArticles expired:                       %24d\r\n", s->expire_articles);
-	fprintf(clientout, "\tPosts:                                  %24d\r\n", s->posts);
-	fprintf(clientout, "\tPosts (crossposted):                    %24d\r\n", s->posts_cross);
-	fprintf(clientout, "\tPosts (failed):                         %24d\r\n", s->posts_failed);
-	fprintf(clientout, "\tPosts (bytes):                          %24s\r\n", c(s->posts_bytes));
-	fprintf(clientout, "\tIncoming crossposts:                    %24d\r\n", s->crossposts);
-	fprintf(clientout, "\tIncoming crossposts (bytes):            %24s\r\n", c(s->crossposts_bytes));
-fprintf(clientout, "\
+	emitf("\r\nNNTPCACHE %s statistics on %s, %s\r\n", VERSION, Host, con.bindAddr);
+	emitf("\r\nOVERALL:\r\n\r\n");
+	emitf("\tStatistics gathering commenced:         %24s\r\n", NewsDate(s->time_statistics_started));
+	emitf("\tCurrent nntpcached started:             %24s\r\n", NewsDate(s->time_server_started));
+	emitf("\tServers run during statistical period:  %24d\r\n", s->servers_run);
+	emitf("\tClients active:                         %24d\r\n", s->clients_active);
+	emitf("\tClient connects:                        %24d\r\n", s->client_connects);
+	emitf("\tOuting server connections:              %24d\r\n", s->server_connects);
+	emitf("\tOuting server connections (failed):     %24d\r\n", s->server_connects_failed);
+	emitf("\tTotal data from remote servers (bytes): %24s\r\n", c(s->server_read_bytes));
+	emitf("\tTotal data to remote servers (bytes):   %24s\r\n", c(s->server_write_bytes));
+	emitf("\tTotal data from clients (bytes):        %24s\r\n", c(s->client_read_bytes));
+	emitf("\tTotal data to clients (bytes):          %24s\r\n", c(s->client_write_bytes));
+	emitf("\tTotal cache cache efficiency:           %23.2f%%\r\n", 1.0-((float)(s->server_read_bytes+s->server_write_bytes+1)/(float)(s->client_write_bytes+s->client_read_bytes+1)));
+	emitf("\tIPC messages to children (bytes):       %24s\r\n", c(s->ipc_messages_out_bytes));
+	emitf("\tIPC messages to children:               %24d\r\n", s->ipc_messages_out);
+	emitf("\tIPC messages to children (bytes):       %24s\r\n", c(s->ipc_messages_out_bytes));
+	emitf("\tIPC messages from children:             %24d\r\n", s->ipc_messages_in);
+	emitf("\tIPC messages from children (bytes):     %24s\r\n", c(s->ipc_messages_in_bytes));
+	emitf("\tTotal time elapsed in children:         %24s\r\n", nnitod(s->client_elapsed));
+	emitf("\tDatabase stores:                        %24d\r\n", s->dbz_stores);
+	emitf("\tDatabase fetches:                       %24d\r\n", s->dbz_fetches);
+	emitf("\tDatabase size:                          %24s\r\n", c(s->dbz_len));
+	emitf("\t\"group\" commands:                       %24d\r\n", s->group);
+	emitf("\t\"listgroup\" commands:                   %24d\r\n", s->listgroup);
+	emitf("\tGroups cached with > 0 articles:        %24d\r\n", s->groups_cached);
+	emitf("\tGroups expired:                         %24d\r\n", s->expire_groups);
+	emitf("\tArticles expired:                       %24d\r\n", s->expire_articles);
+	emitf("\tPosts:                                  %24d\r\n", s->posts);
+	emitf("\tPosts (crossposted):                    %24d\r\n", s->posts_cross);
+	emitf("\tPosts (failed):                         %24d\r\n", s->posts_failed);
+	emitf("\tPosts (bytes):                          %24s\r\n", c(s->posts_bytes));
+	emitf("\tIncoming crossposts:                    %24d\r\n", s->crossposts);
+	emitf("\tIncoming crossposts (bytes):            %24s\r\n", c(s->crossposts_bytes));
+emitf("\
 \r\n\
 LISTS:\r\n\
 \r\n\
 List-name---- --out-of-cache -----into-cache hits --xfer-per-hour lines -length\r\n");
-fprintf(clientout, "\
+emitf("\
 active       %7d %7s %7d %7s %3.0f%% %7.2f %7s %5d %7s\r\n", s->list_active_cache, c(s->list_active_cache_bytes), s->list_active_cached, c(s->list_active_cached_bytes),  100.0*(s->list_active_cache_bytes+1)/(float)(s->list_active_cache_bytes+s->list_active_cached_bytes+1), (s->list_active_cache+s->list_active_cached)/(dif/3600.0), c(((s->list_active_cache_bytes+s->list_active_cached_bytes)*3600.0)/dif), s->active_entries, c(s->active_len));
-fprintf(clientout, "\
+emitf("\
 active.times %7d %7s %7d %7s %3.0f%% %7.2f %7s %5d %7s\r\n", s->list_active_times_cache, c(s->list_active_times_cache_bytes), s->list_active_times_cached, c(s->list_active_times_cached_bytes),  100*(s->list_active_times_cache_bytes+1)/(float)(s->list_active_times_cache_bytes+s->list_active_times_cached_bytes+1), (s->list_active_times_cache+s->list_active_times_cached)/(dif/3600.0), c((s->list_active_times_cache_bytes+s->list_active_times_cached_bytes)/(dif/3600.0)), s->active_times_entries, c(s->active_times_len));
-fprintf(clientout, "\
+emitf("\
 newsgroups   %7d %7s %7d %7s %3.0f%% %7.2f %7s %5d %7s\r\n", s->list_newsgroups_cache, c(s->list_newsgroups_cache_bytes), s->list_newsgroups_cached, c(s->list_newsgroups_cached_bytes),  100.0*(s->list_newsgroups_cache_bytes+1)/(float)(s->list_newsgroups_cache_bytes+s->list_newsgroups_cached_bytes+1), (s->list_newsgroups_cache+s->list_newsgroups_cached)/(dif/3600.0), c((s->list_newsgroups_cache_bytes+s->list_newsgroups_cached_bytes)/(dif/3600.0)), s->newsgroups_entries, c(s->newsgroups_len));
-fprintf(clientout, "\
-overview_fmt   %7d %7s %7d %7s %3.0f%% %7.2f %7s %5d %7s\r\n", s->list_overview_fmt_cache, c(s->list_overview_fmt_cache_bytes), s->list_overview_fmt_cached, c(s->list_overview_fmt_cached_bytes),  100.0*(s->list_overview_fmt_cache_bytes+1)/(float)(s->list_overview_fmt_cache_bytes+s->list_overview_fmt_cached_bytes+1), (s->list_overview_fmt_cache+s->list_overview_fmt_cached)/(dif/3600.0), c((s->list_overview_fmt_cache_bytes+s->list_overview_fmt_cached_bytes)/(dif/3600.0)), s->overview_fmt_entries, c(s->overview_fmt_len));
-fprintf(clientout, "\r\n\
+emitf("\
+overview_fmt %7d %7s %7d %7s %3.0f%% %7.2f %7s %5d %7s\r\n", s->list_overview_fmt_cache, c(s->list_overview_fmt_cache_bytes), s->list_overview_fmt_cached, c(s->list_overview_fmt_cached_bytes),  100.0*(s->list_overview_fmt_cache_bytes+1)/(float)(s->list_overview_fmt_cache_bytes+s->list_overview_fmt_cached_bytes+1), (s->list_overview_fmt_cache+s->list_overview_fmt_cached)/(dif/3600.0), c((s->list_overview_fmt_cache_bytes+s->list_overview_fmt_cached_bytes)/(dif/3600.0)), s->overview_fmt_entries, c(s->overview_fmt_len));
+emitf("\r\n\
 MESSAGES:\r\n\
 \r\n\
 Data-type---- --out-of-cache -----into-cache hits ----------proxy xfer-per-hour\r\n");
-fprintf(clientout, "\
+emitf("\
 article      %7d %7s %7d %7s %3.0f%% %7d %7s %5.0f %7s\r\n", s->article_cache, c(s->article_cache_bytes), s->article_cached, c(s->article_cached_bytes),  100.0*(s->article_cache_bytes+1)/(float)(s->article_cache_bytes+s->article_cached_bytes+1), s->article_proxy, c(s->article_proxy_bytes), (s->article_cache+s->article_cached)/(dif/3600.0), c(((s->article_cache_bytes+s->article_cached_bytes)*3600.0)/dif));
-fprintf(clientout, "\
+emitf("\
 head         %7d %7s %7d %7s %3.0f%% %7d %7s %5.0f %7s\r\n", s->head_cache, c(s->head_cache_bytes), s->head_cached, c(s->head_cached_bytes),  100.0*(s->head_cache_bytes+1)/(float)(s->head_cache_bytes+s->head_cached_bytes+1), s->head_proxy, c(s->head_proxy_bytes), ((s->head_cache+s->head_cached)*3600.0)/dif, c(((s->head_cache_bytes+s->head_cached_bytes)*3600.0)/dif));
-fprintf(clientout, "\
+emitf("\
 body         %7d %7s %7d %7s %3.0f%% %7d %7s %5.0f %7s\r\n", s->body_cache, c(s->body_cache_bytes), s->body_cached, c(s->body_cached_bytes),  100.0*(s->body_cache_bytes+1)/(float)(s->body_cache_bytes+s->body_cached_bytes+1), s->body_proxy, c(s->body_proxy_bytes), ((s->body_cache+s->body_cached)*3600.0)/dif, c(((s->body_cache_bytes+s->body_cached_bytes)*3600)/dif));
-fprintf(clientout, "\
+emitf("\
 xover        %7d %7s %7d %7s %3.0f%% %7d %7s %5.0f %7s\r\n", s->xover_cache, c(s->xover_cache_bytes), s->xover_cached, c(s->xover_cached_bytes),  100.0*(s->xover_cache_bytes+1)/(float)(s->xover_cache_bytes+s->xover_cached_bytes+1), s->xover_proxy, c(s->xover_proxy_bytes), ((s->xover_cache+s->xover_cached+s->xover_proxy)*3600.0)/dif, c(((s->xover_cache_bytes+s->xover_cached_bytes+s->xover_proxy_bytes)*3600.0)/dif));
-fprintf(clientout, "\
-xhdr        %7d %7s %7d %7s %3.0f%% %7d %7s %5.0f %7s\r\n", s->xhdr_cache, c(s->xhdr_cache_bytes), s->xhdr_cached, c(s->xhdr_cached_bytes),  100.0*(s->xhdr_cache_bytes+1)/(float)(s->xhdr_cache_bytes+s->xhdr_cached_bytes+1), s->xhdr_proxy, c(s->xhdr_proxy_bytes), ((s->xhdr_cache+s->xhdr_cached+s->xhdr_proxy)*3600.0)/dif, c(((s->xhdr_cache_bytes+s->xhdr_cached_bytes+s->xhdr_proxy_bytes)*3600.0)/dif));
-fprintf(clientout, "\
+emitf("\
+xhdr         %7d %7s %7d %7s %3.0f%% %7d %7s %5.0f %7s\r\n", s->xhdr_cache, c(s->xhdr_cache_bytes), s->xhdr_cached, c(s->xhdr_cached_bytes),  100.0*(s->xhdr_cache_bytes+1)/(float)(s->xhdr_cache_bytes+s->xhdr_cached_bytes+1), s->xhdr_proxy, c(s->xhdr_proxy_bytes), ((s->xhdr_cache+s->xhdr_cached+s->xhdr_proxy)*3600.0)/dif, c(((s->xhdr_cache_bytes+s->xhdr_cached_bytes+s->xhdr_proxy_bytes)*3600.0)/dif));
+emitf("\
 msgid        %7d %7s %7d %7s %3.0f%%                 %5.0f %7s\r\n", s->msgid_cache, c(s->msgid_cache_bytes), s->msgid_cached, c(s->msgid_cached_bytes),  100.0*(s->msgid_cache_bytes+1)/(float)(s->msgid_cache_bytes+s->msgid_cached_bytes+1), ((s->msgid_cache+s->msgid_cached)*3600.0)/dif, c(((s->msgid_cache_bytes+s->msgid_cached_bytes)*3600.0)/dif));
 	convFree();
 	return;
@@ -80,6 +80,7 @@
 X(bool PrintStats())
 {
 	struct stats stats, *s=&stats;
+
 	if (!GetStats(s, ""))
 		loge (("couldn't get statistics via IPC"));
 	print_stats(s);
Index: nntpcache/acc.c
diff -u nntpcache/acc.c:1.1.1.1 nntpcache/acc.c:1.2
--- nntpcache/acc.c:1.1.1.1	Thu Jun 19 12:23:57 1997
+++ nntpcache/acc.c	Thu Jul 10 11:23:08 1997
@@ -175,7 +175,7 @@
 				if (!p)
 				{
 					loge (("bad filter header/scope %s in file %s: %s", scope, fi, buf));
-					Exit();
+					Exit(1);
 				}
 				*p='\0';
 				f->scope = sc_header;
@@ -188,7 +188,7 @@
 			if (sscanf(weight, "%d", &f->weight)!=1)
 			{
 				loge (("bad filter weight in %s line %d: '%s'", fi, n, buf));
-				Exit();
+				Exit(1);
 			}
 			for (opt = strtok(options, ","); opt; opt = strtok(NULL, ","))
 			{
@@ -200,7 +200,7 @@
 				else
 				{
 					loge (("bad filter option in %s line %d: '%s'", fi, n, buf));
-					Exit();
+					Exit(1);
 				}
 			}
 			strStripEOL(pat);
@@ -222,7 +222,7 @@
 				char errbuf[MAX_LINE];
 				regerror(err_code, &f->preg, errbuf, sizeof errbuf);
 				loge (("bad regular expression in %s line %d: %s", fi, n, errbuf));
-				Exit();
+				Exit(1);
 			}
 #endif
 			if (feof(fp) || ferror(fp))
@@ -231,7 +231,7 @@
 		if (!fc->filter)
 		{
 			loge (("filter file %s contained no filters!", fi));
-			Exit();
+			Exit(1);
 		}
 		if (!filter_list_index)
 		{
Index: nntpcache/article.c
diff -u nntpcache/article.c:1.2 nntpcache/article.c:1.3
--- nntpcache/article.c:1.2	Wed Jul  9 13:51:15 1997
+++ nntpcache/article.c	Thu Jul 10 11:23:08 1997
@@ -306,7 +306,7 @@
 		break;
 	default:
 		loge(("strange flow"));
-		Exit();
+		Exit(1);
 	}
 	if (getbyid) /* dumb, but that is how inn does it */
 		emitf ("%s %d %s <%s>\r\n", m, artno, c, msgid);
@@ -701,8 +701,16 @@
 		if (!f_filt)
 		{
 			emit_follows(type, artno, msgid, getbyid); /* 221 52 <839359140.253541@suburbia.net> head etc */
+			Stats->article_this_group++;
 			if (type==c_head || type == c_article)
+			{
+				if (type==c_head)
+				{
+					Stats->head_proxy++;
+					Stats->head_proxy_bytes += art_stack->used - 1;
+				}
 				emit(art_stack->data);	/* emit the head */
+			}
 		}
 		if (type == c_body || type == c_article)
 		{
@@ -736,8 +744,19 @@
 				return FALSE;
 			}
 			emit_follows(type, artno, msgid, getbyid);
+			Stats->article_this_group++;
 			if (type != c_stat)
+			{
+				if (type==c_article)
+				{
+					Stats->article_proxy++;
+					Stats->article_proxy_bytes += art_stack->used - 1;
+				} else {
+					Stats->body_proxy++;
+					Stats->body_proxy_bytes += art_stack->used - head_len - 1;
+				}
 				emit ((type == c_body && !CurrentScfg->article_timeout)? art_stack->data+head_len+2: art_stack->data);
+			}
 		}
 		if (type != c_stat)
 			emitrn (".");
@@ -836,27 +855,42 @@
 		emit_follows(type, artno, msgid, getbyid);
 		switch (type)
 		{
+			int wsize;
+
 		case c_article:
 			fflush(clientout); /* keep fd and fh in-sync */
-			write (fileno(clientout), buf, len); /* avoid buffering, push it out in one hit */
+			/* avoid buffering, push it out in one hit */
+			wsize = write (fileno(clientout), buf, len);
+			Stats->client_write_bytes += wsize;
+			Stats->article_proxy_bytes += wsize;
+			Stats->article_proxy++;
 			break;
 		case c_head:
-			fwrite (buf, body? body-2-buf: len, 1, clientout); /* head's tend to be small */
+			/* head's tend to be small */
+			wsize = fwrite (buf, body? body-2-buf: len, 1, clientout);
+			Stats->client_write_bytes += wsize;
+			Stats->head_proxy_bytes += wsize;
+			Stats->head_proxy++;
 			break;
 		case c_body:
 			/* bodies can be large or small */
 			if (buf+len-body > 1024)
 			{
 				fflush(clientout); /* keep fd and fh in-sync */
-				write (fileno(clientout), body, buf+len-body); /* avoid buffering, push it out in one hit */
+				/* avoid buffering, push it out in one hit */
+				wsize = write (fileno(clientout), body, buf+len-body);
 			} else
-				fwrite (body, buf+len-body, 1, clientout);
+				wsize = fwrite (body, buf+len-body, 1, clientout);
+			Stats->client_write_bytes += wsize;
+			Stats->body_proxy_bytes += wsize;
+			Stats->body_proxy++;
 			break;
 		case c_stat:
 			break;
 		default:
 			break;
 		}
+		Stats->article_this_group;
 		if (munmap(buf, len)!=0)
 			loge (("couldn't munmap %s (%d length)", artfile, len));
 		if (type != c_stat)
Index: nntpcache/group.c
diff -u nntpcache/group.c:1.1.1.1 nntpcache/group.c:1.2
--- nntpcache/group.c:1.1.1.1	Thu Jun 19 12:23:57 1997
+++ nntpcache/group.c	Thu Jul 10 11:23:08 1997
@@ -239,6 +239,11 @@
 		return FALSE;
 	}
 	Stats->group++;
+	if (Stats->article_this_group && CurrentGroupScfg->group)
+		log (("%s group %s %d", ClientHostNormal, 
+		      CurrentGroupScfg->group, Stats->article_this_group));
+	Stats->article_this_group = 0;
+
 	if (!safeGroup(attempted_group))
 	{
 		emitrn (NNTP_NOSUCHGROUP);
Index: nntpcache/history.c
diff -u nntpcache/history.c:1.1.1.1 nntpcache/history.c:1.2
--- nntpcache/history.c:1.1.1.1	Thu Jun 19 12:23:57 1997
+++ nntpcache/history.c	Thu Jul 10 11:23:09 1997
@@ -272,7 +272,7 @@
 	if (chdir(con.cacheDir)<0 || !hisOpen () || stat (con.historyFile, &st) < 0)
 	{
 		loge (("fatal error: couldn't open/stat %s", con.historyFile));
-		Exit ();
+		Exit (1);
 	}
 	if (st.st_size < con.hisHighWater)
 		return TRUE;
@@ -288,7 +288,7 @@
 	if (msg)
 	{
 		loge (("couldn't prune history (%s)", msg));
-		Exit ();	/* seriously suffering */
+		Exit (1);	/* seriously suffering */
 	}
 	return TRUE;		/* hisPruneDbz leaves con.historyFile open */
 }
Index: nntpcache/ipc.c
diff -u nntpcache/ipc.c:1.2 nntpcache/ipc.c:1.3
--- nntpcache/ipc.c:1.2	Wed Jul  9 13:51:15 1997
+++ nntpcache/ipc.c	Thu Jul 10 11:23:09 1997
@@ -24,8 +24,10 @@
 			unlink (f);
 			return FALSE;
 		} else
-			*Stats = st;
+			memcpy(Stats, &st, sizeof st);
 	}
+	else
+		return FALSE;
 	close (fd);
 	return TRUE;
 }
@@ -211,7 +213,7 @@
 #if 0
 			if (*serv)
 			{
-				Exit ();
+				Exit (1);
 				/* find server stats TODO */
 			} else
 #endif
@@ -225,6 +227,7 @@
 	case IPC_STATS:
 		{
 			int *i, *o;
+			unsigned long *ii, *oo;
 			struct stats *p = (struct stats *) buf;
 			if (cc < sizeof *p)
 			{
@@ -234,7 +237,9 @@
 			for (o = &Stats->active_len, i = &p->active_len; i <= (int *) &p->newsgroups_entries; o++, i++)
 				if (*i)
 					*o = *i;
-			for (o = &Stats->server_connects, i = &p->server_connects; i < (int *) &p->servername; *o++ += *i++) ;	/* add */
+			for (o = &Stats->server_connects, i = &p->server_connects; i < (int *) &p->client_user_cpu; *o++ += *i++) ;	/* add */
+			for (oo = &Stats->client_user_cpu, ii = &p->client_user_cpu;
+			     ii < (unsigned long *) &p->servername; *oo++ += *ii++) ;	/* add */
 			break;
 		}
 #ifdef GRRR
Index: nntpcache/ipc.h
diff -u nntpcache/ipc.h:1.1.1.1 nntpcache/ipc.h:1.2
--- nntpcache/ipc.h:1.1.1.1	Thu Jun 19 12:23:57 1997
+++ nntpcache/ipc.h	Thu Jul 10 11:23:09 1997
@@ -114,6 +114,7 @@
 	int article_cached_bytes;
 	int article_cache;
 	int article_cache_bytes;
+	int article_this_group;
 	int head_cache;
 	int head_cache_bytes;
 	int head_cached;
@@ -128,5 +129,7 @@
 	int body_proxy_bytes;
 	int body_msgid;
 	int body_artnum;
+	unsigned long client_user_cpu;
+	unsigned long client_syst_cpu;
 	char servername[1]; /* nul if totall */
 };
Index: nntpcache/nntpcache.c
diff -u nntpcache/nntpcache.c:1.4 nntpcache/nntpcache.c:1.5
--- nntpcache/nntpcache.c:1.4	Wed Jul  9 13:51:16 1997
+++ nntpcache/nntpcache.c	Thu Jul 10 11:23:09 1997
@@ -89,13 +89,13 @@
 		errno = 0;
 		logw (("caught SIGTERM or SIGINT..syncing database...syncing disks...exiting"));
 	}
-	Exit ();
+	Exit (1);
 }
 
 static RETSIGTYPE sigsegv (int sig)
 {
 	loge (("SIGSEGV!"));
-	Exit ();
+	Exit (1);
 }
 
 static RETSIGTYPE sigusr1 (int sig)
@@ -122,7 +122,7 @@
 {
 	emitf ("%d Timeout after %s, closing connection.\r\n", NNTP_TEMPERR_VAL, nnitod(con.idleTimeout));
 	log (("client idled out after %s", nnitod(con.idleTimeout)));
-	Exit ();
+	Exit (1);
 }
 
 static void check_child ()
@@ -153,8 +153,9 @@
 	signal (SIGCHLD, sigchld);
 }
 
-X (void Exit ())
+X (void Exit (int code))
 {
+	flush ();
 	if (!Daemon || InDaemon)
 	{
 		dbzsync ();
@@ -165,6 +166,23 @@
 			unlink (PidFile);
 			sync ();
 		}
+	} else {
+		struct tms buffer;
+
+	  	if (Stats->article_this_group && CurrentGroupScfg->group)
+			log (("%s group %s %d", ClientHostNormal, 
+			      CurrentGroupScfg->group, Stats->article_this_group));
+		log (("%s exit articles %d groups %d", ClientHostNormal, 
+		      (Stats->article_proxy + Stats->article_proxy + Stats->article_proxy),
+		      Stats->group));
+		times(&buffer);
+		Stats->client_user_cpu = buffer.tms_utime + buffer.tms_cutime;
+		Stats->client_syst_cpu = buffer.tms_stime + buffer.tms_cstime;
+		log (("%s times user %.2f system %.2f elapsed %d.00",
+		      ClientHostNormal,
+		      ((double) Stats->client_user_cpu/(double) HZ),
+		      ((double) Stats->client_syst_cpu/(double) HZ),
+		      (time(NULL) - Stats->time_client_started)));
 	}
 	if (Daddy >= 0)
 	{
@@ -176,14 +194,14 @@
 		mmalloc_detach (Mbase);
 #endif
 	closelog ();
-	exit (1);
+	exit (code);
 }
 
 static RETSIGTYPE sigpipe (int sig)
 {
 	signal (SIGPIPE, SIG_IGN);	/* syslog can cause a SIGPIPE ! */
 	logd (("client disconnected before QUIT ACK"));
-	Exit ();
+	Exit (1);
 }
 
 static bool load_config (char *file)
@@ -260,7 +278,7 @@
 	if (ferror(fp))
 	{
 		loge (("error reading groups config from %s", file));
-		Exit();
+		Exit(1);
 	}
 	if (!list)
 	{
@@ -382,7 +400,7 @@
 	if (ferror(fp))
 	{
 		loge (("error reading servers config from %s", file));
-		Exit();
+		Exit(1);
 	}
 	if (!list)
 	{
@@ -413,11 +431,11 @@
         if (chdir (con.chrootDir) != 0 || chroot (".") != 0)
 	{
 		loge (("unable to chroot(\"%s\")", con.chrootDir));
-		Exit();
+		Exit(1);
 	}
 #else
 	loge (("no chroot() call available on this system"));
-	Exit();
+	Exit(1);
 #endif
 }
 
@@ -428,7 +446,7 @@
 	if (!ret)
 	{
 		loge (("mmalloc in %s failed", con.mmapFile));
-		Exit();
+		Exit(1);
 	}
 	return ret;
 #else
@@ -443,7 +461,7 @@
 	if (!ret)
 	{
 		loge (("mcalloc in %s failed", con.mmapFile));
-		Exit();
+		Exit(1);
 	}
 	return ret;
 #else
@@ -466,7 +484,7 @@
 void bad_mmalloc()
 {
 	loge (("Memory corrupt"));
-	Exit();
+	Exit(1);
 }
 
 static void init_mmalloc(POINTER *m)
@@ -516,7 +534,7 @@
 			if (fd<0)
 			{
 				loge (("unable to open %s", con.mmapFile));
-				Exit();
+				Exit(1);
 			}
 		} else
 			f_cleanSlate = FALSE;
@@ -527,7 +545,7 @@
 		if (fd<0 || f_retried)
 		{
 			loge (("couldn't attach mmap() region. abort."));
-			Exit();
+			Exit(1);
 		}
 		loge (("mmap() attach to %s failed (probably corrupted...removing it and trying again)", con.mmapFile));
 		unlink(con.mmapFile);
@@ -545,7 +563,7 @@
 	if (!Ni)
 	{
 		loge (("invalid getkey/setkey in %s", con.mmapFile));
-		Exit();
+		Exit(1);
 	}
 #else
 	Ni=Scalloc(1, sizeof *Ni);
@@ -568,14 +586,14 @@
 	{
 		loge (("unable to set gid to %d", gid));
 #ifndef DEBUG
-		Exit ();
+		Exit (1);
 #endif
 	}
 	if (setuid (uid) == -1)
 	{
 		loge (("unable to set uid to %d", uid));
 #ifndef DEBUG
-		Exit ();
+		Exit (1);
 #endif
 	}
 }
@@ -707,7 +725,7 @@
 	if (chdir (con.configDir) == -1)
 	{
 		loge (("couldn't set cwd to %s", con.configDir));
-		Exit ();
+		Exit (1);
 	}
 	if (!load_config (config_file))
 		Exit (1);
@@ -744,7 +762,7 @@
 	if (chdir (con.configDir) == -1)
 	{
 		loge (("couldn't set cwd to %s", con.configDir));
-		Exit ();
+		Exit (1);
 	}
 	logd(("cwd now %s", con.configDir));
 	if (!load_servers (con.serversFile))
@@ -757,13 +775,13 @@
 		if (!(pw = getpwnam (con.user)))
 		{
 			loge (("configuration error: no such user '%s'", con.user));
-			Exit ();
+			Exit (1);
 		}
 		uid = pw->pw_uid;
 		if (!(gr = getgrnam (con.group)))
 		{
 			loge (("configuration error: no such group '%s'", con.group));
-			Exit ();
+			Exit (1);
 		}
 		gid = gr->gr_gid;
 		if (con.chroot)
@@ -778,7 +796,7 @@
 	if (chdir (con.cacheDir) == -1)
 	{
 		loge (("couldn't set cwd to %s", con.cacheDir));
-		Exit ();
+		Exit (1);
 	}
 	logd(("cwd now %s", con.cacheDir));
 	signal (SIGTERM, sigterm);
@@ -811,7 +829,7 @@
 			if (daemon_port == -1)
 			{
 				loge (("couldn't get socket()"));
-				Exit ();
+				Exit (1);
 			}
 			setsockopt (daemon_port, SOL_SOCKET, SO_REUSEADDR, 
 					(char*) &yes, sizeof yes);
@@ -820,7 +838,7 @@
 			if (bind (daemon_port, (struct sockaddr *) in, sizeof *in) == -1)
 			{
 				loge (("couldn't bind %s", con.bindAddr));
-				Exit ();
+				Exit (1);
 			}
 			drop_priv(uid, gid);
 			listen (daemon_port, 50);
@@ -1017,7 +1035,7 @@
 	{
 		emitf ("%d access denied <%s>, you do not have connect permissions in the %s file.\r\n", NNTP_ACCESS_VAL, ClientHost, con.accessFile);
 		log (("refused connect from %s (%s)", ClientHost, ClientHostAddr));
-		Exit ();
+		Exit (1);
 	}
 	if (con.contentFilters)
 	{
@@ -1035,7 +1053,7 @@
 	}
 	signal(SIGALRM, sigalrm);
 	alarm(con.idleTimeout);
-	log (("connect from %s (%s)", ClientHost, ClientHostAddr));
+	log (("%s connect from %s (%s)", ClientHostNormal, ClientHostRFC931, ClientHostAddr));
 	setproctitle("%s: starting", ClientHost);
 	emitf ("%d %s NNTPcache server V%s %s ready (posting %s).\r\n", gauth->post? NNTP_POSTOK_VAL: NNTP_NOPOSTOK_VAL, Host, VERSION, __DATE__, gauth->post? "ok": "not permitted");
 	flush ();
@@ -1053,7 +1071,7 @@
 		if (!Get (buf, sizeof buf))
 		{
 			logd (("client disconnected before QUIT"));
-			Exit ();
+			Exit (1);
 		}
 		if (con.statistics)
 			Stats->client_read_bytes += strlen(buf);
@@ -1196,20 +1214,6 @@
 	}
 	setproctitle("%s closing", ClientHost);
 end:
-	flush ();
-	if (!Daemon || InDaemon)
-	{
-		dbzsync ();
-		dbmclose ();
-		sync ();
-		if (InDaemon)
-			unlink (PidFile);
-	}
-	if (Daddy >= 0)
-	{
-		PutStats (Stats);
-		close (Daddy);
-	}
-	closelog ();
-	exit (0);		/* we let the kernel close any open fd's */
+	Exit(0);
+	exit(0);		/* Prevents complaints from compiler! */
 }
Index: nntpcache/post.c
diff -u nntpcache/post.c:1.1.1.1 nntpcache/post.c:1.2
--- nntpcache/post.c:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/post.c	Thu Jul 10 11:23:09 1997
@@ -43,7 +43,7 @@
 	{
 		int cc=Get(bfr, sizeof bfr);
 		if (cc<1)
-			Exit();
+			Exit(1);
 		bytes += cc;
 		if (!body)
 		{
Index: nntpcache/standard.h
diff -u nntpcache/standard.h:1.1.1.1 nntpcache/standard.h:1.2
--- nntpcache/standard.h:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/standard.h	Thu Jul 10 11:23:09 1997
@@ -19,6 +19,7 @@
 #include <string.h>
 #include <syslog.h>
 #include <ctype.h>
+#include <sys/times.h>
 #ifndef LOG_NEWS
 #  ifdef NEWS_NOTICE
 #    define LOG_NEWS NEWS_NOTICE
Index: nntpcache/stats.c
diff -u nntpcache/stats.c:1.2 nntpcache/stats.c:1.3
--- nntpcache/stats.c:1.2	Wed Jul  9 13:51:16 1997
+++ nntpcache/stats.c	Thu Jul 10 11:23:09 1997
@@ -29,6 +29,8 @@
 	emitf("\tIPC messages to children (bytes):       %24s\r\n", c(s->ipc_messages_out_bytes));
 	emitf("\tIPC messages from children:             %24d\r\n", s->ipc_messages_in);
 	emitf("\tIPC messages from children (bytes):     %24s\r\n", c(s->ipc_messages_in_bytes));
+	emitf("\tCPU Usage (User) in children:           %23.2fs\r\n", ((double) s->client_user_cpu/(double) HZ));
+	emitf("\tCPU Usage (System) in children:         %23.2fs\r\n", ((double) s->client_syst_cpu/(double) HZ));
 	emitf("\tTotal time elapsed in children:         %24s\r\n", nnitod(s->client_elapsed));
 	emitf("\tDatabase stores:                        %24d\r\n", s->dbz_stores);
 	emitf("\tDatabase fetches:                       %24d\r\n", s->dbz_fetches);
Index: nntpcache/libproff/utils.c
diff -u nntpcache/libproff/utils.c:1.1.1.1 nntpcache/libproff/utils.c:1.2
--- nntpcache/libproff/utils.c:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/libproff/utils.c	Thu Jul 10 11:23:09 1997
@@ -83,7 +83,7 @@
 	if (!ret)
 	{
 		loge (("mmalloc in %s failed", con.mmapFile));
-		Exit();
+		Exit(1);
 	}
 	return ret;
 }
@@ -104,7 +104,7 @@
 	if (!ret)
 	{
 		loge (("mcalloc in %s failed", con.mmapFile));
-		Exit();
+		Exit(1);
 	}
 	return ret;
 }
Index: nntpcache/mmalloc/README.osf
diff -u nntpcache/mmalloc/README.osf:1.1.1.1 nntpcache/mmalloc/README.osf:1.2
--- nntpcache/mmalloc/README.osf:1.1.1.1	Thu Jun 19 12:23:58 1997
+++ nntpcache/mmalloc/README.osf	Thu Jul 10 11:23:09 1997
@@ -85,7 +85,7 @@
 ! static void bad_mmalloc()
   {
   	loge (("Memory currupt"));
-  	Exit();
+  	Exit(1);
 --- 475,481 ----
   }
   
@@ -93,7 +93,7 @@
 ! void bad_mmalloc()
   {
   	loge (("Memory currupt"));
-  	Exit();
+  	Exit(1);
 ***************
 *** 610,616 ****
   	bool f_swap_child = FALSE;
[ Nigel.Metheringham@theplanet.net   -  Systems Software Engineer ]
[ Tel : +44 113 251 6012                   Fax : +44 113 224 0003 ]
[            Friends don't let friends use sendmail!              ]

[Index of Archives]     [Yosemite]     [Yosemite Campsites]     [Bugtraq]     [Linux]     [Trn]

Powered by Linux