/* 
 *  Unix SMB/CIFS implementation.
 *  Performance Counter Daemon
 *
 *  Copyright (C) Marcin Krzysztof Porwit    2005
 *  
 *  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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include "perf.h"

void init_diskdata_desc(PERF_DATA_BLOCK *data)
{
	init_perf_counter(&(data->diskInfo.diskObjDesc),
			  &(data->diskInfo.diskObjDesc),
			  get_counter_id(data),
			  "Logical Disk",
			  "The Logical Disk object consists of counters that show information about disks.",
			  0,
			  PERF_OBJECT);
	init_perf_counter(&(data->diskInfo.freeMegs),
			  &(data->diskInfo.diskObjDesc),
			  get_counter_id(data),
			  "Megabytes Free",
			  "The amount of available disk space, in megabytes.",
			  PERF_SIZE_LARGE | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL | PERF_DISPLAY_NO_SUFFIX,
			  PERF_COUNTER);
	init_perf_counter(&(data->diskInfo.writesPerSec),
			  &(data->diskInfo.diskObjDesc),
			  get_counter_id(data),
			  "Writes/sec",
			  "The number of writes per second to that disk.",
			  PERF_SIZE_DWORD | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_DELTA_COUNTER | PERF_DISPLAY_PER_SEC,
			  PERF_COUNTER);
	init_perf_counter(&(data->diskInfo.readsPerSec),
			  &(data->diskInfo.diskObjDesc),
			  get_counter_id(data),
			  "Reads/sec",
			  "The number of reads of that disk per second.",
			  PERF_SIZE_DWORD | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_DELTA_COUNTER | PERF_DISPLAY_PER_SEC,
			  PERF_COUNTER);

	return;
}
void init_num_disks(PERF_DATA_BLOCK *data)
{
	FILE *mtab;
	char buf[PROC_BUF];
	char *start, *stop;
	int i = 0, num;

	if(!(mtab = fopen("/etc/mtab", "r")))
	{
		perror("init_disk_names: fopen");
		exit(1);
	}

	rewind(mtab);
	fflush(mtab);

	while(fgets(buf, sizeof(buf), mtab))
	{
		if(start = strstr(buf, "/dev/"))
		{
			if(start = strstr(start, "da"))
			{
				i++;
			}
		}
	}

	data->diskInfo.numDisks = i;
	fclose(mtab);

	return;
}
	
void init_disk_names(PERF_DATA_BLOCK *data)
{
	FILE *mtab;
	char buf[PROC_BUF];
	char *start, *stop;
	int i = 0, num;

	if(!(mtab = fopen("/etc/mtab", "r")))
	{
		perror("init_disk_names: fopen");
		exit(1);
	}

	rewind(mtab);
	fflush(mtab);

	while(fgets(buf, sizeof(buf), mtab))
	{
		if(start = strstr(buf, "/dev/"))
		{
			if(start = strstr(start, "da"))
			{
				start -=1;
				stop = strstr(start, " ");
				memcpy(data->diskInfo.mdata[i].name, start, stop - start);
				start = stop +1;
				stop = strstr(start, " ");
				memcpy(data->diskInfo.mdata[i].mountpoint, start, stop - start);
				i++;
			}
		}
	}

	fclose(mtab);

	return;
}

void get_diskinfo(PERF_DATA_BLOCK *data)
{
	int i;
	DiskData *p;
	struct statfs statfsbuf;
	int status, num;
	char buf[LARGE_BUF], *start;
	FILE *diskstats;
	long reads, writes, discard;

	diskstats = fopen("/proc/diskstats", "r");
	rewind(diskstats);
	fflush(diskstats);
	status = fread(buf, sizeof(char), LARGE_BUF, diskstats);
	fclose(diskstats);

	for(i = 0; i < data->diskInfo.numDisks; i++)
	{
		p = &(data->diskInfo.data[i]);
		status = statfs(data->diskInfo.mdata[i].mountpoint, &statfsbuf);
		p->freeMegs = (statfsbuf.f_bfree*statfsbuf.f_bsize)/1048576;
		start = strstr(buf, data->diskInfo.mdata[i].name);
		start += strlen(data->diskInfo.mdata[i].name) + 1;
		num = sscanf(start, "%u %u %u %u",
			     &reads,
			     &discard, 
			     &writes, 
			     &discard);
		p->writesPerSec = writes;
		p->readsPerSec = reads;
		fprintf(stderr, "%s:\t%u\t%u\n",
			data->diskInfo.mdata[i].mountpoint,
			reads, writes);
	}
	return;
}
void init_disk_data(PERF_DATA_BLOCK *data)
{
	init_diskdata_desc(data);
	
	init_num_disks(data);

	data->diskInfo.mdata = calloc(data->diskInfo.numDisks, sizeof(DiskMetaData));
	if(!data->diskInfo.mdata)
	{
		fatal("init_disk_data: out of memory");
	}

	init_disk_names(data);

	data->diskInfo.data = calloc(data->diskInfo.numDisks, sizeof(DiskData));
	if(!data->diskInfo.data)
	{
		fatal("init_disk_data: out of memory");
	}

	get_diskinfo(data);

	return;
}

void output_disk_desc(PERF_DATA_BLOCK *data, RuntimeSettings rt)
{
	output_perf_desc(data->diskInfo.diskObjDesc, rt);
	output_perf_desc(data->diskInfo.freeMegs, rt);
	output_perf_desc(data->diskInfo.writesPerSec, rt);
	output_perf_desc(data->diskInfo.readsPerSec, rt);
	output_num_instances(data->diskInfo.diskObjDesc, data->diskInfo.numDisks, rt);

	return;
}

void output_diskinfo(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags)
{
	int i;
	
	output_perf_counter(data->diskInfo.freeMegs,
			    data->diskInfo.data[0].freeMegs,
			    rt, tdb_flags);
	output_perf_counter(data->diskInfo.writesPerSec,
			    (unsigned long long)data->diskInfo.data[0].writesPerSec,
			    rt, tdb_flags);
	output_perf_counter(data->diskInfo.readsPerSec,
			    (unsigned long long)data->diskInfo.data[0].readsPerSec,
			    rt, tdb_flags);

	for(i = 0; i < data->diskInfo.numDisks; i++)
	{
		output_perf_instance(data->diskInfo.diskObjDesc.index,
				     i,
				     (void *)&(data->diskInfo.data[i]),
				     sizeof(DiskData),
				     data->diskInfo.mdata[i].mountpoint,
				     rt, tdb_flags);
	}

	return;
}
