6a53a073889a5cb6553d5db22bb09d5c6f6af701
[monky] / src / diskio.c
1 /*
2  * Conky, a system monitor, based on torsmo
3  *
4  * Any original torsmo code is licensed under the BSD license
5  *
6  * All code written since the fork of torsmo is licensed under the GPL
7  *
8  * Please see COPYING for details
9  *
10  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
11  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
12  * (see AUTHORS)
13  * All rights reserved.
14  *
15  * This program is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  *
27  */
28
29 #include "config.h"
30 #include "conky.h"      /* text_buffer_size */
31 #include "logging.h"
32 #include "diskio.h"
33 #include "common.h"
34 #include <stdlib.h>
35 #include <limits.h>
36 /* The following ifdefs were adapted from gkrellm */
37 #include <linux/major.h>
38
39 #if !defined(MD_MAJOR)
40 #define MD_MAJOR 9
41 #endif
42
43 #if !defined(LVM_BLK_MAJOR)
44 #define LVM_BLK_MAJOR 58
45 #endif
46
47 #if !defined(NBD_MAJOR)
48 #define NBD_MAJOR 43
49 #endif
50
51 /* this is the root of all per disk stats,
52  * also containing the totals. */
53 static struct diskio_stat stats = {
54         .next = NULL,
55         .sample = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
56         .sample_read = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
57         .sample_write = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
58         .current = 0,
59         .current_read = 0,
60         .current_write = 0,
61         .last = UINT_MAX,
62         .last_read = UINT_MAX,
63         .last_write = UINT_MAX,
64 };
65
66 void clear_diskio_stats(void)
67 {
68         struct diskio_stat *cur;
69         while (stats.next) {
70                 cur = stats.next;
71                 stats.next = stats.next->next;
72                 free(cur);
73         }
74 }
75
76 struct diskio_stat *prepare_diskio_stat(const char *s)
77 {
78         struct diskio_stat *cur = &stats;
79
80         if (!s)
81                 return &stats;
82
83         /* lookup existing */
84         while (cur->next) {
85                 cur = cur->next;
86                 if (!strcmp(cur->dev, s))
87                         return cur;
88         }
89
90         /* no existing found, make a new one */
91         cur->next = malloc(sizeof(struct diskio_stat));
92         cur = cur->next;
93         memset(cur, 0, sizeof(struct diskio_stat));
94         cur->dev = strndup(s, text_buffer_size);
95         cur->last = UINT_MAX;
96         cur->last_read = UINT_MAX;
97         cur->last_write = UINT_MAX;
98         return cur;
99 }
100
101 static void update_diskio_values(struct diskio_stat *ds,
102                 unsigned int reads, unsigned int writes)
103 {
104         int i;
105         double sum=0, sum_r=0, sum_w=0;
106
107         if (reads < ds->last_read || writes < ds->last_write) {
108                 /* counter overflow or reset - rebase to sane values */
109                 ds->last = reads+writes;
110                 ds->last_read = reads;
111                 ds->last_write = writes;
112         }
113         /* since the values in /proc/diskstats are absolute, we have to substract
114          * our last reading. The numbers stand for "sectors read", and we therefore
115          * have to divide by two to get KB */
116         ds->sample_read[0] = (reads - ds->last_read) / 2;
117         ds->sample_write[0] = (writes - ds->last_write) / 2;
118         ds->sample[0] = ds->sample_read[0] + ds->sample_write[0];
119
120         /* compute averages */
121         for (i = 0; i < (signed) info.diskio_avg_samples; i++) {
122                 sum += ds->sample[i];
123                 sum_r += ds->sample_read[i];
124                 sum_w += ds->sample_write[i];
125         }
126         ds->current = sum / (double) info.diskio_avg_samples;
127         ds->current_read = sum_r / (double) info.diskio_avg_samples;
128         ds->current_write = sum_w / (double) info.diskio_avg_samples;
129
130         /* shift sample history */
131         for (i = info.diskio_avg_samples-1; i > 0; i--) {
132                 ds->sample[i] = ds->sample[i-1];
133                 ds->sample_read[i] = ds->sample_read[i-1];
134                 ds->sample_write[i] = ds->sample_write[i-1];
135         }
136
137         /* save last */
138         ds->last_read = reads;
139         ds->last_write = writes;
140         ds->last = ds->last_read + ds->last_write;
141 }
142
143 void update_diskio(void)
144 {
145         FILE *fp;
146         static int rep = 0;
147
148         struct diskio_stat *cur;
149         char buf[512], devbuf[64];
150         unsigned int major, minor;
151         unsigned int reads, writes;
152         unsigned int total_reads=0, total_writes=0;
153         int col_count = 0;
154
155         stats.current = 0;
156         stats.current_read = 0;
157         stats.current_write = 0;
158
159         if (!(fp = open_file("/proc/diskstats", &rep))) {
160                 return;
161         }
162
163         /* read reads and writes from all disks (minor = 0), including cd-roms
164          * and floppies, and sum them up */
165         while (fgets(buf, 512, fp)) {
166                 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
167                         &minor, devbuf, &reads, &writes);
168                 /* ignore subdevices (they have only 3 matching entries in their line)
169                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
170                  *
171                  * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
172                 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
173                                 && major != RAMDISK_MAJOR && major != LOOP_MAJOR && minor==0) {
174                         total_reads += reads;
175                         total_writes += writes;
176                 } else {
177                         col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
178                                 &major, &minor, devbuf, &reads, &writes);
179                         if (col_count != 5) {
180                                 continue;
181                         }
182                 }
183                 cur = stats.next;
184                 while (cur && strcmp(devbuf, cur->dev))
185                         cur = cur->next;
186
187                 if (cur)
188                         update_diskio_values(cur, reads, writes);
189         }
190         update_diskio_values(&stats, total_reads, total_writes);
191         fclose(fp);
192 }
193