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