/*
** CBLM v3
** $Id: probe.c,v 1.1 2006/05/08 01:52:14 sella Exp $
** Copyright (c) 2004-2006 James M. Sella. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
** 
** Cheap Bastard Latency Monitor v3
*/

#include "probe.h"
#include "cblm.h"
#include "db.h"
#include "prefs.h"
#include "util.h"

/* Global variables */
extern struct args_t args;
extern struct prefs_t prefs;
struct probe_t *probes = NULL;
extern int running, restart;
extern pthread_mutex_t probe_mutex, packet_list_mutex, db_mutex, hash_mutex;
unsigned long probes_crc;

MYSQL mysql;

int register_probe() {
	int ret_val = 1;
	char sql[132];
	char buf[INET_ADDRSTRLEN];
	MYSQL_RES *res;
	MYSQL_ROW row;

	pthread_mutex_lock(&db_mutex);

	if (db_query(&res, "LOCK TABLE probes WRITE") != 0) {
		goto exit;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "SELECT id FROM probes WHERE ip = %lu", ntohl(args.source.s_addr));
	if (db_query(&res, sql) != 0) {
		goto exit;
	}

	if (mysql_num_rows(res) < 1) {
		/* We currently aren't registered, do an insert. */
		snprintf(sql, sizeof(sql), "INSERT INTO probes (id, timestamp, ip, name) VALUES (NULL, NULL, %lu, '%s')", ntohl(args.source.s_addr), args.name);

		if (args.debug >= DEBUG_INTERNAL) {
			syslog(LOG_INFO, "Registering probe %s with name %s.", inet_ntop(AF_INET, &(args.source), buf, sizeof(buf)), args.name);
		}
	} else {
		/* Already registered, just update. */
		row = mysql_fetch_row(res);
		snprintf(sql, sizeof(sql), "UPDATE probes SET timestamp = NULL, name = '%s' WHERE id = %s", args.name, row[0]);

		if (args.debug >= DEBUG_INTERNAL) {
			syslog(LOG_INFO, "Refreshing probe %s registration with name %s.", inet_ntop(AF_INET, &(args.source), buf, sizeof(buf)), args.name);
		}
	}
	mysql_free_result(res);

	if (db_query(&res, sql) != 0) {
		goto exit;
	}
	mysql_free_result(res);

	/* Successful. */
	ret_val = 0;

exit:
	db_query(&res, "UNLOCK TABLES");

	pthread_mutex_unlock(&db_mutex);

	return ret_val;
}

int unregister_probe() {
	int ret_val = 1;
	char sql[132];
	MYSQL_RES *res;
	MYSQL_ROW row;

	pthread_mutex_lock(&db_mutex);

	if (db_query(&res, "LOCK TABLE probes WRITE") != 0) {
		goto exit;
	}
	mysql_free_result(res);

	/* Set timestamp to 1, forcing it to be expired. */
	snprintf(sql, sizeof(sql), "UPDATE probes SET timestamp = 1 WHERE ip = %lu", ntohl(args.source.s_addr));
	if (db_query(&res, sql) != 0) {
		goto exit;
	}
	mysql_free_result(res);

	/* Successful. */
	ret_val = 0;

exit:
	db_query(&res, "UNLOCK TABLES");

	pthread_mutex_unlock(&db_mutex);

	return ret_val;
}

int read_probes() {
	int ret_val = 1;
	char sql[132];
	MYSQL_RES *res;
	MYSQL_ROW row;
	struct in_addr in;
	struct probe_t **cur, **prev;
	unsigned long crc;

	pthread_mutex_lock(&probe_mutex);
	pthread_mutex_lock(&db_mutex);

	if (args.debug >= DEBUG_INTERNAL) {
		syslog(LOG_DEBUG, "Checking for updates to the list of probes..");
	}

	if (db_query(&res, "LOCK TABLE probes WRITE") != 0) {
		goto exit;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "SELECT SUM(id), SUM(ip) AS crc FROM probes WHERE timestamp > NOW() - SEC_TO_TIME(%lu)", prefs.maxage);
	if (db_query(&res, sql) != 0) {
		goto exit;
	}
 
	/* Verify we got a row. */
	if (mysql_num_rows(res) < 1) {
		goto exit;
	}

	/* Verify the result isn't NULL. At least one probe must be registered. */
	row = mysql_fetch_row(res);
	if (row[0] == NULL || row[1] == NULL) {
		mysql_free_result(res);

		goto exit;
	}

	/* Calculate the crc. */
	crc = (strtoul(row[0], NULL, 10) / 2) + (strtoul(row[1], NULL, 10) / 7);

	/* Free result */
	mysql_free_result(res);

	if (crc != probes_crc) {
		if (args.debug >= DEBUG_INTERNAL) {
			syslog(LOG_DEBUG, "Update available. Rebuilding probe list.");
		}

		/* Release old probes struct. */
		free_probes();

		/* Change crc to something else. */
		probes_crc--;

		/* Build probes structure. */
		snprintf(sql, sizeof(sql), "SELECT timestamp, ip FROM probes WHERE ip != %lu AND timestamp > NOW() - SEC_TO_TIME(%lu)", ntohl(args.source.s_addr), prefs.maxage);
		if (db_query(&res, sql) != 0) {
			goto exit;
		} else if (mysql_num_rows(res) > 0) {
			/* Build probes structure. */
			cur = &probes;
			while (row = mysql_fetch_row(res)) {
				if (args.debug >= DEBUG_INTERNAL) {
					in.s_addr = htonl(strtoul(row[1], NULL, 10));

					syslog(LOG_DEBUG, "probe: %s (%s), timestamp: %s", inet_ntoa(in), row[1], row[0]);
				}

				in.s_addr = htonl(strtoul(row[1], NULL, 10));
				*cur = malloc(sizeof(struct probe_t));
				(*cur)->ip.s_addr = htonl(strtoul(row[1], NULL, 10));
				(*cur)->next = NULL;
				cur = &(*cur)->next;
			}

			/* Update the CRC. */
			probes_crc = crc;
		}
		mysql_free_result(res);
	} else {
		if (args.debug >= DEBUG_INTERNAL) {
			syslog(LOG_DEBUG, "No updates to probe list.");
		}
	}

	/* Successful. */
	ret_val = 0;

exit:
	db_query(&res, "UNLOCK TABLES");
	mysql_free_result(res);

	pthread_mutex_unlock(&db_mutex);
	pthread_mutex_unlock(&probe_mutex);

	return ret_val;
}

void free_probes() {
	struct probe_t *cur, *prev;

	cur = probes;
	while (cur != NULL) {
		prev = cur;
		cur = cur->next;
		free(prev);
		prev = NULL;
	}
	probes = NULL;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim: noet ts=3 sw=3
*/
