xref: /plan9/sys/src/9/port/devwd.c (revision 99c5542733063989ecf76031d5eb261c95490702)
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 int
wdstat(Chan * c,uchar * dp,int n)129 wdstat(Chan *c, uchar *dp, int 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 	ulong offset = off;
155 	char *p;
156 
157 	switch((ulong)c->qid.path){
158 	case Qdir:
159 		return devdirread(c, a, n, wddir, nelem(wddir), devgen);
160 
161 	case Qwdctl:
162 		if(wd == nil || wd->stat == nil)
163 			return 0;
164 
165 		p = malloc(READSTR);
166 		if(p == nil)
167 			error(Enomem);
168 		if(waserror()){
169 			free(p);
170 			nexterror();
171 		}
172 
173 		wd->stat(p, p + READSTR);
174 		n = readstr(offset, a, n, p);
175 		free(p);
176 		poperror();
177 		return n;
178 
179 	default:
180 		error(Egreg);
181 		break;
182 	}
183 	return 0;
184 }
185 
186 static long
wdwrite(Chan * c,void * a,long n,vlong off)187 wdwrite(Chan* c, void* a, long n, vlong off)
188 {
189 	ulong offset = off;
190 	char *p;
191 
192 	switch((ulong)c->qid.path){
193 	case Qdir:
194 		error(Eperm);
195 
196 	case Qwdctl:
197 		if(wd == nil)
198 			return n;
199 
200 		if(offset || n >= READSTR)
201 			error(Ebadarg);
202 
203 		if((p = strchr(a, '\n')) != nil)
204 			*p = 0;
205 
206 		if(strncmp(a, "enable", n) == 0) {
207 			if (waserror()) {
208 				print("watchdog: enable failed\n");
209 				nexterror();
210 			}
211 			wd->enable();
212 			poperror();
213 			watchdogon = 1;
214 		} else if(strncmp(a, "disable", n) == 0)
215 			wdshutdown();
216 		else if(strncmp(a, "restart", n) == 0)
217 			wd->restart();
218 		else
219 			error(Ebadarg);
220 		return n;
221 
222 	default:
223 		error(Egreg);
224 		break;
225 	}
226 
227 	return 0;
228 }
229 
230 Dev wddevtab = {
231 	'w',
232 	"watchdog",
233 
234 	devreset,
235 	wdinit,
236 	wdshutdown,
237 	wdattach,
238 	wdwalk,
239 	wdstat,
240 	wdopen,
241 	devcreate,
242 	wdclose,
243 	wdread,
244 	devbread,
245 	wdwrite,
246 	devbwrite,
247 	devremove,
248 	devwstat,
249 	devpower,
250 };
251