/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 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,}; voidaddwatchdog(Watchdog *wdog){ if(wd){ print("addwatchdog: watchdog already installed\n"); return; } wd = watchdog = wdog; if(wd) wd->disable();} static intwdallowed(void){ return getconf("*nowatchdog") == nil;} static voidwdshutdown(void){ if (wd) { wd->disable(); watchdogon = 0; }} /* called from clock interrupt, so restart needs ilock internally */static voidwdpet(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 voidwdautostart(void){ if (wdautopet || !wd || !wdallowed()) return; if (waserror()) { print("watchdog: automatic enable failed\n"); return; } wd->enable(); poperror(); 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 voidwdautostop(void){ if (!wdautopet) return; wdautopet = 0; wdshutdown();} /* * user processes exist and up is non-nil when the * device init routines are called. */static voidwdinit(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 intwdstat(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 voidwdclose(Chan *c){ if(c->qid.path == Qwdctl && c->flag&COPEN && decref(&refs) <= 0) wdshutdown();} static longwdread(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 longwdwrite(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) { if (waserror()) { print("watchdog: enable failed\n"); nexterror(); } wd->enable(); poperror(); 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", devreset, wdinit, wdshutdown, wdattach, wdwalk, wdstat, wdopen, devcreate, wdclose, wdread, devbread, wdwrite, devbwrite, devremove, devwstat, devpower,};
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* 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;
if (waserror()) {
print("watchdog: automatic enable failed\n");
return;
}
wd->enable();
poperror();
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();
}
/*
* user processes exist and up is non-nil when the
* device init routines are called.
*/
static void
wdinit(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) {
if (waserror()) {
print("watchdog: enable failed\n");
nexterror();
}
wd->enable();
poperror();
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",
devreset,
wdinit,
wdshutdown,
wdattach,
wdwalk,
wdstat,
wdopen,
devcreate,
wdclose,
wdread,
devbread,
wdwrite,
devbwrite,
devremove,
devwstat,
devpower,
};