-/*
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
+ * vim: ts=4 sw=4 noet ai cindent syntax=c
+ *
* Conky, a system monitor, based on torsmo
*
* Any original torsmo code is licensed under the BSD license
* Please see COPYING for details
*
* Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
- * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
+ * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
* (see AUTHORS)
* All rights reserved.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * $Id$
*/
-#include "conky.h"
+#include "config.h"
+#include "conky.h" /* text_buffer_size */
+#include "core.h"
+#include "logging.h"
+#include "diskio.h"
+#include "common.h"
+#include "specials.h"
+#include "text_object.h"
+#include <stdlib.h>
#include <limits.h>
-/* The following ifdefs were adapted from gkrellm */
-#include <linux/major.h>
-
-#if !defined(MD_MAJOR)
-#define MD_MAJOR 9
-#endif
-
-#if !defined(LVM_BLK_MAJOR)
-#define LVM_BLK_MAJOR 58
-#endif
-
-#if !defined(NBD_MAJOR)
-#define NBD_MAJOR 43
-#endif
-
-static struct diskio_stat diskio_stats_[MAX_DISKIO_STATS];
-struct diskio_stat *diskio_stats = diskio_stats_;
+#include <sys/stat.h>
+
+/* this is the root of all per disk stats,
+ * also containing the totals. */
+struct diskio_stat stats = {
+ .next = NULL,
+ .sample = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ .sample_read = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ .sample_write = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ .current = 0,
+ .current_read = 0,
+ .current_write = 0,
+ .last = UINT_MAX,
+ .last_read = UINT_MAX,
+ .last_write = UINT_MAX,
+};
void clear_diskio_stats(void)
{
- unsigned i;
- for(i = 0; i < MAX_DISKIO_STATS; i++) {
- if (diskio_stats[i].dev) {
- free(diskio_stats[i].dev);
- diskio_stats[i].dev = 0;
- }
+ struct diskio_stat *cur;
+ while (stats.next) {
+ cur = stats.next;
+ stats.next = stats.next->next;
+ if (cur->dev)
+ free(cur->dev);
+ free(cur);
}
}
struct diskio_stat *prepare_diskio_stat(const char *s)
{
- struct diskio_stat *new = 0;
- unsigned i;
- FILE *fp;
- int found = 0;
- char device[text_buffer_size], fbuf[text_buffer_size];
- static int rep = 0;
- /* lookup existing or get new */
- for (i = 0; i < MAX_DISKIO_STATS; i++) {
- if (diskio_stats[i].dev) {
- if (strcmp(diskio_stats[i].dev, s) == 0) {
- return &diskio_stats[i];
- }
- } else {
- new = &diskio_stats[i];
- break;
- }
- }
- /* new dev */
- if (!new) {
- ERR("too many diskio stats");
- return 0;
- }
- if (new->dev) {
- free(new->dev);
- new->dev = 0;
- }
- new->dev = strndup(s, text_buffer_size);
+ struct stat sb;
+ char stat_name[text_buffer_size], device_name[text_buffer_size];
+ struct diskio_stat *cur = &stats;
+
+ if (!s)
+ return &stats;
- /*
- * check that device actually exists
- */
+ strncpy(device_name, s, text_buffer_size);
- if (!(fp = open_file("/proc/diskstats", &rep))) {
- ERR("cannot read from /proc/diskstats");
- return 0;
+ snprintf(stat_name, text_buffer_size, "/dev/%s", device_name);
+
+ if (stat(stat_name, &sb)) {
+ NORM_ERR("diskio device '%s' does not exist", s);
}
- while (!feof(fp)) {
- fgets(fbuf, text_buffer_size, fp);
- if (sscanf(fbuf, "%*u %*u %255s %*u %*u %*u %*u %*u %*u %*u", device)) {
- // check for device match
- if (strncmp(new->dev, device, 256) == 0) {
- found = 1;
- break;
- }
+ /* lookup existing */
+ while (cur->next) {
+ cur = cur->next;
+ if (!strcmp(cur->dev, device_name)) {
+ return cur;
}
}
- fclose(fp);
- fp = 0;
- if (!found) {
- ERR("diskio device '%s' does not exist", s);
- return 0;
- }
- new->current = 0;
- new->current_read = 0;
- new ->current_write = 0;
- new->last = UINT_MAX;
- new->last_read = UINT_MAX;
- new->last_write = UINT_MAX;
- return new;
+
+ /* no existing found, make a new one */
+ cur->next = calloc(1, sizeof(struct diskio_stat));
+ cur = cur->next;
+ cur->dev = strndup(device_name, text_buffer_size);
+ cur->last = UINT_MAX;
+ cur->last_read = UINT_MAX;
+ cur->last_write = UINT_MAX;
+
+ return cur;
}
-void update_diskio(void)
+void parse_diskio_arg(struct text_object *obj, const char *arg)
{
- static unsigned int last = UINT_MAX;
- static unsigned int last_read = UINT_MAX;
- static unsigned int last_write = UINT_MAX;
- FILE *fp;
- static int rep = 0;
+ obj->data.opaque = prepare_diskio_stat(arg);
+}
- char buf[512], devbuf[64];
- int i;
- unsigned int major, minor;
- unsigned int current = 0;
- unsigned int current_read = 0;
- unsigned int current_write = 0;
- unsigned int reads, writes = 0;
- int col_count = 0;
- int tot, tot_read, tot_write;
-
- if (!(fp = open_file("/proc/diskstats", &rep))) {
- info.diskio_value = 0;
+/* dir indicates the direction:
+ * -1: read
+ * 0: read + write
+ * 1: write
+ */
+static void print_diskio_dir(struct text_object *obj, int dir, char *p, int p_max_size)
+{
+ struct diskio_stat *diskio = obj->data.opaque;
+ double val;
+
+ if (!diskio)
return;
- }
- /* read reads and writes from all disks (minor = 0), including cd-roms
- * and floppies, and sum them up */
- while (!feof(fp)) {
- fgets(buf, 512, fp);
- col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
- &minor, devbuf, &reads, &writes);
- /* ignore subdevices (they have only 3 matching entries in their line)
- * and virtual devices (LVM, network block devices, RAM disks, Loopback)
- *
- * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
- if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
- && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
- current += reads + writes;
- current_read += reads;
- current_write += writes;
- } else {
- col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
- &major, &minor, devbuf, &reads, &writes);
- if (col_count != 5) {
- continue;
- }
- }
- for (i = 0; i < MAX_DISKIO_STATS; i++) {
- if (diskio_stats[i].dev &&
- strncmp(devbuf, diskio_stats[i].dev, text_buffer_size) == 0) {
- diskio_stats[i].current =
- (reads + writes - diskio_stats[i].last) / 2;
- diskio_stats[i].current_read =
- (reads - diskio_stats[i].last_read) / 2;
- diskio_stats[i].current_write =
- (writes - diskio_stats[i].last_write) / 2;
- if (reads + writes < diskio_stats[i].last) {
- diskio_stats[i].current = 0;
- }
- if (reads < diskio_stats[i].last_read) {
- diskio_stats[i].current_read = 0;
- diskio_stats[i].current = diskio_stats[i].current_write;
- }
- if (writes < diskio_stats[i].last_write) {
- diskio_stats[i].current_write = 0;
- diskio_stats[i].current = diskio_stats[i].current_read;
- }
- diskio_stats[i].last = reads + writes;
- diskio_stats[i].last_read = reads;
- diskio_stats[i].last_write = writes;
- }
- }
- }
+ if (dir < 0)
+ val = diskio->current_read;
+ else if (dir == 0)
+ val = diskio->current;
+ else
+ val = diskio->current_write;
- /* since the values in /proc/diststats are absolute, we have to substract
- * our last reading. The numbers stand for "sectors read", and we therefore
- * have to divide by two to get KB */
- tot = ((double) (current - last) / 2);
- tot_read = ((double) (current_read - last_read) / 2);
- tot_write = ((double) (current_write - last_write) / 2);
+ /* TODO: move this correction from kB to kB/s elsewhere
+ * (or get rid of it??) */
+ human_readable((val / update_interval) * 1024LL, p, p_max_size);
+}
+
+void print_diskio(struct text_object *obj, char *p, int p_max_size)
+{
+ print_diskio_dir(obj, 0, p, p_max_size);
+}
+
+void print_diskio_read(struct text_object *obj, char *p, int p_max_size)
+{
+ print_diskio_dir(obj, -1, p, p_max_size);
+}
- if (last_read > current_read) {
- tot_read = 0;
+void print_diskio_write(struct text_object *obj, char *p, int p_max_size)
+{
+ print_diskio_dir(obj, 1, p, p_max_size);
+}
+
+#ifdef X11
+void parse_diskiograph_arg(struct text_object *obj, const char *arg)
+{
+ char *buf = 0;
+ buf = scan_graph(obj, arg, 0);
+
+ obj->data.opaque = prepare_diskio_stat(dev_name(buf));
+ if (buf)
+ free(buf);
+}
+
+static void print_diskiograph_dir(struct text_object *obj, int dir, char *p, int p_max_size)
+{
+ struct diskio_stat *diskio = obj->data.opaque;
+ double val;
+
+ if (!diskio)
+ return;
+
+ if (!p_max_size)
+ return;
+
+ if (dir < 0)
+ val = diskio->current_read;
+ else if (dir == 0)
+ val = diskio->current;
+ else
+ val = diskio->current_write;
+
+ new_graph(obj, p, p_max_size, val);
+}
+
+void print_diskiograph(struct text_object *obj, char *p, int p_max_size)
+{
+ print_diskiograph_dir(obj, 0, p, p_max_size);
+}
+
+void print_diskiograph_read(struct text_object *obj, char *p, int p_max_size)
+{
+ print_diskiograph_dir(obj, -1, p, p_max_size);
+}
+
+void print_diskiograph_write(struct text_object *obj, char *p, int p_max_size)
+{
+ print_diskiograph_dir(obj, 1, p, p_max_size);
+}
+#endif /* X11 */
+
+void update_diskio_values(struct diskio_stat *ds,
+ unsigned int reads, unsigned int writes)
+{
+ int i;
+ double sum=0, sum_r=0, sum_w=0;
+
+ if (reads < ds->last_read || writes < ds->last_write) {
+ /* counter overflow or reset - rebase to sane values */
+ ds->last = reads+writes;
+ ds->last_read = reads;
+ ds->last_write = writes;
}
- if (last_write > current_write) {
- tot_write = 0;
+ /* since the values in /proc/diskstats are absolute, we have to subtract
+ * our last reading. The numbers stand for "sectors read", and we therefore
+ * have to divide by two to get KB */
+ ds->sample_read[0] = (reads - ds->last_read) / 2;
+ ds->sample_write[0] = (writes - ds->last_write) / 2;
+ ds->sample[0] = ds->sample_read[0] + ds->sample_write[0];
+
+ /* compute averages */
+ for (i = 0; i < (signed) info.diskio_avg_samples; i++) {
+ sum += ds->sample[i];
+ sum_r += ds->sample_read[i];
+ sum_w += ds->sample_write[i];
}
-
- if (last > current) {
- /* we hit this either if it's the very first time we run this, or
- * when /proc/diskstats overflows; while 0 is not correct, it's at
- * least not way off */
- tot = 0;
+ ds->current = sum / (double) info.diskio_avg_samples;
+ ds->current_read = sum_r / (double) info.diskio_avg_samples;
+ ds->current_write = sum_w / (double) info.diskio_avg_samples;
+
+ /* shift sample history */
+ for (i = info.diskio_avg_samples-1; i > 0; i--) {
+ ds->sample[i] = ds->sample[i-1];
+ ds->sample_read[i] = ds->sample_read[i-1];
+ ds->sample_write[i] = ds->sample_write[i-1];
}
- last = current;
- last_read = current_read;
- last_write = current_write;
-
- info.diskio_value = tot;
- info.diskio_read_value = tot_read;
- info.diskio_write_value = tot_write;
- fclose(fp);
+ /* save last */
+ ds->last_read = reads;
+ ds->last_write = writes;
+ ds->last = ds->last_read + ds->last_write;
}