xref: /plan9-contrib/sys/src/9/port/devwd.c (revision 1936bb650459bace06c38a45b60888b47e5cd459)
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
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
47 wdallowed(void)
48 {
49 	return getconf("*nowatchdog") == nil;
50 }
51 
52 static 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
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
75 wdautostart(void)
76 {
77 	if (wdautopet || !wd || !wdallowed())
78 		return;
79 	if (waserror()) {
80 		iprint("watchdog: enable failed\n");
81 		return;
82 	}
83 	wd->enable();
84 	poperror();
85 
86 	iprint("watchdog: on with clock strokes\n");
87 	wdautopet = watchdogon = 1;
88 	if (!wdclock0called) {
89 		addclock0link(wdpet, 200);
90 		wdclock0called = 1;
91 	}
92 }
93 
94 /*
95  * disable strokes from the clock interrupt.
96  * have to disable the watchdog to mark it `not in use'.
97  */
98 static void
99 wdautostop(void)
100 {
101 	if (!wdautopet)
102 		return;
103 	wdautopet = 0;
104 	wdshutdown();
105 	iprint("watchdog: disabled before open\n");
106 }
107 
108 /*
109  * user processes exist and up is non-nil when the
110  * device init routines are called.
111  */
112 static void
113 wdinit(void)
114 {
115 	wdautostart();
116 }
117 
118 static Chan*
119 wdattach(char *spec)
120 {
121 	return devattach('w', spec);
122 }
123 
124 static Walkqid*
125 wdwalk(Chan *c, Chan *nc, char **name, int nname)
126 {
127 	return devwalk(c, nc, name, nname, wddir, nelem(wddir), devgen);
128 }
129 
130 static int
131 wdstat(Chan *c, uchar *dp, int n)
132 {
133 	return devstat(c, dp, n, wddir, nelem(wddir), devgen);
134 }
135 
136 static Chan*
137 wdopen(Chan* c, int omode)
138 {
139 	wdautostop();
140 	c = devopen(c, omode, wddir, nelem(wddir), devgen);
141 	if (c->qid.path == Qwdctl)
142 		incref(&refs);
143 	return c;
144 }
145 
146 static void
147 wdclose(Chan *c)
148 {
149 	if(c->qid.path == Qwdctl && c->flag&COPEN && decref(&refs) <= 0)
150 		wdshutdown();
151 }
152 
153 static long
154 wdread(Chan* c, void* a, long n, vlong off)
155 {
156 	ulong offset = off;
157 	char *p;
158 
159 	switch((ulong)c->qid.path){
160 	case Qdir:
161 		return devdirread(c, a, n, wddir, nelem(wddir), devgen);
162 
163 	case Qwdctl:
164 		if(wd == nil || wd->stat == nil)
165 			return 0;
166 
167 		p = malloc(READSTR);
168 		if(p == nil)
169 			error(Enomem);
170 		if(waserror()){
171 			free(p);
172 			nexterror();
173 		}
174 
175 		wd->stat(p, p + READSTR);
176 		n = readstr(offset, a, n, p);
177 		free(p);
178 		poperror();
179 		return n;
180 
181 	default:
182 		error(Egreg);
183 		break;
184 	}
185 	return 0;
186 }
187 
188 static long
189 wdwrite(Chan* c, void* a, long n, vlong off)
190 {
191 	ulong offset = off;
192 	char *p;
193 
194 	switch((ulong)c->qid.path){
195 	case Qdir:
196 		error(Eperm);
197 
198 	case Qwdctl:
199 		if(wd == nil)
200 			return n;
201 
202 		if(offset || n >= READSTR)
203 			error(Ebadarg);
204 
205 		if((p = strchr(a, '\n')) != nil)
206 			*p = 0;
207 
208 		if(strncmp(a, "enable", n) == 0) {
209 			if (waserror()) {
210 				iprint("watchdog: enable failed\n");
211 				nexterror();
212 			}
213 			wd->enable();
214 			poperror();
215 			watchdogon = 1;
216 		} else if(strncmp(a, "disable", n) == 0)
217 			wdshutdown();
218 		else if(strncmp(a, "restart", n) == 0)
219 			wd->restart();
220 		else
221 			error(Ebadarg);
222 		return n;
223 
224 	default:
225 		error(Egreg);
226 		break;
227 	}
228 
229 	return 0;
230 }
231 
232 Dev wddevtab = {
233 	'w',
234 	"watchdog",
235 
236 	devreset,
237 	wdinit,
238 	wdshutdown,
239 	wdattach,
240 	wdwalk,
241 	wdstat,
242 	wdopen,
243 	devcreate,
244 	wdclose,
245 	wdread,
246 	devbread,
247 	wdwrite,
248 	devbwrite,
249 	devremove,
250 	devwstat,
251 	devpower,
252 };
253