Example #1
0
/*
 * Lists md(4) disks. Is used also as a query routine, since it handles XML
 * interface. 'units' can be NULL for listing memory disks. It might be
 * coma-separated string containing md(4) disk names. 'opt' distinguished
 * between list and query mode.
 */
static int
md_list(const char *units, int opt, const char *fflag)
{
	struct gmesh gm;
	struct gprovider *pp;
	struct gconf *gc;
	struct gident *gid;
	struct devstat *gsp;
	struct ggeom *gg;
	struct gclass *gcl;
	void *sq;
	int retcode, ffound, ufound;
	char *length;
	const char *type, *file, *label;

	type = file = length = NULL;

	retcode = geom_gettree(&gm);
	if (retcode != 0)
		return (-1);
	retcode = geom_stats_open();
	if (retcode != 0)
		return (-1);
	sq = geom_stats_snapshot_get();
	if (sq == NULL)
		return (-1);

	ffound = ufound = 0;
	while ((gsp = geom_stats_snapshot_next(sq)) != NULL) {
		gid = geom_lookupid(&gm, gsp->id);
		if (gid == NULL)
			continue;
		if (gid->lg_what == ISPROVIDER) {
			pp = gid->lg_ptr;
			gg = pp->lg_geom;
			gcl = gg->lg_class;
			if (strcmp(gcl->lg_name, CLASS_NAME_MD) != 0)
				continue;
			if ((opt & OPT_UNIT) && (units != NULL)) {
				retcode = md_find(units, pp->lg_name);
				if (retcode != 1)
					continue;
				else
					ufound = 1;
			}
			gc = &pp->lg_config;
			type = geom_config_get(gc, "type");
			if (type != NULL && (strcmp(type, "vnode") == 0 ||
			    strcmp(type, "preload") == 0)) {
				file = geom_config_get(gc, "file");
				if (fflag != NULL &&
				    strcmp(fflag, file) != 0)
					continue;
				else
					ffound = 1;
			} else if (fflag != NULL)
					continue;
			if (nflag && strncmp(pp->lg_name, MD_NAME, 2) == 0)
				printf("%s", pp->lg_name + 2);
			else
				printf("%s", pp->lg_name);

			if (opt & OPT_VERBOSE ||
			    ((opt & OPT_UNIT) && fflag == NULL)) {
				length = geom_config_get(gc, "length");
				printf("\t%s\t", type);
				if (length != NULL)
					md_prthumanval(length);
				if (file == NULL)
					file = "-";
				printf("\t%s", file);
				file = NULL;
				label = geom_config_get(gc, "label");
				if (label == NULL)
					label = "";
				printf("\t%s", label);
			}
			opt |= OPT_DONE;
			if ((opt & OPT_LIST) && !(opt & OPT_VERBOSE))
				printf(" ");
			else
				printf("\n");
		}
	}
	if ((opt & OPT_LIST) && (opt & OPT_DONE) && !(opt & OPT_VERBOSE))
		printf("\n");
	/* XXX: Check if it's enough to clean everything. */
	geom_stats_snapshot_free(sq);
	if (opt & OPT_UNIT) {
		if (((fflag == NULL) && ufound) ||
		    ((fflag == NULL) && (units != NULL) && ufound) ||
		    ((fflag != NULL) && ffound) ||
		    ((fflag != NULL) && (units != NULL) && ufound && ffound))
			return (0);
	} else if (opt & OPT_LIST) {
		if ((fflag == NULL) ||
		    ((fflag != NULL) && ffound))
			return (0);
	}
	return (-1);
}
Example #2
0
int
main(int argc, char **argv)
{
	int error, i, quit;
	int curx, cury, maxx, maxy, line_len, loop, max_flen;
	struct devstat *gsp, *gsq;
	void *sp, *sq;
	double dt;
	struct timespec tp, tq;
	struct gmesh gmp;
	struct gprovider *pp;
	struct gconsumer *cp;
	struct gident *gid;
	regex_t f_re, tmp_f_re;
	short cf, cb;
	char *p;
	char f_s[100], pf_s[100], tmp_f_s[100];
	const char *line;
	long double ld[13];
	uint64_t u64;
	EditLine *el;
	History *hist;
	HistEvent hist_ev;

	hist = NULL;
	el = NULL;
	maxx = -1;
	curx = -1;
	loop = 1;
	/* Turn on batch mode if output is not tty. */
	if (!isatty(fileno(stdout)))
		flag_b = 1;

	f_s[0] = '\0';
	while ((i = getopt(argc, argv, "abdcf:I:op")) != -1) {
		switch (i) {
		case 'a':
			flag_a = 1;
			break;
		case 'b':
			flag_b = 1;
			break;
		case 'c':
			flag_c = 1;
			break;
		case 'd':
			flag_d = 1;
			break;
		case 'f':
			if (strlen(optarg) > sizeof(f_s) - 1)
				errx(EX_USAGE, "Filter string too long");
			if (regcomp(&f_re, optarg, REG_EXTENDED) != 0)
				errx(EX_USAGE,
				    "Invalid filter - see re_format(7)");
			strncpy(f_s, optarg, sizeof(f_s));
			break;
		case 'o':
			flag_o = 1;
			break;
		case 'I':
			p = NULL;
			i = strtoul(optarg, &p, 0);
			if (p == optarg || errno == EINVAL ||
			    errno == ERANGE) {
				errx(1, "Invalid argument to -I");
			} else if (!strcmp(p, "s"))
				i *= 1000000;
			else if (!strcmp(p, "ms"))
				i *= 1000;
			else if (!strcmp(p, "us"))
				i *= 1;
			flag_I = i;
			break;
		case 'p':
			flag_p = 1;
			break;
		case '?':
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;
	if (argc != 0)
		usage();

	i = geom_gettree(&gmp);
	if (i != 0)
		err(1, "geom_gettree = %d", i);
	error = geom_stats_open();
	if (error)
		err(1, "geom_stats_open()");
	sq = NULL;
	sq = geom_stats_snapshot_get();
	if (sq == NULL)
		err(1, "geom_stats_snapshot()");
	if (!flag_b) {
		/* Setup curses */
		initscr();
		start_color();
		use_default_colors();
		pair_content(0, &cf, &cb);
		init_pair(1, COLOR_GREEN, cb);
		init_pair(2, COLOR_MAGENTA, cb);
		init_pair(3, COLOR_RED, cb);
		cbreak();
		noecho();
		nonl();
		nodelay(stdscr, 1);
		intrflush(stdscr, FALSE);
		keypad(stdscr, TRUE);
		/* Setup libedit */
		hist = history_init();
		if (hist == NULL)
			err(EX_SOFTWARE, "history_init()");
		history(hist, &hist_ev, H_SETSIZE, 100);
		el = el_init("gstat", stdin, stdout, stderr);
		if (el == NULL)
			err(EX_SOFTWARE, "el_init");
		el_set(el, EL_EDITOR, "emacs");
		el_set(el, EL_SIGNAL, 1);
		el_set(el, EL_HIST, history, hist);
		el_set(el, EL_PROMPT, el_prompt);
		if (f_s[0] != '\0')
			history(hist, &hist_ev, H_ENTER, f_s);
	}
	geom_stats_snapshot_timestamp(sq, &tq);
	for (quit = 0; !quit;) {
		sp = geom_stats_snapshot_get();
		if (sp == NULL)
			err(1, "geom_stats_snapshot()");
		geom_stats_snapshot_timestamp(sp, &tp);
		dt = tp.tv_sec - tq.tv_sec;
		dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9;
		tq = tp;
	
		geom_stats_snapshot_reset(sp);
		geom_stats_snapshot_reset(sq);
		move(0,0);
		PRINTMSG("dT: %5.3fs  w: %.3fs", dt, (float)flag_I / 1000000);
		if (f_s[0] != '\0') {
			PRINTMSG("  filter: ");
			if (!flag_b) {
				getyx(stdscr, cury, curx);
				getmaxyx(stdscr, maxy, maxx);
			}
			strncpy(pf_s, f_s, sizeof(pf_s));
			max_flen = maxx - curx - 1;
			if ((int)strlen(f_s) > max_flen && max_flen >= 0) {
				if (max_flen > 3)
					pf_s[max_flen - 3] = '.';
				if (max_flen > 2)
					pf_s[max_flen - 2] = '.';
				if (max_flen > 1)
					pf_s[max_flen - 1] = '.';
				pf_s[max_flen] = '\0';
			}
			PRINTMSG("%s", pf_s);
		}
		PRINTMSG("\n");
		PRINTMSG(" L(q)  ops/s   ");
		PRINTMSG(" r/s   kBps   ms/r   ");
		PRINTMSG(" w/s   kBps   ms/w   ");
		if (flag_d)
			PRINTMSG(" d/s   kBps   ms/d   ");
		if (flag_o)
			PRINTMSG(" o/s   ms/o   ");
		PRINTMSG("%%busy Name\n");
		for (;;) {
			gsp = geom_stats_snapshot_next(sp);
			gsq = geom_stats_snapshot_next(sq);
			if (gsp == NULL || gsq == NULL)
				break;
			if (gsp->id == NULL)
				continue;
			gid = geom_lookupid(&gmp, gsp->id);
			if (gid == NULL) {
				geom_deletetree(&gmp);
				i = geom_gettree(&gmp);
				if (i != 0)
					err(1, "geom_gettree = %d", i);
				gid = geom_lookupid(&gmp, gsp->id);
			}
			if (gid == NULL)
				continue;
			if (gid->lg_what == ISCONSUMER && !flag_c)
				continue;
			if (flag_p && gid->lg_what == ISPROVIDER &&
			   ((struct gprovider *)(gid->lg_ptr))->lg_geom->lg_rank != 1)
				continue;
			/* Do not print past end of window */
			if (!flag_b) {
				getyx(stdscr, cury, curx);
				if (curx > 0)
					continue;
			}
			if ((gid->lg_what == ISPROVIDER
			    || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') {
				pp = gid->lg_ptr;
				if ((regexec(&f_re, pp->lg_name, 0, NULL, 0)
				     != 0))
				  continue;
			}
			if (gsp->sequence0 != gsp->sequence1) {
				PRINTMSG("*\n");
				continue;
			}
			devstat_compute_statistics(gsp, gsq, dt, 
			    DSM_QUEUE_LENGTH, &u64,
			    DSM_TRANSFERS_PER_SECOND, &ld[0],

			    DSM_TRANSFERS_PER_SECOND_READ, &ld[1],
			    DSM_MB_PER_SECOND_READ, &ld[2],
			    DSM_MS_PER_TRANSACTION_READ, &ld[3],

			    DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4],
			    DSM_MB_PER_SECOND_WRITE, &ld[5],
			    DSM_MS_PER_TRANSACTION_WRITE, &ld[6],

			    DSM_BUSY_PCT, &ld[7],

			    DSM_TRANSFERS_PER_SECOND_FREE, &ld[8],
			    DSM_MB_PER_SECOND_FREE, &ld[9],
			    DSM_MS_PER_TRANSACTION_FREE, &ld[10],

			    DSM_TRANSFERS_PER_SECOND_OTHER, &ld[11],
			    DSM_MS_PER_TRANSACTION_OTHER, &ld[12],

			    DSM_NONE);

			if (flag_a && ld[7] < 0.1) {
				*gsq = *gsp;
				continue;
			}

			PRINTMSG(" %4ju", (uintmax_t)u64);
			PRINTMSG(" %6.0f", (double)ld[0]);
			PRINTMSG(" %6.0f", (double)ld[1]);
			PRINTMSG(" %6.0f", (double)ld[2] * 1024);
			if (ld[3] > 1e3) 
				PRINTMSG(" %6.0f", (double)ld[3]);
			else
				PRINTMSG(" %6.1f", (double)ld[3]);
			PRINTMSG(" %6.0f", (double)ld[4]);
			PRINTMSG(" %6.0f", (double)ld[5] * 1024);
			if (ld[6] > 1e3) 
				PRINTMSG(" %6.0f", (double)ld[6]);
			else
				PRINTMSG(" %6.1f", (double)ld[6]);

			if (flag_d) {
				PRINTMSG(" %6.0f", (double)ld[8]);
				PRINTMSG(" %6.0f", (double)ld[9] * 1024);
				if (ld[10] > 1e3) 
					PRINTMSG(" %6.0f", (double)ld[10]);
				else
					PRINTMSG(" %6.1f", (double)ld[10]);
			}

			if (flag_o) {
				PRINTMSG(" %6.0f", (double)ld[11]);
				if (ld[12] > 1e3) 
					PRINTMSG(" %6.0f", (double)ld[12]);
				else
					PRINTMSG(" %6.1f", (double)ld[12]);
			}

			if (ld[7] > 80)
				i = 3;
			else if (ld[7] > 50)
				i = 2;
			else 
				i = 1;
			if (!flag_b)
				attron(COLOR_PAIR(i));
			PRINTMSG(" %6.1lf", (double)ld[7]);
			if (!flag_b) {
				attroff(COLOR_PAIR(i));
				PRINTMSG("|");
			} else
				PRINTMSG(" ");
			if (gid == NULL) {
				PRINTMSG(" ??");
			} else if (gid->lg_what == ISPROVIDER) {
				pp = gid->lg_ptr;
				PRINTMSG(" %s", pp->lg_name);
			} else if (gid->lg_what == ISCONSUMER) {
				cp = gid->lg_ptr;
				PRINTMSG(" %s/%s/%s",
				    cp->lg_geom->lg_class->lg_name,
				    cp->lg_geom->lg_name,
				    cp->lg_provider->lg_name);
			}
			if (!flag_b)
				clrtoeol();
			PRINTMSG("\n");
			*gsq = *gsp;
		}
		geom_stats_snapshot_free(sp);
		if (flag_b) {
			/* We loop extra to make sure we get the information. */
			if (!loop)
				break;
			loop = 0;
			usleep(flag_I);
			continue;
		}
		getyx(stdscr, cury, curx);
		getmaxyx(stdscr, maxy, maxx);
		clrtobot();
		if (maxy - 1 <= cury)
			move(maxy - 1, 0);
		refresh();
		usleep(flag_I);
		while((i = getch()) != ERR) {
			switch (i) {
			case '>':
				flag_I *= 2;
				break;
			case '<':
				flag_I /= 2;
				if (flag_I < 1000)
					flag_I = 1000;
				break;
			case 'c':
				flag_c = !flag_c;
				break;
			case 'f':
				move(0,0);
				clrtoeol();
				refresh();
				line = el_gets(el, &line_len);
				if (line == NULL)
					err(1, "el_gets");
				if (line_len > 1)
					history(hist, &hist_ev, H_ENTER, line);
				strncpy(tmp_f_s, line, sizeof(f_s));
				if ((p = strchr(tmp_f_s, '\n')) != NULL)
					*p = '\0';
				/*
				 * We have to clear since we messed up
				 * curses idea of the screen by using
				 * libedit.
				 */
				clear();
				refresh();
				if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED)
				    != 0) {
					move(0, 0);
					printw("Invalid filter");
					refresh();
					sleep(1);
				} else {
					strncpy(f_s, tmp_f_s, sizeof(f_s));
					f_re = tmp_f_re;
				}
				break;
			case 'F':
				f_s[0] = '\0';
				break;
			case 'q':
				quit = 1;
				break;
			default:
				break;
			}
		}
	}

	if (!flag_b) {
		endwin();
		el_end(el);
	}
	exit(EX_OK);
}
Example #3
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 */