/* 
** ZABBIX
** Copyright (C) 2000-2005 SIA Zabbix
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**/

#include "common.h"

#include "sysinfo.h"

static struct nlist kernel_symbols[] = 
{
	{"_ifnet", N_UNDF, 0, 0, 0},
	{"_tcbtable", N_UNDF, 0, 0, 0},
	{NULL, 0, 0, 0, 0}
};

#define IFNET_ID 0

static int	get_ifdata(const char *if_name, zbx_uint64_t *ibytes, zbx_uint64_t *ipackets, zbx_uint64_t *ierrors, zbx_uint64_t *idropped,
						zbx_uint64_t *obytes, zbx_uint64_t *opackets, zbx_uint64_t *oerrors,
						zbx_uint64_t *tbytes, zbx_uint64_t *tpackets, zbx_uint64_t *terrors, zbx_uint64_t *tdropped,
						zbx_uint64_t *icollisions)
{
	int			mib[6], ifnum;
	size_t			len;
	struct ifmibdata	ifdata;
	int 			ret = SYSINFO_RET_FAIL;

	mib[0] = CTL_NET;
	mib[1] = PF_LINK;
	mib[2] = NETLINK_GENERIC;
	mib[3] = IFMIB_SYSTEM;
	mib[4] = IFMIB_IFCOUNT;

	len = sizeof(ifnum);
	if (0 != sysctl(mib, 5, &ifnum, &len, NULL, 0))
		return SYSINFO_RET_FAIL;

	mib[3] = IFMIB_IFDATA;
	mib[4] = 0;
	mib[5] = IFDATA_GENERAL;

	len = sizeof(ifdata);

	if (ibytes)
		*ibytes = 0;
	if (ipackets)
		*ipackets = 0;
	if (ierrors)
		*ierrors = 0;
	if (idropped)
		*idropped = 0;
	if (obytes)
		*obytes = 0;
	if (opackets)
		*opackets = 0;
	if (oerrors)
		*oerrors = 0;
	if (tbytes)
		*tbytes = 0;
	if (tpackets)
		*tpackets = 0;
	if (terrors)
		*terrors = 0;
	if (tdropped)
		*tdropped = 0;
	if (icollisions)
		*icollisions = 0;

	for (; ifnum > 0; ifnum--) {
		mib[4] = ifnum;

		if (0 != sysctl(mib, 6, &ifdata, &len, NULL, 0))
			continue;

		if (*if_name == '\0' || 0 == strcmp(if_name, ifdata.ifmd_name)) {
			if (ibytes)
				*ibytes += ifdata.ifmd_data.ifi_ibytes;
			if (ipackets)
				*ipackets += ifdata.ifmd_data.ifi_ipackets;
			if (ierrors)
				*ierrors += ifdata.ifmd_data.ifi_ierrors;
			if (idropped)
				*idropped += ifdata.ifmd_data.ifi_iqdrops;
			if (obytes)
				*obytes += ifdata.ifmd_data.ifi_obytes;
			if (opackets)
				*opackets += ifdata.ifmd_data.ifi_opackets;
			if (oerrors)
				*oerrors += ifdata.ifmd_data.ifi_oerrors;
			if (tbytes)
				*tbytes += ifdata.ifmd_data.ifi_ibytes + ifdata.ifmd_data.ifi_obytes;
			if (tpackets)
				*tpackets += ifdata.ifmd_data.ifi_ipackets + ifdata.ifmd_data.ifi_opackets;
			if (terrors)
				*terrors += ifdata.ifmd_data.ifi_ierrors + ifdata.ifmd_data.ifi_oerrors;
			if (tdropped)
				*tdropped += ifdata.ifmd_data.ifi_iqdrops;
			if (icollisions)
				*icollisions += ifdata.ifmd_data.ifi_collisions;
			ret = SYSINFO_RET_OK;
		}
	}

	return ret;
}

static int	NET_IF_IN_BYTES(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, &value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int	NET_IF_IN_PACKETS(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, &value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int	NET_IF_IN_ERRORS(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, &value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int	NET_IF_IN_DROPPED(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, &value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

int	NET_IF_IN(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result)
{
#define NET_FNCLIST struct net_fnclist_s
NET_FNCLIST
{
	char	*mode;
	int	(*function)();
};

	NET_FNCLIST fl[] =
	{
		{"bytes",	NET_IF_IN_BYTES},
		{"packets",	NET_IF_IN_PACKETS},
		{"errors",	NET_IF_IN_ERRORS},
		{"dropped",	NET_IF_IN_DROPPED},
		{0,		0}
	};

	char	if_name[MAX_STRING_LEN];
	char	mode[MAX_STRING_LEN];
	int	i;

	assert(result);

	init_result(result);

	if (num_param(param) > 2)
		return SYSINFO_RET_FAIL;

	if (0 != get_param(param, 1, if_name, sizeof(if_name)))
		*if_name = '\0';

	if (0 != get_param(param, 2, mode, sizeof(mode)))
		*mode = '\0';

	/* default parameter */
	if (*mode == '\0')
		zbx_snprintf(mode, sizeof(mode), "bytes");

	for (i = 0; fl[i].mode != 0; i++)
		if (0 == strncmp(mode, fl[i].mode, MAX_STRING_LEN))
			return (fl[i].function)(if_name, result);

	return SYSINFO_RET_FAIL;
}

static int	NET_IF_OUT_BYTES(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, &value, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int      NET_IF_OUT_PACKETS(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, &value, NULL, NULL, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int      NET_IF_OUT_ERRORS(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, &value, NULL, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

int	NET_IF_OUT(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result)
{
#define NET_FNCLIST struct net_fnclist_s
NET_FNCLIST
{
	char	*mode;
	int	(*function)();
};

	NET_FNCLIST fl[] =
	{
		{"bytes",	NET_IF_OUT_BYTES},
		{"packets",	NET_IF_OUT_PACKETS},
		{"errors",	NET_IF_OUT_ERRORS},
/*		{"dropped",	NET_IF_OUT_DROPPED},*/
		{0,		0}
	};

	char	if_name[MAX_STRING_LEN];
	char	mode[MAX_STRING_LEN];
	int	i;

	assert(result);

	init_result(result);

	if (num_param(param) > 2)
		return SYSINFO_RET_FAIL;

	if (0 != get_param(param, 1, if_name, sizeof(if_name)))
		*if_name = '\0';

	if (0 != get_param(param, 2, mode, sizeof(mode)))
		*mode = '\0';

	/* default parameter */
	if (*mode == '\0')
		zbx_snprintf(mode, sizeof(mode), "bytes");

	for (i = 0; fl[i].mode != 0; i++)
		if (0 == strncmp(mode, fl[i].mode, MAX_STRING_LEN))
			return (fl[i].function)(if_name, result);

	return SYSINFO_RET_FAIL;
}

static int	NET_IF_TOTAL_BYTES(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &value, NULL, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int	NET_IF_TOTAL_PACKETS(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &value, NULL, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int	NET_IF_TOTAL_ERRORS(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &value, NULL, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

static int	NET_IF_TOTAL_DROPPED(const char *if_name, AGENT_RESULT *result)
{
	zbx_uint64_t	value;

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &value, NULL))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}

int	NET_IF_TOTAL(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result)
{
#define NET_FNCLIST struct net_fnclist_s
NET_FNCLIST
{
	char	*mode;
	int	(*function)();
};

	NET_FNCLIST fl[] =
	{
		{"bytes",	NET_IF_TOTAL_BYTES},
		{"packets",	NET_IF_TOTAL_PACKETS},
		{"errors",	NET_IF_TOTAL_ERRORS},
		{"dropped",	NET_IF_TOTAL_DROPPED},
		{0,		0}
	};

	char	if_name[MAX_STRING_LEN];
	char	mode[MAX_STRING_LEN];
	int	i;

	assert(result);

	init_result(result);

	if (num_param(param) > 2)
		return SYSINFO_RET_FAIL;

	if (0 != get_param(param, 1, if_name, sizeof(if_name)))
		*if_name = '\0';

	if (0 != get_param(param, 2, mode, sizeof(mode)))
		*mode = '\0';

	/* default parameter */
	if (*mode == '\0')
		zbx_snprintf(mode, sizeof(mode), "bytes");

	for (i = 0; fl[i].mode != 0; i++)
		if (0 == strncmp(mode, fl[i].mode, MAX_STRING_LEN))
			return (fl[i].function)(if_name, result);

	return SYSINFO_RET_FAIL;
}

int     NET_TCP_LISTEN(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result)
{
	assert(result);

	init_result(result);
	
	return SYSINFO_RET_FAIL;
}

int     NET_IF_COLLISIONS(const char *cmd, const char *param, unsigned flags, AGENT_RESULT *result)
{
	zbx_uint64_t	value;
	char		if_name[MAX_STRING_LEN];

	assert(result);

	init_result(result);

	if (num_param(param) > 1)
		return SYSINFO_RET_FAIL;

	if (0 != get_param(param, 1, if_name, sizeof(if_name)))
		*if_name = '\0';

	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &value))
		return SYSINFO_RET_FAIL;

	SET_UI64_RESULT(result, value);

	return SYSINFO_RET_OK;
}
