]>
git.ipfire.org Git - thirdparty/mdadm.git/blob - Monitor.c
2 * mdadm - manage Linux "md" devices aka RAID arrays.
4 * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Email: <neilb@suse.de>
33 /* The largest number of disks current arrays can manage is 384
34 * This really should be dynamically, but that will have to wait
35 * At least it isn't MD_SB_DISKS.
40 int devnum
; /* to sync with mdstat info */
44 int active
, working
, failed
, spare
, raid
;
46 int devstate
[MaxDisks
];
47 unsigned devid
[MaxDisks
];
49 int parent_dev
; /* For subarray, devnum of parent.
52 struct supertype
*metadata
;
56 static int make_daemon(char *pidfile
);
57 static int check_one_sharer(int scan
);
58 static void alert(char *event
, char *dev
, char *disc
, char *mailaddr
, char *mailfrom
,
59 char *cmd
, int dosyslog
);
60 static void check_array(struct state
*st
, struct mdstat_ent
*mdstat
,
61 int test
, char *mailaddr
,
62 char *mailfrom
, char *alert_cmd
, int dosyslog
,
64 static int add_new_arrays(struct mdstat_ent
*mdstat
, struct state
*statelist
,
65 int test
, char *mailaddr
, char *mailfrom
,
66 char *alert_cmd
, int dosyslog
);
67 static void try_spare_migration(struct state
*statelist
,
68 char *mailaddr
, char *mailfrom
,
69 char *alert_cmd
, int dosyslog
);
71 int Monitor(struct mddev_dev
*devlist
,
72 char *mailaddr
, char *alert_cmd
,
73 int period
, int daemonise
, int scan
, int oneshot
,
74 int dosyslog
, int test
, char *pidfile
, int increments
,
78 * Every few seconds, scan every md device looking for changes
79 * When a change is found, log it, possibly run the alert command,
80 * and possibly send Email
82 * For each array, we record:
84 * active/working/failed/spare drives
85 * State of each device.
86 * %rebuilt if rebuilding
88 * If the update time changes, check out all the data again
89 * It is possible that we cannot get the state of each device
90 * due to bugs in the md kernel module.
91 * We also read /proc/mdstat to get rebuild percent,
92 * and to get state on all active devices incase of kernel bug.
96 * An active device had Faulty set or Active/Sync removed
98 * A spare device had Faulty set
100 * An active device had a reverse transition
102 * percent went from -1 to +ve
104 * percent went from below to not-below NN%
106 * Couldn't access a device which was previously visible
108 * if we detect an array with active<raid and spare==0
109 * we look at other arrays that have same spare-group
110 * If we find one with active==raid and spare>0,
111 * and if we can get_disk_info and find a name
112 * Then we hot-remove and hot-add to the other array
114 * If devlist is NULL, then we can monitor everything because --scan
115 * was given. We get an initial list from config file and add anything
116 * that appears in /proc/mdstat
119 struct state
*statelist
= NULL
;
121 struct mdstat_ent
*mdstat
= NULL
;
122 char *mailfrom
= NULL
;
125 mailaddr
= conf_get_mailaddr();
126 if (mailaddr
&& ! scan
)
127 fprintf(stderr
, Name
": Monitor using email address \"%s\" from config file\n",
130 mailfrom
= conf_get_mailfrom();
133 alert_cmd
= conf_get_program();
134 if (alert_cmd
&& ! scan
)
135 fprintf(stderr
, Name
": Monitor using program \"%s\" from config file\n",
138 if (scan
&& !mailaddr
&& !alert_cmd
) {
139 fprintf(stderr
, Name
": No mail address or alert command - not monitoring.\n");
144 if (make_daemon(pidfile
))
148 if (check_one_sharer(scan
))
151 if (devlist
== NULL
) {
152 struct mddev_ident
*mdlist
= conf_get_ident(NULL
);
153 for (; mdlist
; mdlist
=mdlist
->next
) {
155 if (mdlist
->devname
== NULL
)
157 if (strcasecmp(mdlist
->devname
, "<ignore>") == 0)
159 st
= malloc(sizeof *st
);
162 if (mdlist
->devname
[0] == '/')
163 st
->devname
= strdup(mdlist
->devname
);
165 st
->devname
= malloc(8+strlen(mdlist
->devname
)+1);
166 strcpy(strcpy(st
->devname
, "/dev/md/"),
170 st
->next
= statelist
;
172 st
->devnum
= INT_MAX
;
174 st
->expected_spares
= mdlist
->spare_disks
;
175 if (mdlist
->spare_group
)
176 st
->spare_group
= strdup(mdlist
->spare_group
);
178 st
->spare_group
= NULL
;
182 struct mddev_dev
*dv
;
183 for (dv
=devlist
; dv
; dv
=dv
->next
) {
184 struct mddev_ident
*mdlist
= conf_get_ident(dv
->devname
);
185 struct state
*st
= malloc(sizeof *st
);
188 st
->devname
= strdup(dv
->devname
);
190 st
->next
= statelist
;
192 st
->devnum
= INT_MAX
;
194 st
->expected_spares
= -1;
195 st
->spare_group
= NULL
;
197 st
->expected_spares
= mdlist
->spare_disks
;
198 if (mdlist
->spare_group
)
199 st
->spare_group
= strdup(mdlist
->spare_group
);
212 mdstat
= mdstat_read(oneshot
?0:1, 0);
214 for (st
=statelist
; st
; st
=st
->next
)
215 check_array(st
, mdstat
, test
, mailaddr
, mailfrom
,
216 alert_cmd
, dosyslog
, increments
);
218 /* now check if there are any new devices found in mdstat */
220 new_found
= add_new_arrays(mdstat
, statelist
, test
,
221 mailaddr
, mailfrom
, alert_cmd
,
224 /* If an array has active < raid && spare == 0 && spare_group != NULL
225 * Look for another array with spare > 0 and active == raid and same spare_group
226 * if found, choose a device and hotremove/hotadd
229 try_spare_migration(statelist
, mailaddr
, mailfrom
,
230 alert_cmd
, dosyslog
);
244 static int make_daemon(char *pidfile
)
252 pid_file
=fopen(pidfile
, "w");
254 perror("cannot create pid file");
256 fprintf(pid_file
,"%d\n", pid
);
267 open("/dev/null", O_RDWR
);
274 static int check_one_sharer(int scan
)
280 fp
= fopen("/var/run/mdadm/autorebuild.pid", "r");
282 fscanf(fp
, "%d", &pid
);
283 sprintf(dir
, "/proc/%d", pid
);
284 rv
= stat(dir
, &buf
);
287 fprintf(stderr
, Name
": Only one "
288 "autorebuild process allowed"
289 " in scan mode, aborting\n");
293 fprintf(stderr
, Name
": Warning: One"
294 " autorebuild process already"
301 fp
= fopen("/var/run/mdadm/autorebuild.pid", "w");
303 fprintf(stderr
, Name
": Cannot create"
308 fprintf(fp
, "%d\n", pid
);
315 static void alert(char *event
, char *dev
, char *disc
, char *mailaddr
, char *mailfrom
, char *cmd
,
320 if (!cmd
&& !mailaddr
) {
321 time_t now
= time(0);
323 printf("%1.15s: %s on %s %s\n", ctime(&now
)+4, event
, dev
, disc
?disc
:"unknown device");
329 waitpid(pid
, NULL
, 0);
334 execl(cmd
, cmd
, event
, dev
, disc
, NULL
);
339 (strncmp(event
, "Fail", 4)==0 ||
340 strncmp(event
, "Test", 4)==0 ||
341 strncmp(event
, "Spares", 6)==0 ||
342 strncmp(event
, "Degrade", 7)==0)) {
343 FILE *mp
= popen(Sendmail
, "w");
347 gethostname(hname
, sizeof(hname
));
348 signal(SIGPIPE
, SIG_IGN
);
350 fprintf(mp
, "From: %s\n", mailfrom
);
352 fprintf(mp
, "From: " Name
" monitoring <root>\n");
353 fprintf(mp
, "To: %s\n", mailaddr
);
354 fprintf(mp
, "Subject: %s event on %s:%s\n\n", event
, dev
, hname
);
356 fprintf(mp
, "This is an automatically generated mail message from " Name
"\n");
357 fprintf(mp
, "running on %s\n\n", hname
);
359 fprintf(mp
, "A %s event had been detected on md device %s.\n\n", event
, dev
);
361 if (disc
&& disc
[0] != ' ')
362 fprintf(mp
, "It could be related to component device %s.\n\n", disc
);
363 if (disc
&& disc
[0] == ' ')
364 fprintf(mp
, "Extra information:%s.\n\n", disc
);
366 fprintf(mp
, "Faithfully yours, etc.\n");
368 mdstat
= fopen("/proc/mdstat", "r");
372 fprintf(mp
, "\nP.S. The /proc/mdstat file currently contains the following:\n\n");
373 while ( (n
=fread(buf
, 1, sizeof(buf
), mdstat
)) > 0)
374 n
=fwrite(buf
, 1, n
, mp
); /* yes, i don't care about the result */
382 /* log the event to syslog maybe */
384 /* Log at a different severity depending on the event.
386 * These are the critical events: */
387 if (strncmp(event
, "Fail", 4)==0 ||
388 strncmp(event
, "Degrade", 7)==0 ||
389 strncmp(event
, "DeviceDisappeared", 17)==0)
391 /* Good to know about, but are not failures: */
392 else if (strncmp(event
, "Rebuild", 7)==0 ||
393 strncmp(event
, "MoveSpare", 9)==0 ||
394 strncmp(event
, "Spares", 6) != 0)
395 priority
= LOG_WARNING
;
396 /* Everything else: */
401 syslog(priority
, "%s event detected on md device %s, component device %s", event
, dev
, disc
);
403 syslog(priority
, "%s event detected on md device %s", event
, dev
);
407 static void check_array(struct state
*st
, struct mdstat_ent
*mdstat
,
408 int test
, char *mailaddr
,
409 char *mailfrom
, char *alert_cmd
, int dosyslog
,
412 struct { int state
, major
, minor
; } info
[MaxDisks
];
413 mdu_array_info_t array
;
414 struct mdstat_ent
*mse
= NULL
, *mse2
;
415 char *dev
= st
->devname
;
420 alert("TestMessage", dev
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
421 fd
= open(dev
, O_RDONLY
);
424 alert("DeviceDisappeared", dev
, NULL
,
425 mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
426 /* fprintf(stderr, Name ": cannot open %s: %s\n",
427 dev, strerror(errno));
431 fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
432 if (ioctl(fd
, GET_ARRAY_INFO
, &array
)<0) {
434 alert("DeviceDisappeared", dev
, NULL
,
435 mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
436 /* fprintf(stderr, Name ": cannot get array info for %s: %s\n",
437 dev, strerror(errno));
442 /* It's much easier to list what array levels can't
443 * have a device disappear than all of them that can
445 if (array
.level
== 0 || array
.level
== -1) {
447 alert("DeviceDisappeared", dev
, "Wrong-Level",
448 mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
453 if (st
->devnum
== INT_MAX
) {
455 if (fstat(fd
, &stb
) == 0 &&
456 (S_IFMT
&stb
.st_mode
)==S_IFBLK
) {
457 if (major(stb
.st_rdev
) == MD_MAJOR
)
458 st
->devnum
= minor(stb
.st_rdev
);
460 st
->devnum
= -1- (minor(stb
.st_rdev
)>>6);
464 for (mse2
= mdstat
; mse2
; mse2
=mse2
->next
)
465 if (mse2
->devnum
== st
->devnum
) {
466 mse2
->devnum
= INT_MAX
; /* flag it as "used" */
471 /* duplicated array in statelist
472 * or re-created after reading mdstat*/
477 /* this array is in /proc/mdstat */
478 if (array
.utime
== 0)
479 /* external arrays don't update utime, so
480 * just make sure it is always different. */
481 array
.utime
= st
->utime
+ 1;;
483 if (st
->utime
== array
.utime
&&
484 st
->failed
== array
.failed_disks
&&
485 st
->working
== array
.working_disks
&&
486 st
->spare
== array
.spare_disks
&&
488 mse
->percent
== st
->percent
494 if (st
->utime
== 0 && /* new array */
495 mse
->pattern
&& strchr(mse
->pattern
, '_') /* degraded */
497 alert("DegradedArray", dev
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
499 if (st
->utime
== 0 && /* new array */
500 st
->expected_spares
> 0 &&
501 array
.spare_disks
< st
->expected_spares
)
502 alert("SparesMissing", dev
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
503 if (st
->percent
== -1 &&
505 alert("RebuildStarted", dev
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
506 if (st
->percent
>= 0 &&
508 (mse
->percent
/ increments
) > (st
->percent
/ increments
)) {
509 char percentalert
[15]; // "RebuildNN" (10 chars) or "RebuildStarted" (15 chars)
511 if((mse
->percent
/ increments
) == 0)
512 snprintf(percentalert
, sizeof(percentalert
), "RebuildStarted");
514 snprintf(percentalert
, sizeof(percentalert
), "Rebuild%02d", mse
->percent
);
517 dev
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
520 if (mse
->percent
== -1 &&
522 /* Rebuild/sync/whatever just finished.
523 * If there is a number in /mismatch_cnt,
524 * we should report that.
527 sysfs_read(-1, st
->devnum
, GET_MISMATCH
);
528 if (sra
&& sra
->mismatch_cnt
> 0) {
530 sprintf(cnt
, " mismatches found: %d", sra
->mismatch_cnt
);
531 alert("RebuildFinished", dev
, cnt
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
533 alert("RebuildFinished", dev
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
537 st
->percent
= mse
->percent
;
539 for (i
=0; i
<MaxDisks
&& i
<= array
.raid_disks
+ array
.nr_disks
;
541 mdu_disk_info_t disc
;
543 if (ioctl(fd
, GET_DISK_INFO
, &disc
) >= 0) {
544 info
[i
].state
= disc
.state
;
545 info
[i
].major
= disc
.major
;
546 info
[i
].minor
= disc
.minor
;
548 info
[i
].major
= info
[i
].minor
= 0;
551 if (strncmp(mse
->metadata_version
, "external:", 9) == 0 &&
552 is_subarray(mse
->metadata_version
+9))
554 devname2devnum(mse
->metadata_version
+10);
556 st
->parent_dev
= NoMdDev
;
557 if (st
->metadata
== NULL
&&
558 st
->parent_dev
== NoMdDev
)
559 st
->metadata
= super_by_fd(fd
, NULL
);
563 for (i
=0; i
<MaxDisks
; i
++) {
564 mdu_disk_info_t disc
= {0,0,0,0,0};
569 if (i
> array
.raid_disks
+ array
.nr_disks
) {
571 disc
.major
= disc
.minor
= 0;
572 } else if (info
[i
].major
|| info
[i
].minor
) {
573 newstate
= info
[i
].state
;
574 dv
= map_dev(info
[i
].major
, info
[i
].minor
, 1);
575 disc
.state
= newstate
;
576 disc
.major
= info
[i
].major
;
577 disc
.minor
= info
[i
].minor
;
578 } else if (mse
&& mse
->pattern
&& i
< (int)strlen(mse
->pattern
)) {
579 switch(mse
->pattern
[i
]) {
580 case 'U': newstate
= 6 /* ACTIVE/SYNC */; break;
581 case '_': newstate
= 0; break;
583 disc
.major
= disc
.minor
= 0;
585 if (dv
== NULL
&& st
->devid
[i
])
586 dv
= map_dev(major(st
->devid
[i
]),
587 minor(st
->devid
[i
]), 1);
588 change
= newstate
^ st
->devstate
[i
];
589 if (st
->utime
&& change
&& !st
->err
) {
590 if (i
< array
.raid_disks
&&
591 (((newstate
&change
)&(1<<MD_DISK_FAULTY
)) ||
592 ((st
->devstate
[i
]&change
)&(1<<MD_DISK_ACTIVE
)) ||
593 ((st
->devstate
[i
]&change
)&(1<<MD_DISK_SYNC
)))
595 alert("Fail", dev
, dv
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
596 else if (i
>= array
.raid_disks
&&
597 (disc
.major
|| disc
.minor
) &&
598 st
->devid
[i
] == makedev(disc
.major
, disc
.minor
) &&
599 ((newstate
&change
)&(1<<MD_DISK_FAULTY
))
601 alert("FailSpare", dev
, dv
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
602 else if (i
< array
.raid_disks
&&
603 ! (newstate
& (1<<MD_DISK_REMOVED
)) &&
604 (((st
->devstate
[i
]&change
)&(1<<MD_DISK_FAULTY
)) ||
605 ((newstate
&change
)&(1<<MD_DISK_ACTIVE
)) ||
606 ((newstate
&change
)&(1<<MD_DISK_SYNC
)))
608 alert("SpareActive", dev
, dv
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
610 st
->devstate
[i
] = newstate
;
611 st
->devid
[i
] = makedev(disc
.major
, disc
.minor
);
613 st
->active
= array
.active_disks
;
614 st
->working
= array
.working_disks
;
615 st
->spare
= array
.spare_disks
;
616 st
->failed
= array
.failed_disks
;
617 st
->utime
= array
.utime
;
618 st
->raid
= array
.raid_disks
;
622 static int add_new_arrays(struct mdstat_ent
*mdstat
, struct state
*statelist
,
623 int test
, char *mailaddr
, char *mailfrom
,
624 char *alert_cmd
, int dosyslog
)
626 struct mdstat_ent
*mse
;
629 for (mse
=mdstat
; mse
; mse
=mse
->next
)
630 if (mse
->devnum
!= INT_MAX
&&
631 (!mse
->level
|| /* retrieve containers */
632 (strcmp(mse
->level
, "raid0") != 0 &&
633 strcmp(mse
->level
, "linear") != 0))
635 struct state
*st
= malloc(sizeof *st
);
636 mdu_array_info_t array
;
640 st
->devname
= strdup(get_md_name(mse
->devnum
));
641 if ((fd
= open(st
->devname
, O_RDONLY
)) < 0 ||
642 ioctl(fd
, GET_ARRAY_INFO
, &array
)< 0) {
644 if (fd
>=0) close(fd
);
645 put_md_name(st
->devname
);
648 st
->metadata
->ss
->free_super(st
->metadata
);
656 st
->next
= statelist
;
658 st
->devnum
= mse
->devnum
;
660 st
->spare_group
= NULL
;
661 st
->expected_spares
= -1;
662 if (strncmp(mse
->metadata_version
, "external:", 9) == 0 &&
663 is_subarray(mse
->metadata_version
+9))
665 devname2devnum(mse
->metadata_version
+10);
667 st
->parent_dev
= NoMdDev
;
671 alert("TestMessage", st
->devname
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
672 alert("NewArray", st
->devname
, NULL
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
678 static void try_spare_migration(struct state
*statelist
,
679 char *mailaddr
, char *mailfrom
,
680 char *alert_cmd
, int dosyslog
)
683 for (st
= statelist
; st
; st
=st
->next
)
684 if (st
->active
< st
->raid
&&
686 st
->spare_group
!= NULL
) {
688 for (st2
=statelist
; st2
; st2
=st2
->next
)
691 st2
->active
== st2
->raid
&&
692 st2
->spare_group
!= NULL
&&
693 strcmp(st
->spare_group
, st2
->spare_group
) == 0) {
694 /* try to remove and add */
695 int fd1
= open(st
->devname
, O_RDONLY
);
696 int fd2
= open(st2
->devname
, O_RDONLY
);
699 if (fd1
< 0 || fd2
< 0) {
700 if (fd1
>=0) close(fd1
);
701 if (fd2
>=0) close(fd2
);
704 for (d
=st2
->raid
; d
< MaxDisks
; d
++) {
705 if (st2
->devid
[d
] > 0 &&
706 st2
->devstate
[d
] == 0) {
712 struct mddev_dev devlist
;
717 devlist
.writemostly
= 0;
718 devlist
.devname
= devname
;
719 sprintf(devname
, "%d:%d", major(dev
), minor(dev
));
721 devlist
.disposition
= 'r';
722 if (Manage_subdevs(st2
->devname
, fd2
, &devlist
, -1, 0) == 0) {
723 devlist
.disposition
= 'a';
724 if (Manage_subdevs(st
->devname
, fd1
, &devlist
, -1, 0) == 0) {
725 alert("MoveSpare", st
->devname
, st2
->devname
, mailaddr
, mailfrom
, alert_cmd
, dosyslog
);
730 else Manage_subdevs(st2
->devname
, fd2
, &devlist
, -1, 0);
738 /* Not really Monitor but ... */
745 if (stat(dev
, &stb
) != 0) {
746 fprintf(stderr
, Name
": Cannot find %s: %s\n", dev
,
750 devnum
= stat2devnum(&stb
);
753 struct mdstat_ent
*ms
= mdstat_read(1, 0);
754 struct mdstat_ent
*e
;
756 for (e
=ms
; e
; e
=e
->next
)
757 if (e
->devnum
== devnum
)
760 if (!e
|| e
->percent
< 0) {
761 if (e
&& e
->metadata_version
&&
762 strncmp(e
->metadata_version
, "external:", 9) == 0) {
763 if (is_subarray(&e
->metadata_version
[9]))
764 ping_monitor(&e
->metadata_version
[9]);
766 ping_monitor(devnum2devname(devnum
));