/* Distributed Checksum Clearinghouse
 *
 * count work
 *
 * --C-LICENSE--
 * $Revision: 1.22 $
 */


#include "cmn_defs.h"
#include "helper.h"
#include <signal.h>


TOTALS totals;
static time_t signaled;
static pthread_t totals_tid;
static u_char stopping;

static void totals_msg(void);
static void totals_msg_signal(int);
static void *totals_msg_thread(void *);



static void
totals_msg_signal(int sig DCC_UNUSED)
{
	time_t now;

	signal(SIGUSR1, totals_msg_signal);

	/* ignore signals faster than 60 seconds */
	now = time(0);
	if (!signaled || now > signaled+60) {
		signaled = now;
		totals.msg_next = signaled;
	}
}



static void * DCC_NORET
totals_msg_thread(void *ign DCC_UNUSED)
{
	time_t now, next, last_clean;
	struct tm tm, signaled_tm;
	int secs;

	clnt_sigs_off(0);

	last_clean = time(0);
	for (;;) {
		if (stopping)
			pthread_exit(0);

		helper_lock();
		reap_helpers();
		helper_unlock();

		now = time(0);
		next = totals.msg_next;
		if (now >= next && next != 0) {
			totals_msg();
			next = 0;
		}

		if (!next) {
			/* make a note to announce totals at midnight
			 * or 24 hours after the signal */
			dcc_localtime(totals.msg_prev, &tm);
			if (signaled != 0) {
				dcc_localtime(signaled, &signaled_tm);
				tm.tm_hour = signaled_tm.tm_hour;
				tm.tm_min = signaled_tm.tm_min;
			} else {
				tm.tm_hour = 0;
				tm.tm_min = 0;
			}
			tm.tm_sec = 0;
			++tm.tm_mday;
			next = mktime(&tm);
			if (next == -1) {
				dcc_error_msg("mktime() failed");
				next = now + 24*60*60;
			}
			totals.msg_next = next;

		}

		/* tell dccifd or dccm to close old sockets to recover
		 * from dictionary attacks */
		if (last_clean > now || last_clean+5*60 < now) {
			last_clean = now;
			work_clean();
		}

		secs = next - now;
		if (secs > 0) {
			/* reap helpers regularly */
			if (secs > HELPER_AUTO_REAP)
				secs = HELPER_AUTO_REAP;
			sleep(secs);
		}
	}
}



void
totals_init(void)
{
	int i;

	signal(SIGUSR1, totals_msg_signal);
	totals.msg_prev = time(0);

	i = pthread_create(&totals_tid, 0, totals_msg_thread, 0);
	if (i)
		dcc_logbad(EX_SOFTWARE, "pthread_create(totals msg): %s",
			   ERROR_STR1(i));
	i = pthread_detach(totals_tid);
	if (i)
		dcc_error_msg("pthread_detach(totals msg): %s",
			      ERROR_STR1(i));
}



void
totals_stop(void)
{
	stopping = 1;

	helper_lock();
	stop_helpers();
	helper_unlock();

	totals_msg();
}



/* brag about our accomplishments */
static void
totals_msg(void)
{
	time_t now;
	char tbuf[20];
	char gbuf[80];
	sigset_t sigsold;
	int error;

	clnt_sigs_off(&sigsold);

	lock_work();
	now = time(0);

	if (grey_on)
		snprintf(gbuf, sizeof(gbuf),
			 " greylist embargoed %d messages to %d targets;",
			 totals.msgs_embargoed, totals.tgts_embargoed);
	else
		gbuf[0] = '\0';

	dcc_trace_msg(DCC_VERSION
		      "%s detected %d spam, ignored for %d, rejected for %d,"
		      " and discarded for %d targets among"
		      " %d total messages for %d targets since %s",
		      gbuf,
		      totals.msgs_spam, totals.tgts_ignored,
		      totals.tgts_rejected, totals.tgts_discarded,
		      totals.msgs, totals.tgts,
		      dcc_time2str(tbuf, sizeof(tbuf), "%x %X",
				   totals.msg_prev));

	memset(&totals, 0, sizeof(totals));
	totals.msg_prev = now;
	unlock_work();

	error = pthread_sigmask(SIG_SETMASK, &sigsold, 0);
	if (error)
		dcc_logbad(EX_SOFTWARE, "pthread_sigmask(totals): %s",
			   ERROR_STR1(error));
}
