1/*
2 KSysGuard, the KDE System Guard
3
4 Copyright (c) 2001 Tobias Koenig <tokoe@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of version 2 of the GNU General Public
8 License as published by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19*/
20
21#include <config-workspace.h>
22
23#include <mntent.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/statvfs.h>
28#include <time.h>
29#include <unistd.h>
30#include <ctype.h>
31
32#include "Command.h"
33#include "ccont.h"
34#include "diskstat.h"
35#include "ksysguardd.h"
36
37typedef struct {
38 char device[ 256 ];
39 char mntpnt[ 256 ];
40 struct statvfs statvfs;
41} DiskInfo;
42
43static CONTAINER DiskStatList = 0;
44static CONTAINER OldDiskStatList = 0;
45static struct SensorModul* DiskStatSM;
46char *getMntPnt( const char* cmd );
47
48static void sanitize(char *str) {
49 if(str == NULL)
50 return;
51 while (*str != 0) {
52 if(*str == '\t' || *str == '\n' || *str == '\r' || *str == ' ' || !isascii(*str) )
53 *str = '?';
54 ++str;
55 }
56}
57
58char *getMntPnt( const char* cmd )
59{
60 static char device[ 1025 ];
61 char* ptr;
62
63 memset( device, 0, sizeof( device ) );
64 sscanf( cmd, "partitions%1024s", device );
65
66 ptr = (char*)rindex( device, '/' );
67 *ptr = '\0';
68
69 return (char*)device;
70}
71
72/* ----------------------------- public part ------------------------------- */
73
74static char monitor[ 1024 ];
75static void registerMonitors(const char* mntpnt) {
76 snprintf( monitor, sizeof( monitor ), "partitions%s/usedspace", mntpnt );
77 registerMonitor( monitor, "integer", printDiskStatUsed, printDiskStatUsedInfo, DiskStatSM );
78 snprintf( monitor, sizeof( monitor ), "partitions%s/freespace", mntpnt );
79 registerMonitor( monitor, "integer", printDiskStatFree, printDiskStatFreeInfo, DiskStatSM );
80 snprintf( monitor, sizeof( monitor ), "partitions%s/filllevel", mntpnt );
81 registerMonitor( monitor, "integer", printDiskStatPercent, printDiskStatPercentInfo, DiskStatSM );
82}
83static void removeMonitors(const char* mntpnt) {
84 snprintf( monitor, sizeof( monitor ), "partitions%s/usedspace", mntpnt );
85 removeMonitor( monitor );
86 snprintf( monitor, sizeof( monitor ), "partitions%s/freespace", mntpnt );
87 removeMonitor( monitor );
88 snprintf( monitor, sizeof( monitor ), "partitions%s/filllevel", mntpnt );
89 removeMonitor( monitor );
90}
91
92void initDiskStat( struct SensorModul* sm )
93{
94 DiskInfo* disk_info;
95
96 DiskStatList = NULL;
97 OldDiskStatList = NULL;
98 DiskStatSM = sm;
99 if ( updateDiskStat() < 0 )
100 return;
101
102 registerMonitor( "partitions/list", "listview", printDiskStat, printDiskStatInfo, sm );
103
104 for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) {
105 registerMonitors(disk_info->mntpnt);
106 }
107}
108
109void exitDiskStat( void )
110{
111 DiskInfo* disk_info;
112
113 removeMonitor( "partitions/list" );
114
115 for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) {
116 removeMonitors(disk_info->mntpnt);
117 }
118
119 destr_ctnr( DiskStatList, free );
120 if(OldDiskStatList)
121 destr_ctnr( OldDiskStatList, free );
122}
123
124void checkDiskStat( void )
125{
126 updateDiskStat();
127 DiskInfo* disk_info_new;
128 DiskInfo* disk_info_old;
129 int changed = 0;
130 for ( disk_info_new = first_ctnr( DiskStatList ); disk_info_new; disk_info_new = next_ctnr( DiskStatList ) ) {
131 int found = 0;
132 for ( disk_info_old = first_ctnr( OldDiskStatList ); disk_info_old; disk_info_old = next_ctnr( OldDiskStatList ) ) {
133 if(strcmp(disk_info_new->mntpnt, disk_info_old->mntpnt) == 0) {
134 free( remove_ctnr( OldDiskStatList ) );
135 found = 1;
136 continue;
137 }
138 }
139 if(!found) {
140 /* register all the devices that did not exist before*/
141 registerMonitors(disk_info_new->mntpnt);
142 changed++;
143 }
144 }
145 /*Now remove all the devices that do not exist anymore*/
146 for ( disk_info_old = first_ctnr( OldDiskStatList ); disk_info_old; disk_info_old = next_ctnr( OldDiskStatList ) ) {
147 removeMonitors(disk_info_old->mntpnt);
148 changed++;
149 }
150 destr_ctnr( OldDiskStatList, free );
151 OldDiskStatList = NULL;
152 updateDiskStat();
153 if(changed)
154 print_error( "RECONFIGURE" ); /*Let ksysguard know that we've added a sensor*/
155}
156
157int updateDiskStat( void )
158{
159 DiskInfo *disk_info;
160 FILE *fh;
161 struct mntent *mnt_info;
162
163 if ( ( fh = setmntent( "/etc/mtab", "r" ) ) == NULL ) {
164 print_error( "Cannot open \'/etc/mtab\'!\n" );
165 return -1;
166 }
167 if(OldDiskStatList == 0) {
168 OldDiskStatList = DiskStatList;
169 DiskStatList = new_ctnr();
170 } else {
171 empty_ctnr(DiskStatList);
172 }
173
174 while ( ( mnt_info = getmntent( fh ) ) != NULL ) {
175 /*
176 * An entry which device name doesn't start with a '/' is
177 * either a dummy file system or a network file system.
178 * Add special handling for smbfs and cifs as is done by
179 * coreutils as well.
180 */
181 if ( (mnt_info->mnt_fsname[0] != '/') ||
182 !strcmp( mnt_info->mnt_type, "smbfs" ) ||
183 !strcmp( mnt_info->mnt_type, "cifs" ) ||
184 !strcmp( mnt_info->mnt_type, "proc" ) ||
185 !strcmp( mnt_info->mnt_type, "devfs" ) ||
186 !strcmp( mnt_info->mnt_type, "usbfs" ) ||
187 !strcmp( mnt_info->mnt_type, "sysfs" ) ||
188 !strcmp( mnt_info->mnt_type, "tmpfs" ) ||
189 !strcmp( mnt_info->mnt_type, "devpts" ) )
190 continue; /* Skip these file systems */
191
192 if ( ( disk_info = (DiskInfo *)malloc( sizeof( DiskInfo ) ) ) == NULL )
193 continue;
194
195 memset( disk_info, 0, sizeof( DiskInfo ) );
196
197 if ( statvfs( mnt_info->mnt_dir, &(disk_info->statvfs) ) < 0 )
198 continue;
199
200 strncpy( disk_info->device, mnt_info->mnt_fsname, sizeof( disk_info->device ) );
201 disk_info->device[ sizeof(disk_info->device) -1] = 0;
202
203 strncpy( disk_info->mntpnt, mnt_info->mnt_dir, sizeof( disk_info->mntpnt ) );
204 disk_info->mntpnt[ sizeof(disk_info->mntpnt) - 1] = 0;
205 sanitize(disk_info->mntpnt);
206
207 push_ctnr( DiskStatList, disk_info );
208 }
209 endmntent( fh );
210
211 return 0;
212}
213
214int calculatePercentageUsed( unsigned long totalSizeKB, unsigned long available) {
215 if (!available)
216 return 0;
217
218 unsigned long totalSizeKBdividedBy100 = (50 + totalSizeKB )/ 100;
219 if (!totalSizeKBdividedBy100)
220 return 0;
221
222 int percentageUsed = 100 - available / totalSizeKBdividedBy100; /* Percentage is 1 - available / totalSizeKB, meaning that we count root-only reserved space as "used" here */
223 /* If we have rounded down to 0%, make it 1%, like "df" does */
224 if (percentageUsed == 0)
225 return 1;
226 return percentageUsed;
227}
228
229void printDiskStat( const char* cmd )
230{
231 DiskInfo* disk_info;
232
233 (void)cmd;
234 for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) {
235 /* See man statvfs(2) for meaning of fields */
236 unsigned long totalSizeKB = disk_info->statvfs.f_blocks * (disk_info->statvfs.f_frsize/1024);
237 unsigned long usedKB = totalSizeKB - (disk_info->statvfs.f_bfree * (disk_info->statvfs.f_bsize/1024)); /* used is the total size minus free blocks including those for root only */
238 unsigned long available = disk_info->statvfs.f_bavail * (disk_info->statvfs.f_bsize/1024); /* available is only those for non-root. So available + used != total because some are reserved for root */
239 int percentageUsed = calculatePercentageUsed(totalSizeKB, available);
240 output( "%s\t%ld\t%ld\t%ld\t%d\t%s\n",
241 disk_info->device,
242 totalSizeKB,
243 usedKB,
244 available,
245 percentageUsed,
246 disk_info->mntpnt );
247 }
248
249 output( "\n" );
250}
251
252void printDiskStatInfo( const char* cmd )
253{
254 (void)cmd;
255 output( "Device\tSize\tUsed\tAvailable\tUsed %%\tMount point\nM\tKB\tKB\tKB\t%%\ts\n" );
256}
257
258void printDiskStatUsed( const char* cmd )
259{
260 char *mntpnt = (char*)getMntPnt( cmd );
261 DiskInfo* disk_info;
262
263 for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) {
264 if ( !strcmp( mntpnt, disk_info->mntpnt ) ) {
265 unsigned long totalSizeKB = disk_info->statvfs.f_blocks * (disk_info->statvfs.f_frsize/1024);
266 unsigned long usedKB = totalSizeKB - (disk_info->statvfs.f_bfree * (disk_info->statvfs.f_bsize/1024)); /* used is the total size minus free blocks including those for root only */
267 output( "%ld\n", usedKB );
268 }
269 }
270
271 output( "\n" );
272}
273
274void printDiskStatUsedInfo( const char* cmd )
275{
276 (void)cmd;
277 output( "Used\t0\t0\tKB\n" );
278}
279
280void printDiskStatFree( const char* cmd )
281{
282 char *mntpnt = (char*)getMntPnt( cmd );
283 DiskInfo* disk_info;
284
285 for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) {
286 if ( !strcmp( mntpnt, disk_info->mntpnt ) ) {
287 unsigned long available = disk_info->statvfs.f_bavail * (disk_info->statvfs.f_bsize/1024); /* available is only those for non-root. So available + used != total because some are reserved for root */
288 output( "%ld\n", available );
289 }
290 }
291 output( "\n" );
292}
293
294void printDiskStatFreeInfo( const char* cmd )
295{
296 (void)cmd;
297 output( "Available\t0\t0\tKB\n" );
298}
299
300void printDiskStatPercent( const char* cmd )
301{
302 char *mntpnt = (char*)getMntPnt( cmd );
303 DiskInfo* disk_info;
304
305 for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) {
306 if ( !strcmp( mntpnt, disk_info->mntpnt ) ) {
307 unsigned long totalSizeKB = disk_info->statvfs.f_blocks * (disk_info->statvfs.f_frsize/1024);
308 unsigned long available = disk_info->statvfs.f_bavail * (disk_info->statvfs.f_bsize/1024); /* available is only those for non-root. So available + used != total because some are reserved for root */
309
310 int percentageUsed = calculatePercentageUsed(totalSizeKB, available);
311 output( "%d\n", percentageUsed );
312 }
313 }
314
315 output( "\n" );
316}
317
318void printDiskStatPercentInfo( const char* cmd )
319{
320 (void)cmd;
321 output( "Percentage Used\t0\t100\t%%\n" );
322}
323