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