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