Beispiel #1
0
static int disk_read (void)
{
#if HAVE_IOKIT_IOKITLIB_H
	io_registry_entry_t	disk;
	io_registry_entry_t	disk_child;
	io_iterator_t		disk_list;
	CFDictionaryRef		props_dict;
	CFDictionaryRef		stats_dict;
	CFDictionaryRef		child_dict;
	CFStringRef		tmp_cf_string_ref;
	kern_return_t		status;

	signed long long read_ops;
	signed long long read_byt;
	signed long long read_tme;
	signed long long write_ops;
	signed long long write_byt;
	signed long long write_tme;

	int  disk_major;
	int  disk_minor;
	char disk_name[DATA_MAX_NAME_LEN];
	char disk_name_bsd[DATA_MAX_NAME_LEN];

	/* Get the list of all disk objects. */
	if (IOServiceGetMatchingServices (io_master_port,
				IOServiceMatching (kIOBlockStorageDriverClass),
				&disk_list) != kIOReturnSuccess)
	{
		ERROR ("disk plugin: IOServiceGetMatchingServices failed.");
		return (-1);
	}

	while ((disk = IOIteratorNext (disk_list)) != 0)
	{
		props_dict = NULL;
		stats_dict = NULL;
		child_dict = NULL;

		/* `disk_child' must be released */
		if ((status = IORegistryEntryGetChildEntry (disk, kIOServicePlane, &disk_child))
			       	!= kIOReturnSuccess)
		{
			/* This fails for example for DVD/CD drives.. */
			DEBUG ("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status);
			IOObjectRelease (disk);
			continue;
		}

		/* We create `props_dict' => we need to release it later */
		if (IORegistryEntryCreateCFProperties (disk,
					(CFMutableDictionaryRef *) &props_dict,
					kCFAllocatorDefault,
					kNilOptions)
				!= kIOReturnSuccess)
		{
			ERROR ("disk-plugin: IORegistryEntryCreateCFProperties failed.");
			IOObjectRelease (disk_child);
			IOObjectRelease (disk);
			continue;
		}

		if (props_dict == NULL)
		{
			DEBUG ("IORegistryEntryCreateCFProperties (disk) failed.");
			IOObjectRelease (disk_child);
			IOObjectRelease (disk);
			continue;
		}

		/* tmp_cf_string_ref doesn't need to be released. */
		tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict,
				CFSTR(kIOBSDNameKey));
		if (!tmp_cf_string_ref)
		{
			DEBUG ("disk plugin: CFDictionaryGetValue("
					"kIOBSDNameKey) failed.");
			CFRelease (props_dict);
			IOObjectRelease (disk_child);
			IOObjectRelease (disk);
			continue;
		}
		assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());

		memset (disk_name_bsd, 0, sizeof (disk_name_bsd));
		CFStringGetCString (tmp_cf_string_ref,
				disk_name_bsd, sizeof (disk_name_bsd),
				kCFStringEncodingUTF8);
		if (disk_name_bsd[0] == 0)
		{
			ERROR ("disk plugin: CFStringGetCString() failed.");
			CFRelease (props_dict);
			IOObjectRelease (disk_child);
			IOObjectRelease (disk);
			continue;
		}
		DEBUG ("disk plugin: disk_name_bsd = \"%s\"", disk_name_bsd);

		stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict,
				CFSTR (kIOBlockStorageDriverStatisticsKey));

		if (stats_dict == NULL)
		{
			DEBUG ("disk plugin: CFDictionaryGetValue ("
					"%s) failed.",
				       	kIOBlockStorageDriverStatisticsKey);
			CFRelease (props_dict);
			IOObjectRelease (disk_child);
			IOObjectRelease (disk);
			continue;
		}

		if (IORegistryEntryCreateCFProperties (disk_child,
					(CFMutableDictionaryRef *) &child_dict,
					kCFAllocatorDefault,
					kNilOptions)
				!= kIOReturnSuccess)
		{
			DEBUG ("disk plugin: IORegistryEntryCreateCFProperties ("
					"disk_child) failed.");
			IOObjectRelease (disk_child);
			CFRelease (props_dict);
			IOObjectRelease (disk);
			continue;
		}

		/* kIOBSDNameKey */
		disk_major = (int) dict_get_value (child_dict,
			       	kIOBSDMajorKey);
		disk_minor = (int) dict_get_value (child_dict,
			       	kIOBSDMinorKey);
		read_ops  = dict_get_value (stats_dict,
				kIOBlockStorageDriverStatisticsReadsKey);
		read_byt  = dict_get_value (stats_dict,
				kIOBlockStorageDriverStatisticsBytesReadKey);
		read_tme  = dict_get_value (stats_dict,
				kIOBlockStorageDriverStatisticsTotalReadTimeKey);
		write_ops = dict_get_value (stats_dict,
				kIOBlockStorageDriverStatisticsWritesKey);
		write_byt = dict_get_value (stats_dict,
				kIOBlockStorageDriverStatisticsBytesWrittenKey);
		/* This property describes the number of nanoseconds spent
		 * performing writes since the block storage driver was
		 * instantiated. It is one of the statistic entries listed
		 * under the top-level kIOBlockStorageDriverStatisticsKey
		 * property table. It has an OSNumber value. */
		write_tme = dict_get_value (stats_dict,
				kIOBlockStorageDriverStatisticsTotalWriteTimeKey);

		if (use_bsd_name)
			sstrncpy (disk_name, disk_name_bsd, sizeof (disk_name));
		else
			ssnprintf (disk_name, sizeof (disk_name), "%i-%i",
					disk_major, disk_minor);
		DEBUG ("disk plugin: disk_name = \"%s\"", disk_name);

		if ((read_byt != -1LL) || (write_byt != -1LL))
			disk_submit (disk_name, "disk_octets", read_byt, write_byt);
		if ((read_ops != -1LL) || (write_ops != -1LL))
			disk_submit (disk_name, "disk_ops", read_ops, write_ops);
		if ((read_tme != -1LL) || (write_tme != -1LL))
			disk_submit (disk_name, "disk_time",
					read_tme / 1000,
					write_tme / 1000);

		CFRelease (child_dict);
		IOObjectRelease (disk_child);
		CFRelease (props_dict);
		IOObjectRelease (disk);
	}
	IOObjectRelease (disk_list);
/* #endif HAVE_IOKIT_IOKITLIB_H */

#elif KERNEL_LINUX
	FILE *fh;
	char buffer[1024];
	
	char *fields[32];
	int numfields;
	int fieldshift = 0;

	int minor = 0;

	derive_t read_sectors  = 0;
	derive_t write_sectors = 0;

	derive_t read_ops      = 0;
	derive_t read_merged   = 0;
	derive_t read_time     = 0;
	derive_t write_ops     = 0;
	derive_t write_merged  = 0;
	derive_t write_time    = 0;
	gauge_t in_progress    = NAN;
	int is_disk = 0;

	diskstats_t *ds, *pre_ds;

	if ((fh = fopen ("/proc/diskstats", "r")) == NULL)
	{
		fh = fopen ("/proc/partitions", "r");
		if (fh == NULL)
		{
			ERROR ("disk plugin: fopen (/proc/{diskstats,partitions}) failed.");
			return (-1);
		}

		/* Kernel is 2.4.* */
		fieldshift = 1;
	}

#if HAVE_LIBUDEV
	handle_udev = udev_new();
#endif

	while (fgets (buffer, sizeof (buffer), fh) != NULL)
	{
		char *disk_name;
		char *output_name;
		char *alt_name;

		numfields = strsplit (buffer, fields, 32);

		if ((numfields != (14 + fieldshift)) && (numfields != 7))
			continue;

		minor = atoll (fields[1]);

		disk_name = fields[2 + fieldshift];

		for (ds = disklist, pre_ds = disklist; ds != NULL; pre_ds = ds, ds = ds->next)
			if (strcmp (disk_name, ds->name) == 0)
				break;

		if (ds == NULL)
		{
			if ((ds = (diskstats_t *) calloc (1, sizeof (diskstats_t))) == NULL)
				continue;

			if ((ds->name = strdup (disk_name)) == NULL)
			{
				free (ds);
				continue;
			}

			if (pre_ds == NULL)
				disklist = ds;
			else
				pre_ds->next = ds;
		}

		is_disk = 0;
		if (numfields == 7)
		{
			/* Kernel 2.6, Partition */
			read_ops      = atoll (fields[3]);
			read_sectors  = atoll (fields[4]);
			write_ops     = atoll (fields[5]);
			write_sectors = atoll (fields[6]);
		}
		else if (numfields == (14 + fieldshift))
		{
			read_ops  =  atoll (fields[3 + fieldshift]);
			write_ops =  atoll (fields[7 + fieldshift]);

			read_sectors  = atoll (fields[5 + fieldshift]);
			write_sectors = atoll (fields[9 + fieldshift]);

			if ((fieldshift == 0) || (minor == 0))
			{
				is_disk = 1;
				read_merged  = atoll (fields[4 + fieldshift]);
				read_time    = atoll (fields[6 + fieldshift]);
				write_merged = atoll (fields[8 + fieldshift]);
				write_time   = atoll (fields[10+ fieldshift]);

				in_progress = atof (fields[11 + fieldshift]);
			}
		}
		else
		{
			DEBUG ("numfields = %i; => unknown file format.", numfields);
			continue;
		}

		{
			derive_t diff_read_sectors;
			derive_t diff_write_sectors;

		/* If the counter wraps around, it's only 32 bits.. */
			if (read_sectors < ds->read_sectors)
				diff_read_sectors = 1 + read_sectors
					+ (UINT_MAX - ds->read_sectors);
			else
				diff_read_sectors = read_sectors - ds->read_sectors;
			if (write_sectors < ds->write_sectors)
				diff_write_sectors = 1 + write_sectors
					+ (UINT_MAX - ds->write_sectors);
			else
				diff_write_sectors = write_sectors - ds->write_sectors;

			ds->read_bytes += 512 * diff_read_sectors;
			ds->write_bytes += 512 * diff_write_sectors;
			ds->read_sectors = read_sectors;
			ds->write_sectors = write_sectors;
		}

		/* Calculate the average time an io-op needs to complete */
		if (is_disk)
		{
			derive_t diff_read_ops;
			derive_t diff_write_ops;
			derive_t diff_read_time;
			derive_t diff_write_time;

			if (read_ops < ds->read_ops)
				diff_read_ops = 1 + read_ops
					+ (UINT_MAX - ds->read_ops);
			else
				diff_read_ops = read_ops - ds->read_ops;
			DEBUG ("disk plugin: disk_name = %s; read_ops = %"PRIi64"; "
					"ds->read_ops = %"PRIi64"; diff_read_ops = %"PRIi64";",
					disk_name,
					read_ops, ds->read_ops, diff_read_ops);

			if (write_ops < ds->write_ops)
				diff_write_ops = 1 + write_ops
					+ (UINT_MAX - ds->write_ops);
			else
				diff_write_ops = write_ops - ds->write_ops;

			if (read_time < ds->read_time)
				diff_read_time = 1 + read_time
					+ (UINT_MAX - ds->read_time);
			else
				diff_read_time = read_time - ds->read_time;

			if (write_time < ds->write_time)
				diff_write_time = 1 + write_time
					+ (UINT_MAX - ds->write_time);
			else
				diff_write_time = write_time - ds->write_time;

			if (diff_read_ops != 0)
				ds->avg_read_time += disk_calc_time_incr (
						diff_read_time, diff_read_ops);
			if (diff_write_ops != 0)
				ds->avg_write_time += disk_calc_time_incr (
						diff_write_time, diff_write_ops);

			ds->read_ops = read_ops;
			ds->read_time = read_time;
			ds->write_ops = write_ops;
			ds->write_time = write_time;
		} /* if (is_disk) */

		/* Don't write to the RRDs if we've just started.. */
		ds->poll_count++;
		if (ds->poll_count <= 2)
		{
			DEBUG ("disk plugin: (ds->poll_count = %i) <= "
					"(min_poll_count = 2); => Not writing.",
					ds->poll_count);
			continue;
		}

		if ((read_ops == 0) && (write_ops == 0))
		{
			DEBUG ("disk plugin: ((read_ops == 0) && "
					"(write_ops == 0)); => Not writing.");
			continue;
		}

		output_name = disk_name;

#if HAVE_LIBUDEV
		alt_name = disk_udev_attr_name (handle_udev, disk_name,
				conf_udev_name_attr);
#else
		alt_name = NULL;
#endif
		if (alt_name != NULL)
			output_name = alt_name;

		if ((ds->read_bytes != 0) || (ds->write_bytes != 0))
			disk_submit (output_name, "disk_octets",
					ds->read_bytes, ds->write_bytes);

		if ((ds->read_ops != 0) || (ds->write_ops != 0))
			disk_submit (output_name, "disk_ops",
					read_ops, write_ops);

		if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0))
			disk_submit (output_name, "disk_time",
					ds->avg_read_time, ds->avg_write_time);

		if (is_disk)
		{
			disk_submit (output_name, "disk_merged",
					read_merged, write_merged);
			submit_in_progress (output_name, in_progress);
		} /* if (is_disk) */

		/* release udev-based alternate name, if allocated */
		free(alt_name);
	} /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */

#if HAVE_LIBUDEV
	udev_unref(handle_udev);
#endif

	fclose (fh);
/* #endif defined(KERNEL_LINUX) */

#elif HAVE_LIBKSTAT
# if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME
#  define KIO_ROCTETS reads
#  define KIO_WOCTETS writes
#  define KIO_ROPS    nreads
#  define KIO_WOPS    nwrites
#  define KIO_RTIME   rtime
#  define KIO_WTIME   wtime
# elif HAVE_KSTAT_IO_T_NWRITTEN && HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_WTIME
#  define KIO_ROCTETS nread
#  define KIO_WOCTETS nwritten
#  define KIO_ROPS    reads
#  define KIO_WOPS    writes
#  define KIO_RTIME   rtime
#  define KIO_WTIME   wtime
# else
#  error "kstat_io_t does not have the required members"
# endif
	static kstat_io_t kio;
	int i;

	if (kc == NULL)
		return (-1);

	for (i = 0; i < numdisk; i++)
	{
		if (kstat_read (kc, ksp[i], &kio) == -1)
			continue;

		if (strncmp (ksp[i]->ks_class, "disk", 4) == 0)
		{
			disk_submit (ksp[i]->ks_name, "disk_octets",
					kio.KIO_ROCTETS, kio.KIO_WOCTETS);
			disk_submit (ksp[i]->ks_name, "disk_ops",
					kio.KIO_ROPS, kio.KIO_WOPS);
			/* FIXME: Convert this to microseconds if necessary */
			disk_submit (ksp[i]->ks_name, "disk_time",
					kio.KIO_RTIME, kio.KIO_WTIME);
		}
		else if (strncmp (ksp[i]->ks_class, "partition", 9) == 0)
		{
			disk_submit (ksp[i]->ks_name, "disk_octets",
					kio.KIO_ROCTETS, kio.KIO_WOCTETS);
			disk_submit (ksp[i]->ks_name, "disk_ops",
					kio.KIO_ROPS, kio.KIO_WOPS);
		}
	}
/* #endif defined(HAVE_LIBKSTAT) */

#elif defined(HAVE_LIBSTATGRAB)
	sg_disk_io_stats *ds;
# if HAVE_LIBSTATGRAB_0_90
	size_t disks;
# else
	int disks;
#endif
	int counter;
	char name[DATA_MAX_NAME_LEN];
	
	if ((ds = sg_get_disk_io_stats(&disks)) == NULL)
		return (0);
		
	for (counter=0; counter < disks; counter++) {
		strncpy(name, ds->disk_name, sizeof(name));
		name[sizeof(name)-1] = '\0'; /* strncpy doesn't terminate longer strings */
		disk_submit (name, "disk_octets", ds->read_bytes, ds->write_bytes);
		ds++;
	}
/* #endif defined(HAVE_LIBSTATGRAB) */

#elif defined(HAVE_PERFSTAT)
	derive_t read_sectors;
	derive_t write_sectors;
	derive_t read_time;
	derive_t write_time;
	derive_t read_ops;
	derive_t write_ops;
	perfstat_id_t firstpath;
	int rnumdisk;
	int i;

	if ((numdisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0)) < 0) 
	{
		char errbuf[1024];
		WARNING ("disk plugin: perfstat_disk: %s",
				sstrerror (errno, errbuf, sizeof (errbuf)));
		return (-1);
	}

	if (numdisk != pnumdisk || stat_disk==NULL) {
		if (stat_disk!=NULL) 
			free(stat_disk);
		stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t));
	} 
	pnumdisk = numdisk;

	firstpath.name[0]='\0';
	if ((rnumdisk = perfstat_disk(&firstpath, stat_disk, sizeof(perfstat_disk_t), numdisk)) < 0) 
	{
		char errbuf[1024];
		WARNING ("disk plugin: perfstat_disk : %s",
				sstrerror (errno, errbuf, sizeof (errbuf)));
		return (-1);
	}

	for (i = 0; i < rnumdisk; i++) 
	{
		read_sectors = stat_disk[i].rblks*stat_disk[i].bsize;
		write_sectors = stat_disk[i].wblks*stat_disk[i].bsize;
		disk_submit (stat_disk[i].name, "disk_octets", read_sectors, write_sectors);

		read_ops = stat_disk[i].xrate;
		write_ops = stat_disk[i].xfers - stat_disk[i].xrate;
		disk_submit (stat_disk[i].name, "disk_ops", read_ops, write_ops);

		read_time = stat_disk[i].rserv;
		read_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0;
		write_time = stat_disk[i].wserv;
		write_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0;
		disk_submit (stat_disk[i].name, "disk_time", read_time, write_time);
	}
#endif /* defined(HAVE_PERFSTAT) */

	return (0);
} /* int disk_read */
Beispiel #2
0
static int disk_read (void)
{
#if HAVE_IOKIT_IOKITLIB_H
	io_registry_entry_t	disk;
	io_registry_entry_t	disk_child;
	io_iterator_t		disk_list;
	CFMutableDictionaryRef	props_dict, child_dict;
	CFDictionaryRef		stats_dict;
	CFStringRef		tmp_cf_string_ref;
	kern_return_t		status;

	signed long long read_ops, read_byt, read_tme;
	signed long long write_ops, write_byt, write_tme;

	int  disk_major, disk_minor;
	char disk_name[DATA_MAX_NAME_LEN];
	char child_disk_name_bsd[DATA_MAX_NAME_LEN], props_disk_name_bsd[DATA_MAX_NAME_LEN];

	/* Get the list of all disk objects. */
	if (IOServiceGetMatchingServices (io_master_port, IOServiceMatching (kIOBlockStorageDriverClass), &disk_list) != kIOReturnSuccess) {
		ERROR ("disk plugin: IOServiceGetMatchingServices failed.");
		return (-1);
	}

	while ((disk = IOIteratorNext (disk_list)) != 0) {
		props_dict = NULL;
		stats_dict = NULL;
		child_dict = NULL;

		/* get child of disk entry and corresponding property dictionary */
		if ((status = IORegistryEntryGetChildEntry (disk, kIOServicePlane, &disk_child)) != kIOReturnSuccess) {
			/* This fails for example for DVD/CD drives, which we want to ignore anyway */
			DEBUG ("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status);
			IOObjectRelease (disk);
			continue;
		}
		if (IORegistryEntryCreateCFProperties (disk_child, (CFMutableDictionaryRef *) &child_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess || child_dict == NULL) {
			ERROR ("disk plugin: IORegistryEntryCreateCFProperties (disk_child) failed.");
			IOObjectRelease (disk_child);
			IOObjectRelease (disk);
			continue;
		}

		/* extract name and major/minor numbers */
		memset (child_disk_name_bsd, 0, sizeof (child_disk_name_bsd));
		tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (child_dict, CFSTR(kIOBSDNameKey));
		if (tmp_cf_string_ref) {
			assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());
			CFStringGetCString (tmp_cf_string_ref, child_disk_name_bsd, sizeof (child_disk_name_bsd), kCFStringEncodingUTF8);
		}
		disk_major = (int) dict_get_value (child_dict, kIOBSDMajorKey);
		disk_minor = (int) dict_get_value (child_dict, kIOBSDMinorKey);
		DEBUG ("disk plugin: child_disk_name_bsd=\"%s\" major=%d minor=%d", child_disk_name_bsd, disk_major, disk_minor);
		CFRelease (child_dict);
		IOObjectRelease (disk_child);

		/* get property dictionary of the disk entry itself */
		if (IORegistryEntryCreateCFProperties (disk, (CFMutableDictionaryRef *) &props_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess || props_dict == NULL) {
			ERROR ("disk-plugin: IORegistryEntryCreateCFProperties failed.");
			IOObjectRelease (disk);
			continue;
		}

		/* extract name and stats dictionary */
		memset (props_disk_name_bsd, 0, sizeof (props_disk_name_bsd));
		tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict, CFSTR(kIOBSDNameKey));
		if (tmp_cf_string_ref) {
			assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());
			CFStringGetCString (tmp_cf_string_ref, props_disk_name_bsd, sizeof (props_disk_name_bsd), kCFStringEncodingUTF8);
		}
		stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict, CFSTR (kIOBlockStorageDriverStatisticsKey));
		if (stats_dict == NULL) {
			ERROR ("disk plugin: CFDictionaryGetValue (%s) failed.", kIOBlockStorageDriverStatisticsKey);
			CFRelease (props_dict);
			IOObjectRelease (disk);
			continue;
		}
		DEBUG ("disk plugin: props_disk_name_bsd=\"%s\"", props_disk_name_bsd);

		/* choose name */
		if (use_bsd_name) {
			if (child_disk_name_bsd[0] != 0)
				sstrncpy (disk_name, child_disk_name_bsd, sizeof (disk_name));
			else if (props_disk_name_bsd[0] != 0)
				sstrncpy (disk_name, props_disk_name_bsd, sizeof (disk_name));
			else {
				ERROR ("disk plugin: can't find bsd disk name.");
				ssnprintf (disk_name, sizeof (disk_name), "%i-%i", disk_major, disk_minor);
			}
		}
		else
			ssnprintf (disk_name, sizeof (disk_name), "%i-%i", disk_major, disk_minor);

		/* extract the stats */
		read_ops  = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsReadsKey);
		read_byt  = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesReadKey);
		read_tme  = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalReadTimeKey);
		write_ops = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsWritesKey);
		write_byt = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesWrittenKey);
		write_tme = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
		CFRelease (props_dict);
		IOObjectRelease (disk);

		/* and submit */
		DEBUG ("disk plugin: disk_name = \"%s\"", disk_name);
		if ((read_byt != -1LL) || (write_byt != -1LL))
			disk_submit (disk_name, "disk_octets", read_byt, write_byt);
		if ((read_ops != -1LL) || (write_ops != -1LL))
			disk_submit (disk_name, "disk_ops", read_ops, write_ops);
		if ((read_tme != -1LL) || (write_tme != -1LL))
			disk_submit (disk_name, "disk_time", read_tme / 1000, write_tme / 1000);

	}
	IOObjectRelease (disk_list);
/* #endif HAVE_IOKIT_IOKITLIB_H */

#elif KERNEL_FREEBSD
	int retry, dirty;

	void *snap = NULL;
	struct devstat *snap_iter;

	struct gident *geom_id;

	const char *disk_name;
	long double read_time, write_time;

	for (retry = 0, dirty = 1; retry < 5 && dirty == 1; retry++) {
		if (snap != NULL)
			geom_stats_snapshot_free(snap);

		/* Get a fresh copy of stats snapshot */
		snap = geom_stats_snapshot_get();
		if (snap == NULL) {
			ERROR("disk plugin: geom_stats_snapshot_get() failed.");
			return (-1);
		}

		/* Check if we have dirty read from this snapshot */
		dirty = 0;
		geom_stats_snapshot_reset(snap);
		while ((snap_iter = geom_stats_snapshot_next(snap)) != NULL) {
			if (snap_iter->id == NULL)
				continue;
			geom_id = geom_lookupid(&geom_tree, snap_iter->id);

			/* New device? refresh GEOM tree */
			if (geom_id == NULL) {
				geom_deletetree(&geom_tree);
				if (geom_gettree(&geom_tree) != 0) {
					ERROR("disk plugin: geom_gettree() failed");
					geom_stats_snapshot_free(snap);
					return (-1);
				}
				geom_id = geom_lookupid(&geom_tree, snap_iter->id);
			}
			/*
			 * This should be rare: the device come right before we take the
			 * snapshot and went away right after it.  We will handle this
			 * case later, so don't mark dirty but silently ignore it.
			 */
			if (geom_id == NULL)
				continue;

			/* Only collect PROVIDER data */
			if (geom_id->lg_what != ISPROVIDER)
				continue;

			/* Only collect data when rank is 1 (physical devices) */
			if (((struct gprovider *)(geom_id->lg_ptr))->lg_geom->lg_rank != 1)
				continue;

			/* Check if this is a dirty read quit for another try */
			if (snap_iter->sequence0 != snap_iter->sequence1) {
				dirty = 1;
				break;
			}
		}
	}

	/* Reset iterator */
	geom_stats_snapshot_reset(snap);
	for (;;) {
		snap_iter = geom_stats_snapshot_next(snap);
		if (snap_iter == NULL)
			break;

		if (snap_iter->id == NULL)
			continue;
		geom_id = geom_lookupid(&geom_tree, snap_iter->id);
		if (geom_id == NULL)
			continue;
		if (geom_id->lg_what != ISPROVIDER)
			continue;
		if (((struct gprovider *)(geom_id->lg_ptr))->lg_geom->lg_rank != 1)
			continue;
		/* Skip dirty reads, if present */
		if (dirty && (snap_iter->sequence0 != snap_iter->sequence1))
			continue;

		disk_name = ((struct gprovider *)geom_id->lg_ptr)->lg_name;

		if ((snap_iter->bytes[DEVSTAT_READ] != 0) || (snap_iter->bytes[DEVSTAT_WRITE] != 0)) {
			disk_submit(disk_name, "disk_octets",
					(derive_t)snap_iter->bytes[DEVSTAT_READ],
					(derive_t)snap_iter->bytes[DEVSTAT_WRITE]);
		}

		if ((snap_iter->operations[DEVSTAT_READ] != 0) || (snap_iter->operations[DEVSTAT_WRITE] != 0)) {
			disk_submit(disk_name, "disk_ops",
					(derive_t)snap_iter->operations[DEVSTAT_READ],
					(derive_t)snap_iter->operations[DEVSTAT_WRITE]);
		}

		read_time = devstat_compute_etime(&snap_iter->duration[DEVSTAT_READ], NULL);
		write_time = devstat_compute_etime(&snap_iter->duration[DEVSTAT_WRITE], NULL);
		if ((read_time != 0) || (write_time != 0)) {
			disk_submit (disk_name, "disk_time",
					(derive_t)(read_time*1000), (derive_t)(write_time*1000));
		}
	}
	geom_stats_snapshot_free(snap);

#elif KERNEL_LINUX
	FILE *fh;
	char buffer[1024];
	
	char *fields[32];
	int numfields;
	int fieldshift = 0;

	int minor = 0;

	derive_t read_sectors  = 0;
	derive_t write_sectors = 0;

	derive_t read_ops      = 0;
	derive_t read_merged   = 0;
	derive_t read_time     = 0;
	derive_t write_ops     = 0;
	derive_t write_merged  = 0;
	derive_t write_time    = 0;
	gauge_t in_progress    = NAN;
	derive_t io_time       = 0;
	derive_t weighted_time = 0;
	int is_disk = 0;

	diskstats_t *ds, *pre_ds;

	if ((fh = fopen ("/proc/diskstats", "r")) == NULL)
	{
		fh = fopen ("/proc/partitions", "r");
		if (fh == NULL)
		{
			ERROR ("disk plugin: fopen (/proc/{diskstats,partitions}) failed.");
			return (-1);
		}

		/* Kernel is 2.4.* */
		fieldshift = 1;
	}

#if HAVE_LIBUDEV
	handle_udev = udev_new();
#endif

	while (fgets (buffer, sizeof (buffer), fh) != NULL)
	{
		char *disk_name;
		char *output_name;

		numfields = strsplit (buffer, fields, 32);

		if ((numfields != (14 + fieldshift)) && (numfields != 7))
			continue;

		minor = atoll (fields[1]);

		disk_name = fields[2 + fieldshift];

		for (ds = disklist, pre_ds = disklist; ds != NULL; pre_ds = ds, ds = ds->next)
			if (strcmp (disk_name, ds->name) == 0)
				break;

		if (ds == NULL)
		{
			if ((ds = (diskstats_t *) calloc (1, sizeof (diskstats_t))) == NULL)
				continue;

			if ((ds->name = strdup (disk_name)) == NULL)
			{
				free (ds);
				continue;
			}

			if (pre_ds == NULL)
				disklist = ds;
			else
				pre_ds->next = ds;
		}

		is_disk = 0;
		if (numfields == 7)
		{
			/* Kernel 2.6, Partition */
			read_ops      = atoll (fields[3]);
			read_sectors  = atoll (fields[4]);
			write_ops     = atoll (fields[5]);
			write_sectors = atoll (fields[6]);
		}
		else if (numfields == (14 + fieldshift))
		{
			read_ops  =  atoll (fields[3 + fieldshift]);
			write_ops =  atoll (fields[7 + fieldshift]);

			read_sectors  = atoll (fields[5 + fieldshift]);
			write_sectors = atoll (fields[9 + fieldshift]);

			if ((fieldshift == 0) || (minor == 0))
			{
				is_disk = 1;
				read_merged  = atoll (fields[4 + fieldshift]);
				read_time    = atoll (fields[6 + fieldshift]);
				write_merged = atoll (fields[8 + fieldshift]);
				write_time   = atoll (fields[10+ fieldshift]);

				in_progress = atof (fields[11 + fieldshift]);

				io_time       = atof (fields[12 + fieldshift]);
				weighted_time = atof (fields[13 + fieldshift]);
			}
		}
		else
		{
			DEBUG ("numfields = %i; => unknown file format.", numfields);
			continue;
		}

		{
			derive_t diff_read_sectors;
			derive_t diff_write_sectors;

		/* If the counter wraps around, it's only 32 bits.. */
			if (read_sectors < ds->read_sectors)
				diff_read_sectors = 1 + read_sectors
					+ (UINT_MAX - ds->read_sectors);
			else
				diff_read_sectors = read_sectors - ds->read_sectors;
			if (write_sectors < ds->write_sectors)
				diff_write_sectors = 1 + write_sectors
					+ (UINT_MAX - ds->write_sectors);
			else
				diff_write_sectors = write_sectors - ds->write_sectors;

			ds->read_bytes += 512 * diff_read_sectors;
			ds->write_bytes += 512 * diff_write_sectors;
			ds->read_sectors = read_sectors;
			ds->write_sectors = write_sectors;
		}

		/* Calculate the average time an io-op needs to complete */
		if (is_disk)
		{
			derive_t diff_read_ops;
			derive_t diff_write_ops;
			derive_t diff_read_time;
			derive_t diff_write_time;

			if (read_ops < ds->read_ops)
				diff_read_ops = 1 + read_ops
					+ (UINT_MAX - ds->read_ops);
			else
				diff_read_ops = read_ops - ds->read_ops;
			DEBUG ("disk plugin: disk_name = %s; read_ops = %"PRIi64"; "
					"ds->read_ops = %"PRIi64"; diff_read_ops = %"PRIi64";",
					disk_name,
					read_ops, ds->read_ops, diff_read_ops);

			if (write_ops < ds->write_ops)
				diff_write_ops = 1 + write_ops
					+ (UINT_MAX - ds->write_ops);
			else
				diff_write_ops = write_ops - ds->write_ops;

			if (read_time < ds->read_time)
				diff_read_time = 1 + read_time
					+ (UINT_MAX - ds->read_time);
			else
				diff_read_time = read_time - ds->read_time;

			if (write_time < ds->write_time)
				diff_write_time = 1 + write_time
					+ (UINT_MAX - ds->write_time);
			else
				diff_write_time = write_time - ds->write_time;

			if (diff_read_ops != 0)
				ds->avg_read_time += disk_calc_time_incr (
						diff_read_time, diff_read_ops);
			if (diff_write_ops != 0)
				ds->avg_write_time += disk_calc_time_incr (
						diff_write_time, diff_write_ops);

			ds->read_ops = read_ops;
			ds->read_time = read_time;
			ds->write_ops = write_ops;
			ds->write_time = write_time;
		} /* if (is_disk) */

		/* Don't write to the RRDs if we've just started.. */
		ds->poll_count++;
		if (ds->poll_count <= 2)
		{
			DEBUG ("disk plugin: (ds->poll_count = %i) <= "
					"(min_poll_count = 2); => Not writing.",
					ds->poll_count);
			continue;
		}

		if ((read_ops == 0) && (write_ops == 0))
		{
			DEBUG ("disk plugin: ((read_ops == 0) && "
					"(write_ops == 0)); => Not writing.");
			continue;
		}

		output_name = disk_name;

#if HAVE_LIBUDEV
		char *alt_name = disk_udev_attr_name (handle_udev, disk_name, conf_udev_name_attr);
		if (alt_name != NULL)
			output_name = alt_name;
#endif

		if ((ds->read_bytes != 0) || (ds->write_bytes != 0))
			disk_submit (output_name, "disk_octets",
					ds->read_bytes, ds->write_bytes);

		if ((ds->read_ops != 0) || (ds->write_ops != 0))
			disk_submit (output_name, "disk_ops",
					read_ops, write_ops);

		if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0))
			disk_submit (output_name, "disk_time",
					ds->avg_read_time, ds->avg_write_time);

		if (is_disk)
		{
			disk_submit (output_name, "disk_merged",
					read_merged, write_merged);
			submit_in_progress (output_name, in_progress);
			submit_io_time (output_name, io_time, weighted_time);
		} /* if (is_disk) */

#if HAVE_LIBUDEV
		/* release udev-based alternate name, if allocated */
		sfree (alt_name);
#endif
	} /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */

#if HAVE_LIBUDEV
	udev_unref(handle_udev);
#endif

	fclose (fh);
/* #endif defined(KERNEL_LINUX) */

#elif HAVE_LIBKSTAT
# if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME
#  define KIO_ROCTETS reads
#  define KIO_WOCTETS writes
#  define KIO_ROPS    nreads
#  define KIO_WOPS    nwrites
#  define KIO_RTIME   rtime
#  define KIO_WTIME   wtime
# elif HAVE_KSTAT_IO_T_NWRITTEN && HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_WTIME
#  define KIO_ROCTETS nread
#  define KIO_WOCTETS nwritten
#  define KIO_ROPS    reads
#  define KIO_WOPS    writes
#  define KIO_RTIME   rtime
#  define KIO_WTIME   wtime
# else
#  error "kstat_io_t does not have the required members"
# endif
	static kstat_io_t kio;
	int i;

	if (kc == NULL)
		return (-1);

	for (i = 0; i < numdisk; i++)
	{
		if (kstat_read (kc, ksp[i], &kio) == -1)
			continue;

		if (strncmp (ksp[i]->ks_class, "disk", 4) == 0)
		{
			disk_submit (ksp[i]->ks_name, "disk_octets",
					kio.KIO_ROCTETS, kio.KIO_WOCTETS);
			disk_submit (ksp[i]->ks_name, "disk_ops",
					kio.KIO_ROPS, kio.KIO_WOPS);
			/* FIXME: Convert this to microseconds if necessary */
			disk_submit (ksp[i]->ks_name, "disk_time",
					kio.KIO_RTIME, kio.KIO_WTIME);
		}
		else if (strncmp (ksp[i]->ks_class, "partition", 9) == 0)
		{
			disk_submit (ksp[i]->ks_name, "disk_octets",
					kio.KIO_ROCTETS, kio.KIO_WOCTETS);
			disk_submit (ksp[i]->ks_name, "disk_ops",
					kio.KIO_ROPS, kio.KIO_WOPS);
		}
	}
/* #endif defined(HAVE_LIBKSTAT) */

#elif defined(HAVE_LIBSTATGRAB)
	sg_disk_io_stats *ds;
# if HAVE_LIBSTATGRAB_0_90
	size_t disks;
# else
	int disks;
#endif
	int counter;
	char name[DATA_MAX_NAME_LEN];
	
	if ((ds = sg_get_disk_io_stats(&disks)) == NULL)
		return (0);
		
	for (counter=0; counter < disks; counter++) {
		strncpy(name, ds->disk_name, sizeof(name));
		name[sizeof(name)-1] = '\0'; /* strncpy doesn't terminate longer strings */
		disk_submit (name, "disk_octets", ds->read_bytes, ds->write_bytes);
		ds++;
	}
/* #endif defined(HAVE_LIBSTATGRAB) */

#elif defined(HAVE_PERFSTAT)
	derive_t read_sectors;
	derive_t write_sectors;
	derive_t read_time;
	derive_t write_time;
	derive_t read_ops;
	derive_t write_ops;
	perfstat_id_t firstpath;
	int rnumdisk;
	int i;

	if ((numdisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0)) < 0) 
	{
		char errbuf[1024];
		WARNING ("disk plugin: perfstat_disk: %s",
				sstrerror (errno, errbuf, sizeof (errbuf)));
		return (-1);
	}

	if (numdisk != pnumdisk || stat_disk==NULL) {
		if (stat_disk!=NULL) 
			free(stat_disk);
		stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t));
	} 
	pnumdisk = numdisk;

	firstpath.name[0]='\0';
	if ((rnumdisk = perfstat_disk(&firstpath, stat_disk, sizeof(perfstat_disk_t), numdisk)) < 0) 
	{
		char errbuf[1024];
		WARNING ("disk plugin: perfstat_disk : %s",
				sstrerror (errno, errbuf, sizeof (errbuf)));
		return (-1);
	}

	for (i = 0; i < rnumdisk; i++) 
	{
		read_sectors = stat_disk[i].rblks*stat_disk[i].bsize;
		write_sectors = stat_disk[i].wblks*stat_disk[i].bsize;
		disk_submit (stat_disk[i].name, "disk_octets", read_sectors, write_sectors);

		read_ops = stat_disk[i].xrate;
		write_ops = stat_disk[i].xfers - stat_disk[i].xrate;
		disk_submit (stat_disk[i].name, "disk_ops", read_ops, write_ops);

		read_time = stat_disk[i].rserv;
		read_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0;
		write_time = stat_disk[i].wserv;
		write_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0;
		disk_submit (stat_disk[i].name, "disk_time", read_time, write_time);
	}
#endif /* defined(HAVE_PERFSTAT) */

	return (0);
} /* int disk_read */