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