/* * watchdog framework */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" enum { Qdir, Qwdctl, }; /* * these are exposed so that delay() and the like can disable the watchdog * before busy looping for a long time. */ Watchdog*watchdog; int watchdogon; static Watchdog *wd; static int wdautopet; static int wdclock0called; static Ref refs; static Dirtab wddir[] = { ".", { Qdir, 0, QTDIR }, 0, 0555, "wdctl", { Qwdctl, 0 }, 0, 0664, }; void addwatchdog(Watchdog *wdog) { if(wd){ print("addwatchdog: watchdog already installed\n"); return; } wd = watchdog = wdog; if(wd) wd->disable(); } static int wdallowed(void) { return getconf("*nowatchdog") == nil; } static void wdshutdown(void) { if (wd) { wd->disable(); watchdogon = 0; } } /* called from clock interrupt, so restart needs ilock internally */ static void wdpet(void) { /* watchdog could be paused; if so, don't restart */ if (wdautopet && watchdogon) wd->restart(); } /* * reassure the watchdog from the clock interrupt * until the user takes control of it. */ static void wdautostart(void) { if (wdautopet || !wd || !wdallowed()) return; iprint("watchdog: on with clock strokes\n"); wd->enable(); wdautopet = watchdogon = 1; if (!wdclock0called) { addclock0link(wdpet, 200); wdclock0called = 1; } } /* * disable strokes from the clock interrupt. * have to disable the watchdog to mark it `not in use'. */ static void wdautostop(void) { if (!wdautopet) return; wdautopet = 0; wdshutdown(); iprint("watchdog: disabled before open\n"); } static void wdreset(void) { wdautostart(); } static Chan* wdattach(char *spec) { return devattach('w', spec); } static Walkqid* wdwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, wddir, nelem(wddir), devgen); } static int wdstat(Chan *c, uchar *dp, int n) { return devstat(c, dp, n, wddir, nelem(wddir), devgen); } static Chan* wdopen(Chan* c, int omode) { wdautostop(); c = devopen(c, omode, wddir, nelem(wddir), devgen); if (c->qid.path == Qwdctl) incref(&refs); return c; } static void wdclose(Chan *c) { if(c->qid.path == Qwdctl && c->flag&COPEN && decref(&refs) <= 0) wdshutdown(); } static long wdread(Chan* c, void* a, long n, vlong off) { ulong offset = off; char *p; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, a, n, wddir, nelem(wddir), devgen); case Qwdctl: if(wd == nil || wd->stat == nil) return 0; p = malloc(READSTR); if(p == nil) error(Enomem); if(waserror()){ free(p); nexterror(); } wd->stat(p, p + READSTR); n = readstr(offset, a, n, p); free(p); poperror(); return n; default: error(Egreg); break; } return 0; } static long wdwrite(Chan* c, void* a, long n, vlong off) { ulong offset = off; char *p; switch((ulong)c->qid.path){ case Qdir: error(Eperm); case Qwdctl: if(wd == nil) return n; if(offset || n >= READSTR) error(Ebadarg); if((p = strchr(a, '\n')) != nil) *p = 0; if(strncmp(a, "enable", n) == 0) { wd->enable(); watchdogon = 1; } else if(strncmp(a, "disable", n) == 0) wdshutdown(); else if(strncmp(a, "restart", n) == 0) wd->restart(); else error(Ebadarg); return n; default: error(Egreg); break; } return 0; } Dev wddevtab = { 'w', "watchdog", wdreset, devinit, wdshutdown, wdattach, wdwalk, wdstat, wdopen, devcreate, wdclose, wdread, devbread, wdwrite, devbwrite, devremove, devwstat, devpower, };