jail: correctly check for null pointer
[project/procd.git] / watchdog.c
1 /*
2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3 * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License version 2.1
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #include <linux/watchdog.h>
16
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21
22 #include <unistd.h>
23
24 #include <libubox/uloop.h>
25
26 #include "procd.h"
27 #include "watchdog.h"
28
29 #define WDT_PATH "/dev/watchdog"
30
31 static struct uloop_timeout wdt_timeout;
32 static int wdt_fd = -1;
33 static int wdt_drv_timeout = 30;
34 static int wdt_frequency = 5;
35 static bool wdt_magicclose = false;
36
37 void watchdog_ping(void)
38 {
39 DEBUG(4, "Ping\n");
40 if (wdt_fd >= 0 && write(wdt_fd, "X", 1) < 0)
41 ERROR("WDT failed to write: %m\n");
42 }
43
44 static void watchdog_timeout_cb(struct uloop_timeout *t)
45 {
46 watchdog_ping();
47 uloop_timeout_set(t, wdt_frequency * 1000);
48 }
49
50 static int watchdog_open(bool cloexec)
51 {
52 char *env = getenv("WDTFD");
53
54 if (wdt_fd >= 0)
55 return wdt_fd;
56
57 if (env) {
58 DEBUG(2, "Watchdog handover: fd=%s\n", env);
59 wdt_fd = atoi(env);
60 unsetenv("WDTFD");
61 } else {
62 wdt_fd = open(WDT_PATH, O_WRONLY);
63 }
64
65 if (wdt_fd < 0)
66 return wdt_fd;
67
68 if (cloexec)
69 fcntl(wdt_fd, F_SETFD, fcntl(wdt_fd, F_GETFD) | FD_CLOEXEC);
70
71 return wdt_fd;
72 }
73
74 static void watchdog_close(void)
75 {
76 if (wdt_fd < 0)
77 return;
78
79 if (write(wdt_fd, "V", 1) < 0)
80 ERROR("WDT failed to write release: %m\n");
81
82 if (close(wdt_fd) == -1)
83 ERROR("WDT failed to close watchdog: %m\n");
84
85 wdt_fd = -1;
86 }
87
88 static int watchdog_set_drv_timeout(void)
89 {
90 if (wdt_fd < 0)
91 return -1;
92
93 return ioctl(wdt_fd, WDIOC_SETTIMEOUT, &wdt_drv_timeout);
94 }
95
96 static void watchdog_print_status(void)
97 {
98 struct watchdog_info wdt_info;
99 int bootstatus;
100
101 if (wdt_fd < 0)
102 return;
103
104 if (ioctl(wdt_fd, WDIOC_GETSUPPORT, &wdt_info)) {
105 DEBUG(2, "Watchdog GETSUPPORT failed\n");
106 return;
107 }
108
109 if (!(wdt_info.options & WDIOF_CARDRESET)) {
110 DEBUG(2, "Watchdog does not have CARDRESET support\n");
111 return;
112 }
113
114 if (ioctl(wdt_fd, WDIOC_GETBOOTSTATUS, &bootstatus)) {
115 DEBUG(2, "Watchdog GETBOOTSTATUS failed\n");
116 return;
117 }
118
119 if (bootstatus & WDIOF_CARDRESET)
120 LOG("Watchdog has previously reset the system\n");
121 else
122 DEBUG(2, "Watchdog did not previously reset the system\n");
123 }
124
125 void watchdog_set_magicclose(bool val)
126 {
127 wdt_magicclose = val;
128 }
129
130 bool watchdog_get_magicclose(void)
131 {
132 return wdt_magicclose;
133 }
134
135 void watchdog_set_stopped(bool val)
136 {
137 if (val) {
138 uloop_timeout_cancel(&wdt_timeout);
139
140 if (wdt_magicclose)
141 watchdog_close();
142 }
143 else {
144 watchdog_open(true);
145 watchdog_set_drv_timeout();
146 watchdog_timeout_cb(&wdt_timeout);
147 }
148 }
149
150 bool watchdog_get_stopped(void)
151 {
152 return !wdt_timeout.pending;
153 }
154
155 int watchdog_timeout(int timeout)
156 {
157 if (timeout) {
158 DEBUG(4, "Set watchdog timeout: %ds\n", timeout);
159 wdt_drv_timeout = timeout;
160
161 if (wdt_fd >= 0)
162 watchdog_set_drv_timeout();
163 }
164
165 return wdt_drv_timeout;
166 }
167
168 int watchdog_frequency(int frequency)
169 {
170 if (frequency) {
171 DEBUG(4, "Set watchdog frequency: %ds\n", frequency);
172 wdt_frequency = frequency;
173 }
174
175 return wdt_frequency;
176 }
177
178 char* watchdog_fd(void)
179 {
180 static char fd_buf[12];
181
182 if (wdt_fd < 0)
183 return NULL;
184
185 snprintf(fd_buf, sizeof(fd_buf), "%d", wdt_fd);
186
187 return fd_buf;
188 }
189
190 void watchdog_init(int preinit)
191 {
192 wdt_timeout.cb = watchdog_timeout_cb;
193
194 if (watchdog_open(!preinit) < 0)
195 return;
196
197 LOG("- watchdog -\n");
198 watchdog_set_drv_timeout();
199 watchdog_timeout_cb(&wdt_timeout);
200
201 DEBUG(4, "Opened watchdog with timeout %ds\n", watchdog_timeout(0));
202
203 watchdog_print_status();
204 }
205
206
207 void watchdog_set_cloexec(bool val)
208 {
209 if (wdt_fd < 0)
210 return;
211
212 int flags = fcntl(wdt_fd, F_GETFD);
213 if (val)
214 flags |= FD_CLOEXEC;
215 else
216 flags &= ~FD_CLOEXEC;
217 fcntl(wdt_fd, F_SETFD, flags);
218 }