/* * smart monitoring for scsi and ata * copyright © 2009 erik quanstrom */ #include #include #include #include "smart.h" enum{ Checksec = 600, Opensec = 60 * 60, Relogsec = 38400 / 4, }; static Sdisk *disks; static Dtype dtab[] = { Tata, "ata", ataprobe, ataenable, atastatus, Tscsi, "scsi", scsiprobe, scsienable, scsistatus, }; static char *logfile = "smart"; static int aflag; static int tflag; static int vflag; void eprint(Sdisk *d, char *s, ...) { char buf[256]; va_list arg; va_start(arg, s); vseprint(buf, buf + sizeof buf, s, arg); va_end(arg); // syslog(0, logfile, "%s: %s", d->name, buf); if(vflag) fprint(2, "%s: %s", d->name, buf); } void smartlog(Sdisk *d, char *s, ...) { char buf[256]; va_list arg; va_start(arg, s); vseprint(buf, buf + sizeof buf, s, arg); va_end(arg); if(!tflag) syslog(0, logfile, "%s: %s", d->name, buf); if(tflag || vflag) fprint(2, "%s: %s\n", d->name, buf); } static void diskclose(Sdisk *d) { close(d->fd); d->fd = -1; } static int diskopen(Sdisk *d) { char buf[128]; snprint(buf, sizeof buf, "%s/raw", d->path); werrstr(""); return d->fd = open(buf, ORDWR); } static int noexist(void) { char buf[ERRMAX]; errstr(buf, sizeof buf); if(strstr(buf, "exist")) return -1; return 0; } static void lognew(Sdisk *d) { if(aflag && !tflag) smartlog(d, d->t->tname); } static int newdisk(char *s) { char buf[128], *p; int i; Sdisk d; memset(&d, 0, sizeof d); snprint(d.path, sizeof d.path, "%s", s); if(p = strrchr(s, '/')) p++; else p = s; snprint(d.name, sizeof d.name, "%s", p); snprint(buf, sizeof buf, "%s/raw", s); if(diskopen(&d) == -1) return noexist(); for(i = 0; i < nelem(dtab); i++) if(dtab[i].probe(&d) == 0) if(dtab[i].enable(&d) == 0){ d.t = dtab + i; lognew(&d); break; } diskclose(&d); if(d.t != 0){ d.next = disks; disks = malloc(sizeof d); memmove(disks, &d, sizeof d); } return 0; } static int probe0(char *s, int l) { char *p, *f[3], buf[16]; int i; s[l] = 0; for(; p = strchr(s, '\n'); s = p + 1){ if(tokenize(s, f, nelem(f)) < 1) continue; for(i = 0; i < 0x10; i++){ snprint(buf, sizeof buf, "/dev/%s%ux", f[0], i); if(newdisk(buf) == -1 && i > 2) break; } } return -1; } int probe(void) { char *s; int fd, l, r; fd = open("/dev/sdctl", OREAD); if(fd == -1) return -1; r = -1; l = 1024; /* #S/sdctl has 0 size; guess */ if(s = malloc(l + 1)) if((l = read(fd, s, l)) > 0) r = probe0(s, l); free(s); close(fd); return r; } void run(void) { char buf[1024]; int e, s0; uvlong t, t0; Sdisk *d; e = 0; t = time(0); for(d = disks; d; d = d->next){ t0 = d->lastcheck; if(t0 != 0 && t - t0 < Checksec) continue; if(diskopen(d) == -1){ if(t - t0 > Opensec) smartlog(d, "can't open in %ullds\n", t - t0); continue; } s0 = d->status; d->status = d->t->status(d, buf, sizeof buf); diskclose(d); if(d->status == -1) e++; if((aflag || d->status != s0 || d->status != 0) && !d->silent){ t0 = d->lastlog; if(t0 == 0 || t - t0 >= Relogsec){ smartlog(d, buf); d->lastlog = t; } }else d->lastlog = 0; d->lastcheck = t; } if(tflag) exits(e? "smart errors": ""); } void usage(void) { fprint(2, "usage: disk/smart [-aptv] [/dev/sdXX] ...\n"); exits("usage"); } void main(int argc, char **argv) { int pflag; pflag = 0; ARGBEGIN{ case 'a': aflag = 1; break; case 'p': pflag = 1; break; case 't': tflag = 1; case 'v': vflag = 1; break; default: usage(); }ARGEND for(; *argv; argv++) newdisk(*argv); if(argc == 0 || pflag) probe(); if(disks == nil) sysfatal("no disks"); for(;; sleep(30*1000)) run(); }