since DEV_NAME() is used when calling, this check is needless
[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-2008 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  *  $Id$
28  */
29
30 #include "conky.h"
31 #include <limits.h>
32 /* The following ifdefs were adapted from gkrellm */
33 #include <linux/major.h>
34
35 #if !defined(MD_MAJOR)
36 #define MD_MAJOR 9
37 #endif
38
39 #if !defined(LVM_BLK_MAJOR)
40 #define LVM_BLK_MAJOR 58
41 #endif
42
43 #if !defined(NBD_MAJOR)
44 #define NBD_MAJOR 43
45 #endif
46
47 static struct diskio_stat diskio_stats_[MAX_DISKIO_STATS];
48 struct diskio_stat *diskio_stats = diskio_stats_;
49
50 void clear_diskio_stats(void)
51 {
52         unsigned i;
53         for(i = 0; i < MAX_DISKIO_STATS; i++) {
54                 if (diskio_stats[i].dev) {
55                         free(diskio_stats[i].dev);
56                         diskio_stats[i].dev = 0;
57                 }
58         }
59 }
60
61 struct diskio_stat *prepare_diskio_stat(const char *s)
62 {
63         struct diskio_stat *new = 0;
64         unsigned i;
65         FILE *fp;
66         int found = 0;
67         char device[text_buffer_size], fbuf[text_buffer_size];
68         static int rep = 0;
69         /* lookup existing or get new */
70         for (i = 0; i < MAX_DISKIO_STATS; i++) {
71                 if (diskio_stats[i].dev) {
72                         if (strcmp(diskio_stats[i].dev, s) == 0) {
73                                 return &diskio_stats[i];
74                         }
75                 } else {
76                         new = &diskio_stats[i];
77                         break;
78                 }
79         }
80         /* new dev */
81         if (!new) {
82                 ERR("too many diskio stats");
83                 return 0;
84         }
85         if (new->dev) {
86                 free(new->dev);
87                 new->dev = 0;
88         }
89         new->dev = strndup(s, text_buffer_size);
90
91         /*
92          * check that device actually exists
93          */
94
95         if (!(fp = open_file("/proc/diskstats", &rep))) {
96                 ERR("cannot read from /proc/diskstats");
97                 return 0;
98         }
99
100         while (!feof(fp)) {
101                 fgets(fbuf, text_buffer_size, fp);
102                 if (sscanf(fbuf, "%*u %*u %255s %*u %*u %*u %*u %*u %*u %*u", device)) {
103                         // check for device match
104                         if (strncmp(new->dev, device, 256) == 0) {
105                                 found = 1;
106                                 break;
107                         }
108                 }
109         }
110         fclose(fp);
111         fp = 0;
112         if (!found) {
113                 ERR("diskio device '%s' does not exist", s);
114                 return 0;
115         }
116         new->current = 0;
117         new->current_read = 0;
118         new ->current_write = 0;
119         new->last = UINT_MAX;
120         new->last_read = UINT_MAX;
121         new->last_write = UINT_MAX;
122         return new;
123 }
124
125 void update_diskio(void)
126 {
127         static unsigned int last = UINT_MAX;
128         static unsigned int last_read = UINT_MAX;
129         static unsigned int last_write = UINT_MAX;
130         FILE *fp;
131         static int rep = 0;
132
133         char buf[512], devbuf[64];
134         int i;
135         unsigned int major, minor;
136         unsigned int current = 0;
137         unsigned int current_read = 0;
138         unsigned int current_write = 0;
139         unsigned int reads, writes = 0;
140         int col_count = 0;
141         int tot, tot_read, tot_write;
142
143         if (!(fp = open_file("/proc/diskstats", &rep))) {
144                 info.diskio_value = 0;
145                 return;
146         }
147
148         /* read reads and writes from all disks (minor = 0), including cd-roms
149          * and floppies, and sum them up */
150         while (!feof(fp)) {
151                 fgets(buf, 512, fp);
152                 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
153                         &minor, devbuf, &reads, &writes);
154                 /* ignore subdevices (they have only 3 matching entries in their line)
155                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
156                  *
157                  * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
158                 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
159                                 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
160                         current += reads + writes;
161                         current_read += reads;
162                         current_write += writes;
163                 } else {
164                         col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
165                                 &major, &minor, devbuf, &reads, &writes);
166                         if (col_count != 5) {
167                                 continue;
168                         }
169                 }
170                 for (i = 0; i < MAX_DISKIO_STATS; i++) {
171                         if (diskio_stats[i].dev &&
172                                         strncmp(devbuf, diskio_stats[i].dev, text_buffer_size) == 0) {
173                                 diskio_stats[i].current =
174                                         (reads + writes - diskio_stats[i].last) / 2;
175                                 diskio_stats[i].current_read =
176                                         (reads - diskio_stats[i].last_read) / 2;
177                                 diskio_stats[i].current_write =
178                                         (writes - diskio_stats[i].last_write) / 2;
179                                 if (reads + writes < diskio_stats[i].last) {
180                                         diskio_stats[i].current = 0;
181                                 }
182                                 if (reads < diskio_stats[i].last_read) {
183                                         diskio_stats[i].current_read = 0;
184                                         diskio_stats[i].current = diskio_stats[i].current_write;
185                                 }
186                                 if (writes < diskio_stats[i].last_write) {
187                                         diskio_stats[i].current_write = 0;
188                                         diskio_stats[i].current = diskio_stats[i].current_read;
189                                 }
190                                 diskio_stats[i].last = reads + writes;
191                                 diskio_stats[i].last_read = reads;
192                                 diskio_stats[i].last_write = writes;
193                         }
194                 }
195         }
196
197         /* since the values in /proc/diststats are absolute, we have to substract
198          * our last reading. The numbers stand for "sectors read", and we therefore
199          * have to divide by two to get KB */
200         tot = ((double) (current - last) / 2);
201         tot_read = ((double) (current_read - last_read) / 2);
202         tot_write = ((double) (current_write - last_write) / 2);
203
204         if (last_read > current_read) {
205                 tot_read = 0;
206         }
207         if (last_write > current_write) {
208                 tot_write = 0;
209         }
210
211         if (last > current) {
212                 /* we hit this either if it's the very first time we run this, or
213                  * when /proc/diskstats overflows; while 0 is not correct, it's at
214                  * least not way off */
215                 tot = 0;
216         }
217         last = current;
218         last_read = current_read;
219         last_write = current_write;
220
221         info.diskio_value = tot;
222         info.diskio_read_value = tot_read;
223         info.diskio_write_value = tot_write;
224
225         fclose(fp);
226 }
227