xref: /plan9-contrib/sys/src/9k/port/devwd.c (revision 406c76facc4b13aa2a55454bf4091aab9f03da22)
1 /*
2  * watchdog framework
3  */
4 #include	"u.h"
5 #include	"../port/lib.h"
6 #include	"mem.h"
7 #include	"dat.h"
8 #include	"fns.h"
9 #include	"io.h"
10 #include	"../port/error.h"
11 
12 enum {
13 	Qdir,
14 	Qwdctl,
15 };
16 
17 /*
18  * these are exposed so that delay() and the like can disable the watchdog
19  * before busy looping for a long time.
20  */
21 Watchdog*watchdog;
22 int	watchdogon;
23 
24 static Watchdog *wd;
25 static int wdautopet;
26 static int wdclock0called;
27 static Ref refs;
28 static Dirtab wddir[] = {
29 	".",		{ Qdir, 0, QTDIR },	0,		0555,
30 	"wdctl",	{ Qwdctl, 0 },		0,		0664,
31 };
32 
33 
34 void
addwatchdog(Watchdog * wdog)35 addwatchdog(Watchdog *wdog)
36 {
37 	if(wd){
38 		print("addwatchdog: watchdog already installed\n");
39 		return;
40 	}
41 	wd = watchdog = wdog;
42 	if(wd)
43 		wd->disable();
44 }
45 
46 static int
wdallowed(void)47 wdallowed(void)
48 {
49 	return getconf("*nowatchdog") == nil;
50 }
51 
52 static void
wdshutdown(void)53 wdshutdown(void)
54 {
55 	if (wd) {
56 		wd->disable();
57 		watchdogon = 0;
58 	}
59 }
60 
61 /* called from clock interrupt, so restart needs ilock internally */
62 static void
wdpet(void)63 wdpet(void)
64 {
65 	/* watchdog could be paused; if so, don't restart */
66 	if (wdautopet && watchdogon)
67 		wd->restart();
68 }
69 
70 /*
71  * reassure the watchdog from the clock interrupt
72  * until the user takes control of it.
73  */
74 static void
wdautostart(void)75 wdautostart(void)
76 {
77 	if (wdautopet || !wd || !wdallowed())
78 		return;
79 	if (waserror()) {
80 		print("watchdog: automatic enable failed\n");
81 		return;
82 	}
83 	wd->enable();
84 	poperror();
85 
86 	wdautopet = watchdogon = 1;
87 	if (!wdclock0called) {
88 		addclock0link(wdpet, 200);
89 		wdclock0called = 1;
90 	}
91 }
92 
93 /*
94  * disable strokes from the clock interrupt.
95  * have to disable the watchdog to mark it `not in use'.
96  */
97 static void
wdautostop(void)98 wdautostop(void)
99 {
100 	if (!wdautopet)
101 		return;
102 	wdautopet = 0;
103 	wdshutdown();
104 }
105 
106 /*
107  * user processes exist and up is non-nil when the
108  * device init routines are called.
109  */
110 static void
wdinit(void)111 wdinit(void)
112 {
113 	wdautostart();
114 }
115 
116 static Chan*
wdattach(char * spec)117 wdattach(char *spec)
118 {
119 	return devattach('w', spec);
120 }
121 
122 static Walkqid*
wdwalk(Chan * c,Chan * nc,char ** name,int nname)123 wdwalk(Chan *c, Chan *nc, char **name, int nname)
124 {
125 	return devwalk(c, nc, name, nname, wddir, nelem(wddir), devgen);
126 }
127 
128 static long
wdstat(Chan * c,uchar * dp,long n)129 wdstat(Chan *c, uchar *dp, long n)
130 {
131 	return devstat(c, dp, n, wddir, nelem(wddir), devgen);
132 }
133 
134 static Chan*
wdopen(Chan * c,int omode)135 wdopen(Chan* c, int omode)
136 {
137 	wdautostop();
138 	c = devopen(c, omode, wddir, nelem(wddir), devgen);
139 	if (c->qid.path == Qwdctl)
140 		incref(&refs);
141 	return c;
142 }
143 
144 static void
wdclose(Chan * c)145 wdclose(Chan *c)
146 {
147 	if(c->qid.path == Qwdctl && c->flag&COPEN && decref(&refs) <= 0)
148 		wdshutdown();
149 }
150 
151 static long
wdread(Chan * c,void * a,long n,vlong off)152 wdread(Chan* c, void* a, long n, vlong off)
153 {
154 	long offset;
155 	char s[READSTR];
156 
157 	offset = off;
158 	switch((ulong)c->qid.path){
159 	case Qdir:
160 		return devdirread(c, a, n, wddir, nelem(wddir), devgen);
161 
162 	case Qwdctl:
163 		if(wd == nil || wd->stat == nil)
164 			return 0;
165 
166 		wd->stat(s, s + READSTR);
167 		return readstr(offset, a, n, s);
168 
169 	default:
170 		error(Egreg);
171 		break;
172 	}
173 	return 0;
174 }
175 
176 static long
wdwrite(Chan * c,void * a,long n,vlong off)177 wdwrite(Chan* c, void* a, long n, vlong off)
178 {
179 	char *p;
180 
181 	switch((ulong)c->qid.path){
182 	case Qdir:
183 		error(Eperm);
184 
185 	case Qwdctl:
186 		if(wd == nil)
187 			return n;
188 
189 		if(off != 0ll)
190 			error(Ebadarg);
191 
192 		if(p = strchr(a, '\n'))
193 			*p = 0;
194 
195 		if(!strncmp(a, "enable", n))
196 			wd->enable();
197 		else if(!strncmp(a, "disable", n))
198 			wd->disable();
199 		else if(!strncmp(a, "restart", n))
200 			wd->restart();
201 		else
202 			error(Ebadarg);
203 		return n;
204 
205 	default:
206 		error(Egreg);
207 		break;
208 	}
209 
210 	return 0;
211 }
212 
213 Dev wddevtab = {
214 	'w',
215 	"watchdog",
216 
217 	devreset,
218 	wdinit,
219 	wdshutdown,
220 	wdattach,
221 	wdwalk,
222 	wdstat,
223 	wdopen,
224 	devcreate,
225 	wdclose,
226 	wdread,
227 	devbread,
228 	wdwrite,
229 	devbwrite,
230 	devremove,
231 	devwstat,
232 	devpower,
233 };
234