2
KSysGuard, the KDE System Guard
4
Copyright (c) 2007 Greg Martyn <greg.martyn@gmail.com>, John Tapsell <tapsell@kde.org>
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of version 2 or later of the GNU General Public
8
License as published by the Free Software Foundation.
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.
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.
21
#include "ksysguardd.h"
24
#include <string.h> /* for strlen, strcat and strcmp */
25
#include <stdio.h> /* for sprintf */
26
#include <sys/types.h> /* for open */
27
#include <sys/stat.h> /* for open */
28
#include <fcntl.h> /* for open */
29
#include <unistd.h> /* for read, close, exec, fork */
30
#include <stdlib.h> /* for exit */
31
#include <sys/wait.h> /* for wait :) */
32
#include <stdbool.h> /* for bool */
33
#include "ccont.h" /* for CONTAINER */
35
#define MDSTATBUFSIZE (1 * 1024)
36
#define MDADMSTATBUFSIZE (2 * 1024)
37
#define ARRAYNAMELEN 32
38
#define ARRAYNAMELENSTRING "32"
40
static struct SensorModul* StatSM;
42
static CONTAINER ArrayInfos = 0;
43
char mdstatBuf[ MDSTATBUFSIZE ]; /* Buffer for /proc/mdstat */
45
typedef struct Disks {
46
char *name; /* e.g. hda1 */
47
char status; /* e.g. F for failed, S for spare. U for fully synced, _ for syncing */
48
int index; /* number in the array */
49
struct Disks *next; /* A pointer to the next disk in this array. NULL if no next */
56
/* from /sbin/mdadm --detail /dev/ArrayName */
57
bool ArraySizeIsAlive;
58
bool ArraySizeIsRegistered;
61
bool UsedDeviceSizeIsAlive;
62
bool UsedDeviceSizeIsRegistered;
65
bool PreferredMinorIsAlive;
66
bool PreferredMinorIsRegistered;
69
/* from /proc/mdstat */
70
char ArrayName[ ARRAYNAMELEN +1];
73
int NumRaidDevices; /* Number of 'useful' disks. Included working and spare disks, but not failed. */
75
int TotalDevices; /* Total number of devices, including failed and spare disks */
77
int ActiveDevices; /* Active means it's fully synced, and working, so not a spare or failed */
79
int WorkingDevices; /* Working means it's in the raid (not a spare or failed) but it may or may not be fully synced. Active - Working = number being synced */
81
int FailedDevices; /* Number of failed devices */
83
int SpareDevices; /* Number of spare devices, WE DO NOT Include Failed devices here, unlike mdadm. */
85
int devnum; /* Raid array number. e.g. if ArrayName is "md0", then devnum=0 */
86
bool ArrayActive; /* Whether this raid is active */
87
char *level; /* Raid1, Raid2, etc */
88
char *pattern; /* U for up, _ for down */
89
int ResyncingPercent; /* -1 if not resyncing, otherwise between 0 to 100 */
90
bool IsCurrentlyReSyncing; /* True if currently resyncing - */
91
Disks *first_disk; /* A linked list of hard disks */
94
static int ArrayInfoEqual( void* s1, void* s2 )
96
/* Returns 0 if both ArrayInfos have the same name. */
97
return strncmp( ((ArrayInfo*)s1)->ArrayName, ((ArrayInfo*)s2)->ArrayName,ARRAYNAMELEN );
100
void printArrayAttribute( const char* cmd ) {
103
ArrayInfo* foundArray;
106
if ( sscanf(cmd, "SoftRaid/%[^/]/%39s", key.ArrayName, attribute) == 2 ) {
107
if ( ( idx = search_ctnr( ArrayInfos, ArrayInfoEqual, &key ) ) == 0 ) {
108
foundArray = get_ctnr( ArrayInfos, idx );
110
if ( strcmp( attribute, "NumBlocks" ) == 0 )
111
output( "%d\n", foundArray->NumBlocks );
112
else if ( strcmp( attribute, "ArraySizeKB" ) == 0 )
113
output( "%d\n", foundArray->ArraySizeKB );
114
else if ( strcmp( attribute, "UsedDeviceSizeKB" ) == 0 )
115
output( "%d\n", foundArray->UsedDeviceSizeKB );
116
else if ( strcmp( attribute, "NumRaidDevices" ) == 0 )
117
output( "%d\n", foundArray->NumRaidDevices );
118
else if ( strcmp( attribute, "TotalDevices" ) == 0 )
119
output( "%d\n", foundArray->TotalDevices );
120
else if ( strcmp( attribute, "PreferredMinor" ) == 0 )
121
output( "%d\n", foundArray->PreferredMinor );
122
else if ( strcmp( attribute, "ActiveDevices" ) == 0 )
123
output( "%d\n", foundArray->ActiveDevices );
124
else if ( strcmp( attribute, "WorkingDevices" ) == 0 )
125
output( "%d\n", foundArray->WorkingDevices );
126
else if ( strcmp( attribute, "FailedDevices" ) == 0 )
127
output( "%d\n", foundArray->FailedDevices );
128
else if ( strcmp( attribute, "SpareDevices" ) == 0 )
129
output( "%d\n", foundArray->SpareDevices );
130
else if( strcmp( attribute, "DeviceNumber" ) == 0 )
131
output( "%d\n", foundArray->devnum);
132
else if( strcmp( attribute, "ResyncingPercent" ) == 0 )
133
output( "%d\n", foundArray->ResyncingPercent);
134
else if( strcmp( attribute, "RaidType" ) == 0 )
135
output( "%s\n", foundArray->level);
136
else if( strcmp( attribute, "DiskInfo" ) == 0 ) {
137
Disks *disk = foundArray->first_disk;
139
output( "%s\t%d\t%c\n", disk->name, disk->index, disk->status);
155
void printArrayAttributeInfo( const char* cmd ) {
158
ArrayInfo* foundArray;
161
if ( sscanf(cmd, "SoftRaid/%[^/]/%39s", key.ArrayName, attribute) == 2 ) {
162
if ( ( idx = search_ctnr( ArrayInfos, ArrayInfoEqual, &key ) ) == 0 ) {
163
foundArray = get_ctnr( ArrayInfos, idx );
165
if ( strcmp( attribute, "NumBlocks?" ) == 0 )
166
output( "Num blocks\t0\t0\t\n" );
167
else if ( strcmp( attribute, "ArraySizeKB?" ) == 0 )
168
output( "Array size\t0\t0\tKB\n" );
169
else if ( strcmp( attribute, "UsedDeviceSizeKB?" ) == 0 )
170
output( "Used Device Size\t0\t0\tKB\n" );
171
else if ( strcmp( attribute, "NumRaidDevices?" ) == 0 )
172
output( "Total number of raid devices\t0\t0\t\n" );
173
else if ( strcmp( attribute, "TotalDevices?" ) == 0 )
174
output( "Total number of devices\t0\t0\t\n" );
175
else if ( strcmp( attribute, "PreferredMinor?" ) == 0 )
176
output( "The preferred minor\t0\t0\t\n" );
177
else if ( strcmp( attribute, "ActiveDevices?" ) == 0 )
178
output( "Number of active devices\t0\t%d\t\n", foundArray->TotalDevices );
179
else if ( strcmp( attribute, "WorkingDevices?" ) == 0 )
180
output( "Number of working devices\t0\t%d\t\n", foundArray->TotalDevices );
181
else if ( strcmp( attribute, "FailedDevices?" ) == 0 )
182
output( "Number of failed devices\t0\t%d\t\n", foundArray->TotalDevices );
183
else if ( strcmp( attribute, "SpareDevices?" ) == 0 )
184
output( "Number of spare devices\t0\t%d\t\n", foundArray->TotalDevices );
185
else if( strcmp( attribute, "DeviceNumber?" ) == 0 )
186
output( "Raid Device Number\t0\t0\t\n");
187
else if( strcmp( attribute, "ResyncingPercent?" ) == 0 )
188
output( "Resyncing Percentage Done. -1 if not resyncing\t-1\t100\t%%\n");
189
else if( strcmp( attribute, "RaidType?" ) == 0 )
190
output( "Type of RAID array\n");
191
else if( strcmp( attribute, "DiskInfo?") == 0 )
192
output( "Disk Name\tIndex\tStatus\ns\td\tS\n");
204
void getMdadmDetail( ArrayInfo* MyArray ) {
209
char sensorName[128];
210
char arrayDevice[ARRAYNAMELEN + 5];
212
char lineBuf[ 1024 ];
213
char mdadmStatBuf[ MDADMSTATBUFSIZE ]; /* Buffer for mdadm --detail */
219
perror("Could not create a pipe to launch mdadm.");
224
if((ChildPID = fork()) == -1)
226
perror("Could not fork to launch mdadm.");
230
/* Child will execute the program, parent will listen. */
235
/* Child will execute the program, parent will listen. */
236
/* Close stdout, duplicate the input side of pipe to stdout */
238
/* Close output side of pipe */
242
snprintf( arrayDevice, sizeof( arrayDevice ), "/dev/%s", MyArray->ArrayName );
243
execl ("/sbin/mdadm", "mdadm", "--detail", arrayDevice, (char *)0);
244
exit(0); /* In case /sbin/mdadm isn't found */
245
/* Child is now dead, as per our request */
250
/* Close input side of pipe */
253
waitpid( ChildPID, 0, 0);
255
/* Fill mdadmStatBuf with pipe's output */
256
nbytes = read( fd[0], mdadmStatBuf, MDADMSTATBUFSIZE-1 );
258
mdadmStatBuf[nbytes] = '\0';
260
/* Now, go through mdadmStatBuf line by line. Register monitors along the way */
261
sprintf( format, "%%%d[^\n]\n", (int)sizeof( lineBuf ) - 1 );
262
mdadmStatBufP = mdadmStatBuf;
263
while (sscanf(mdadmStatBufP, format, lineBuf) != EOF) {
264
lineBuf[sizeof(lineBuf) - 1] = '\0';
265
mdadmStatBufP += strlen(lineBuf) + 1; /* move mdadmStatBufP to next line */
267
if ( sscanf(lineBuf, " Array Size : %d", &MyArray->ArraySizeKB) == 1 ) {
268
MyArray->ArraySizeIsAlive = true;
269
if ( !MyArray->ArraySizeIsRegistered ) {
270
sprintf(sensorName, "SoftRaid/%s/ArraySizeKB", MyArray->ArrayName);
271
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
273
MyArray->ArraySizeIsRegistered = true;
277
/* Versions of mdadm prior to 2.6 used "Device Size" instead of "Used Dev Size"
279
else if ( ( sscanf(lineBuf, " Device Size : %d", &MyArray->UsedDeviceSizeKB) == 1 ) ||
280
( sscanf(lineBuf, " Used Dev Size : %d", &MyArray->UsedDeviceSizeKB) == 1 ) ) {
281
MyArray->UsedDeviceSizeIsAlive = true;
282
if ( !MyArray->UsedDeviceSizeIsRegistered ) {
283
sprintf(sensorName, "SoftRaid/%s/UsedDeviceSizeKB", MyArray->ArrayName);
284
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
285
MyArray->UsedDeviceSizeIsRegistered = true;
289
else if ( sscanf(lineBuf, "Preferred Minor : %d", &MyArray->PreferredMinor) == 1 ) {
290
MyArray->PreferredMinorIsAlive = true;
291
if ( !MyArray->PreferredMinorIsRegistered ) {
292
sprintf(sensorName, "SoftRaid/%s/PreferredMinor", MyArray->ArrayName);
293
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
294
MyArray->PreferredMinorIsRegistered = true;
299
/* Note: Don't test NumBlocksIsAlive, because it hasn't been set yet */
300
if ( (!MyArray->ArraySizeIsAlive && MyArray->ArraySizeIsRegistered ) ||
301
(!MyArray->UsedDeviceSizeIsAlive && MyArray->UsedDeviceSizeIsRegistered ) ||
302
(!MyArray->PreferredMinorIsAlive && MyArray->PreferredMinorIsRegistered )
304
print_error( "RECONFIGURE" );
305
log_error( "Soft raid device disappeared" );
310
void openMdstatFile() {
314
mdstatBuf[ 0 ] = '\0';
315
if ( ( fd = open( "/proc/mdstat", O_RDONLY ) ) < 0 )
316
return; /* couldn't open /proc/mdstat */
318
n = read( fd, mdstatBuf, MDSTATBUFSIZE - 1 );
321
if ( n == MDSTATBUFSIZE - 1 || n <= 0 ) {
322
log_error( "Internal buffer too small to read \'/proc/mdstat\'" );
327
mdstatBuf[ n ] = '\0';
330
ArrayInfo *getOrCreateArrayInfo(char *array_name, int array_name_length) {
334
/*We have the array name. see if we already have a record for it*/
335
strncpy(key.ArrayName, array_name, array_name_length);
336
key.ArrayName[array_name_length]='\0';
337
if ( ( idx = search_ctnr( ArrayInfos, ArrayInfoEqual, &key ) ) == 0 ) {
338
/* Found an existing array device */
339
MyArray = get_ctnr( ArrayInfos, idx );
342
/* Found a new array device. Create a data structure for it. */
343
MyArray = calloc(1,sizeof (ArrayInfo));
344
if (MyArray == NULL) {
345
/* Memory could not be allocated, so print an error and exit. */
346
fprintf(stderr, "Could not allocate memory\n");
349
strcpy( MyArray->ArrayName, key.ArrayName );
350
/* Add this array to our list of array devices */
351
push_ctnr(ArrayInfos, MyArray);
352
char sensorName[128];
353
sprintf(sensorName, "SoftRaid/%s/NumBlocks", MyArray->ArrayName);
354
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
356
sprintf(sensorName, "SoftRaid/%s/TotalDevices", MyArray->ArrayName);
357
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
359
sprintf(sensorName, "SoftRaid/%s/FailedDevices", MyArray->ArrayName);
360
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
362
sprintf(sensorName, "SoftRaid/%s/SpareDevices", MyArray->ArrayName);
363
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
365
sprintf(sensorName, "SoftRaid/%s/NumRaidDevices", MyArray->ArrayName);
366
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
368
sprintf(sensorName, "SoftRaid/%s/WorkingDevices", MyArray->ArrayName);
369
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
371
sprintf(sensorName, "SoftRaid/%s/ActiveDevices", MyArray->ArrayName);
372
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
374
sprintf(sensorName, "SoftRaid/%s/RaidType", MyArray->ArrayName);
375
registerMonitor(sensorName, "string", printArrayAttribute, printArrayAttributeInfo, StatSM );
377
sprintf(sensorName, "SoftRaid/%s/DeviceNumber", MyArray->ArrayName);
378
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
380
sprintf(sensorName, "SoftRaid/%s/ResyncingPercent", MyArray->ArrayName);
381
registerMonitor(sensorName, "integer", printArrayAttribute, printArrayAttributeInfo, StatSM );
383
sprintf(sensorName, "SoftRaid/%s/DiskInfo", MyArray->ArrayName);
384
registerMonitor(sensorName, "listview", printArrayAttribute, printArrayAttributeInfo, StatSM );
390
bool scanForArrays() {
393
int current_word_length = 0;
397
/* Mark all data as dead. As we find data, we'll mark it alive. */
398
for ( MyArray = first_ctnr( ArrayInfos ); MyArray; MyArray = next_ctnr( ArrayInfos ) ) {
399
MyArray->Alive = false;
405
current_word = mdstatBufP = mdstatBuf;
407
/* Process values from /proc/mdstat */
410
while(mdstatBufP[0] != '\0') {
413
/*advanced one line at a time*/
414
while( mdstatBufP[0] != '\0' && mdstatBufP[0] != '\n') mdstatBufP++;
419
if( mdstatBufP[0] == '\0') break;
420
current_word_length = 0;
421
current_word = mdstatBufP;
424
if(mdstatBufP[0]=='\n')
427
if(mdstatBufP[0]=='#') /*skip comments */
429
if (strncmp(current_word, "Personalities", sizeof("Personalities")-1)==0)
431
if (strncmp(current_word, "read_ahead", sizeof("read_ahead")-1)==0)
433
if (strncmp(current_word, "unused", sizeof("unused")-1)==0)
435
/* Better be an md line.. */
436
if (strncmp(current_word, "md", 2)!= 0)
439
if (strncmp(current_word, "md_d", 4) == 0)
440
devnum = -1-strtoul(current_word+4, NULL, 10);
441
else /* it's md0 etc */
442
devnum = strtoul(current_word+2, NULL, 10);
443
while( mdstatBufP[0] != '\0' &&mdstatBufP[0] != '\n' && mdstatBufP[0] != ' ' && mdstatBufP[0] != '\t') {
444
/*find the end of the word*/
446
current_word_length++;
449
MyArray = getOrCreateArrayInfo(current_word, current_word_length);
450
MyArray->Alive = true;
451
getMdadmDetail ( MyArray );
452
MyArray->level = MyArray->pattern= NULL;
453
MyArray->ResyncingPercent = -1;
454
MyArray->IsCurrentlyReSyncing = false;
455
MyArray->devnum = devnum;
456
MyArray->ArrayActive = false;
457
MyArray->TotalDevices = MyArray->SpareDevices = MyArray->FailedDevices = 0;
458
MyArray->NumBlocks = 0;
460
Disks *disk = MyArray->first_disk;
461
MyArray->first_disk = NULL;
470
/* In mdstat, we have something that looks like:
472
md0 : active raid1 sda1[0] sdb1[1]
473
312568576 blocks [2/2] [UU]
474
md1 : active raid1 sda2[0] sdb2[1]
475
452568576 blocks [2/2] [UU]
477
We have so far read in the "md0" bit, and now want to continue reading the details for this raid group until
478
we reach the next raid group which we note as starting with a non whitespace.
486
while( mdstatBufP[0] != '\0' && ( (mdstatBufP[0] == '\n' && (mdstatBufP[1] == ' ' || mdstatBufP[1] == '\t')) || mdstatBufP[0] == ' ' || mdstatBufP[0] == '\t')) {
487
mdstatBufP++; /*Remove any whitespace first*/
489
if( mdstatBufP[0] == '\0' || mdstatBufP[0] == '\n') {
490
break; /*we are now at the end of the file or line. Break this for loop*/
493
current_word=mdstatBufP; /*we are now pointing to the first non-whitespace of a word*/
494
current_word_length=0;
495
while( mdstatBufP[0] != '\0' && mdstatBufP[0] != '\n' && mdstatBufP[0] != ' ' && mdstatBufP[0] != '\t') {
496
/*find the end of the word. We do this now so that we know the length of the word*/
498
current_word_length++;
506
if (strncmp(current_word, "active", sizeof("active")-1)==0)
507
MyArray->ArrayActive = true;
508
else if (strncmp(current_word, "inactive", sizeof("inactive")-1)==0)
509
MyArray->ArrayActive = false;
510
else if (MyArray->ArrayActive >=0 && MyArray->level == NULL && current_word[0] != '(' && current_word[0] != ':' /*readonly*/) {
511
MyArray->level = strndup(current_word, current_word_length);
514
} else if (sscanf(current_word, "%d blocks ", &temp_int) == 1 ) {
515
MyArray->NumBlocks = temp_int; /* We have to do it via a temp_int variable otherwise we'll end up with nonsence if it's not found */
516
} else if (in_devs && strncmp(current_word, "blocks", sizeof("blocks")-1)==0)
519
#warning in_devs cannot be != 0!! (CID 3228)
521
else if (in_devs && strncmp(current_word, "md", 2)==0) {
522
/* This has an md device as a component. Maybe we should note this or something*/
523
} else if(sscanf(current_word, "%[^[ ][%d]%[^ ]", buffer, &harddisk_index, status) >= 2) {
524
/*Each device in the raid has an index. We can find the total number of devices in the raid by
525
simply finding the device with the highest index + 1. */
526
if(harddisk_index >= MyArray->TotalDevices) MyArray->TotalDevices = harddisk_index+1;
527
Disks *new_disk = malloc(sizeof(Disks));
528
new_disk->name = strdup(buffer);
529
new_disk->index = harddisk_index;
532
if(status[0] == '(') {
533
new_disk->status = status[1];
534
if(status[1] == 'S') /*Spare hard disk*/
535
MyArray->SpareDevices++;
536
else if(status[1] == 'F')
537
MyArray->FailedDevices++;
539
new_disk->status = 'U';
541
/* insert the new disk into the linked list of disks*/
542
new_disk->next = MyArray->first_disk;
543
MyArray->first_disk = new_disk;
545
MyArray->NumRaidDevices = MyArray->TotalDevices - MyArray->FailedDevices;
546
status[0]=0; /*make sure we zero it again for next time*/
547
} else if (!MyArray->pattern &&
548
current_word[0] == '[' &&
549
(current_word[1] == 'U' || current_word[1] == '_')) {
550
MyArray->pattern = strndup(current_word+1, current_word_length-1);
552
if (MyArray->pattern[current_word_length-2]==']')
553
MyArray->pattern[current_word_length-2] = '\0';
554
MyArray->ActiveDevices = MyArray->TotalDevices - MyArray->SpareDevices - MyArray->FailedDevices;
556
MyArray->WorkingDevices=0;
561
if(current_word[0] == 'U')
562
MyArray->WorkingDevices++;
563
else if(current_word[0] == '_') {
564
Disks *disk = MyArray->first_disk;
566
if(disk->index == index) {
567
if(disk->status == 'U')
568
disk->status = '_'; /* The disk hasn't failed, but is syncing */
577
} else if (MyArray->ResyncingPercent == -1 &&
578
strncmp(current_word, "re", 2)== 0 &&
579
current_word[current_word_length-1] == '%' &&
580
(eq=strchr(current_word, '=')) != NULL ) {
581
MyArray->ResyncingPercent = atoi(eq+1);
582
if (strncmp(current_word,"resync", 4)==0)
583
MyArray->IsCurrentlyReSyncing = true;
584
} else if (MyArray->ResyncingPercent == -1 &&
585
strncmp(current_word, "resync", 4)==0) {
586
MyArray->IsCurrentlyReSyncing = true;
587
} else if (MyArray->ResyncingPercent == -1 &&
588
current_word[0] >= '0' &&
589
current_word[0] <= '9' &&
590
current_word[current_word_length-1] == '%') {
591
MyArray->ResyncingPercent = atoi(current_word);
593
/*ignore anything not understood*/
597
/* Look for dead arrays, and for NumBlocksIsRegistered */
598
for ( MyArray = first_ctnr( ArrayInfos ); MyArray; MyArray = next_ctnr( ArrayInfos ) ) {
599
if ( MyArray->Alive == false ) {
600
print_error( "RECONFIGURE" );
602
log_error( "Soft raid device disappeared" );
609
/* =========== Public part =========== */
611
void initSoftRaid( struct SensorModul* sm ) {
614
ArrayInfos = new_ctnr();
618
void exitSoftRaid( void ) {
619
destr_ctnr( ArrayInfos, free );
622
int updateSoftRaid( void ) {