1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Watchdog Driver Test Program
4 * - Tests all ioctls
5 * - Tests Magic Close - CONFIG_WATCHDOG_NOWAYOUT
6 * - Could be tested against softdog driver on systems that
7 *   don't have watchdog hardware.
8 * - TODO:
9 * - Enhance test to add coverage for WDIOC_GETTEMP.
10 *
11 * Reference: Documentation/watchdog/watchdog-api.rst
12  */
13 
14 #include <errno.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <signal.h>
21 #include <getopt.h>
22 #include <sys/ioctl.h>
23 #include <linux/types.h>
24 #include <linux/watchdog.h>
25 
26 #define DEFAULT_PING_RATE	1
27 
28 int fd;
29 const char v = 'V';
30 static const char sopts[] = "bdehp:st:Tn:NLf:i";
31 static const struct option lopts[] = {
32 	{"bootstatus",          no_argument, NULL, 'b'},
33 	{"disable",             no_argument, NULL, 'd'},
34 	{"enable",              no_argument, NULL, 'e'},
35 	{"help",                no_argument, NULL, 'h'},
36 	{"pingrate",      required_argument, NULL, 'p'},
37 	{"status",              no_argument, NULL, 's'},
38 	{"timeout",       required_argument, NULL, 't'},
39 	{"gettimeout",          no_argument, NULL, 'T'},
40 	{"pretimeout",    required_argument, NULL, 'n'},
41 	{"getpretimeout",       no_argument, NULL, 'N'},
42 	{"gettimeleft",		no_argument, NULL, 'L'},
43 	{"file",          required_argument, NULL, 'f'},
44 	{"info",		no_argument, NULL, 'i'},
45 	{NULL,                  no_argument, NULL, 0x0}
46 };
47 
48 /*
49  * This function simply sends an IOCTL to the driver, which in turn ticks
50  * the PC Watchdog card to reset its internal timer so it doesn't trigger
51  * a computer reset.
52  */
53 static void keep_alive(void)
54 {
55 	int dummy;
56 	int ret;
57 
58 	ret = ioctl(fd, WDIOC_KEEPALIVE, &dummy);
59 	if (!ret)
60 		printf(".");
61 }
62 
63 /*
64  * The main program.  Run the program with "-d" to disable the card,
65  * or "-e" to enable the card.
66  */
67 
68 static void term(int sig)
69 {
70 	int ret = write(fd, &v, 1);
71 
72 	close(fd);
73 	if (ret < 0)
74 		printf("\nStopping watchdog ticks failed (%d)...\n", errno);
75 	else
76 		printf("\nStopping watchdog ticks...\n");
77 	exit(0);
78 }
79 
80 static void usage(char *progname)
81 {
82 	printf("Usage: %s [options]\n", progname);
83 	printf(" -f, --file\t\tOpen watchdog device file\n");
84 	printf("\t\t\tDefault is /dev/watchdog\n");
85 	printf(" -i, --info\t\tShow watchdog_info\n");
86 	printf(" -s, --status\t\tGet status & supported features\n");
87 	printf(" -b, --bootstatus\tGet last boot status (Watchdog/POR)\n");
88 	printf(" -d, --disable\t\tTurn off the watchdog timer\n");
89 	printf(" -e, --enable\t\tTurn on the watchdog timer\n");
90 	printf(" -h, --help\t\tPrint the help message\n");
91 	printf(" -p, --pingrate=P\tSet ping rate to P seconds (default %d)\n",
92 	       DEFAULT_PING_RATE);
93 	printf(" -t, --timeout=T\tSet timeout to T seconds\n");
94 	printf(" -T, --gettimeout\tGet the timeout\n");
95 	printf(" -n, --pretimeout=T\tSet the pretimeout to T seconds\n");
96 	printf(" -N, --getpretimeout\tGet the pretimeout\n");
97 	printf(" -L, --gettimeleft\tGet the time left until timer expires\n");
98 	printf("\n");
99 	printf("Parameters are parsed left-to-right in real-time.\n");
100 	printf("Example: %s -d -t 10 -p 5 -e\n", progname);
101 	printf("Example: %s -t 12 -T -n 7 -N\n", progname);
102 }
103 
104 struct wdiof_status {
105 	int flag;
106 	const char *status_str;
107 };
108 
109 #define WDIOF_NUM_STATUS 8
110 
111 static const struct wdiof_status wdiof_status[WDIOF_NUM_STATUS] = {
112 	{WDIOF_SETTIMEOUT,  "Set timeout (in seconds)"},
113 	{WDIOF_MAGICCLOSE,  "Supports magic close char"},
114 	{WDIOF_PRETIMEOUT,  "Pretimeout (in seconds), get/set"},
115 	{WDIOF_ALARMONLY,  "Watchdog triggers a management or other external alarm not a reboot"},
116 	{WDIOF_KEEPALIVEPING,  "Keep alive ping reply"},
117 	{WDIOS_DISABLECARD,  "Turn off the watchdog timer"},
118 	{WDIOS_ENABLECARD,  "Turn on the watchdog timer"},
119 	{WDIOS_TEMPPANIC,  "Kernel panic on temperature trip"},
120 };
121 
122 static void print_status(int flags)
123 {
124 	int wdiof = 0;
125 
126 	if (flags == WDIOS_UNKNOWN) {
127 		printf("Unknown status error from WDIOC_GETSTATUS\n");
128 		return;
129 	}
130 
131 	for (wdiof = 0; wdiof < WDIOF_NUM_STATUS; wdiof++) {
132 		if (flags & wdiof_status[wdiof].flag)
133 			printf("Support/Status: %s\n",
134 				wdiof_status[wdiof].status_str);
135 	}
136 }
137 
138 #define WDIOF_NUM_BOOTSTATUS 7
139 
140 static const struct wdiof_status wdiof_bootstatus[WDIOF_NUM_BOOTSTATUS] = {
141 	{WDIOF_OVERHEAT, "Reset due to CPU overheat"},
142 	{WDIOF_FANFAULT, "Fan failed"},
143 	{WDIOF_EXTERN1, "External relay 1"},
144 	{WDIOF_EXTERN2, "External relay 2"},
145 	{WDIOF_POWERUNDER, "Power bad/power fault"},
146 	{WDIOF_CARDRESET, "Card previously reset the CPU"},
147 	{WDIOF_POWEROVER,  "Power over voltage"},
148 };
149 
150 static void print_boot_status(int flags)
151 {
152 	int wdiof = 0;
153 
154 	if (flags == WDIOF_UNKNOWN) {
155 		printf("Unknown flag error from WDIOC_GETBOOTSTATUS\n");
156 		return;
157 	}
158 
159 	if (flags == 0) {
160 		printf("Last boot is caused by: Power-On-Reset\n");
161 		return;
162 	}
163 
164 	for (wdiof = 0; wdiof < WDIOF_NUM_BOOTSTATUS; wdiof++) {
165 		if (flags & wdiof_bootstatus[wdiof].flag)
166 			printf("Last boot is caused by: %s\n",
167 				wdiof_bootstatus[wdiof].status_str);
168 	}
169 }
170 
171 int main(int argc, char *argv[])
172 {
173 	int flags;
174 	unsigned int ping_rate = DEFAULT_PING_RATE;
175 	int ret;
176 	int c;
177 	int oneshot = 0;
178 	char *file = "/dev/watchdog";
179 	struct watchdog_info info;
180 	int temperature;
181 
182 	setbuf(stdout, NULL);
183 
184 	while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
185 		if (c == 'f')
186 			file = optarg;
187 	}
188 
189 	fd = open(file, O_WRONLY);
190 
191 	if (fd == -1) {
192 		if (errno == ENOENT)
193 			printf("Watchdog device (%s) not found.\n", file);
194 		else if (errno == EACCES)
195 			printf("Run watchdog as root.\n");
196 		else
197 			printf("Watchdog device open failed %s\n",
198 				strerror(errno));
199 		exit(-1);
200 	}
201 
202 	/*
203 	 * Validate that `file` is a watchdog device
204 	 */
205 	ret = ioctl(fd, WDIOC_GETSUPPORT, &info);
206 	if (ret) {
207 		printf("WDIOC_GETSUPPORT error '%s'\n", strerror(errno));
208 		close(fd);
209 		exit(ret);
210 	}
211 
212 	optind = 0;
213 
214 	while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
215 		switch (c) {
216 		case 'b':
217 			flags = 0;
218 			oneshot = 1;
219 			ret = ioctl(fd, WDIOC_GETBOOTSTATUS, &flags);
220 			if (!ret)
221 				print_boot_status(flags);
222 			else
223 				printf("WDIOC_GETBOOTSTATUS error '%s'\n", strerror(errno));
224 			break;
225 		case 'd':
226 			flags = WDIOS_DISABLECARD;
227 			ret = ioctl(fd, WDIOC_SETOPTIONS, &flags);
228 			if (!ret)
229 				printf("Watchdog card disabled.\n");
230 			else {
231 				printf("WDIOS_DISABLECARD error '%s'\n", strerror(errno));
232 				oneshot = 1;
233 			}
234 			break;
235 		case 'e':
236 			flags = WDIOS_ENABLECARD;
237 			ret = ioctl(fd, WDIOC_SETOPTIONS, &flags);
238 			if (!ret)
239 				printf("Watchdog card enabled.\n");
240 			else {
241 				printf("WDIOS_ENABLECARD error '%s'\n", strerror(errno));
242 				oneshot = 1;
243 			}
244 			break;
245 		case 'p':
246 			ping_rate = strtoul(optarg, NULL, 0);
247 			if (!ping_rate)
248 				ping_rate = DEFAULT_PING_RATE;
249 			printf("Watchdog ping rate set to %u seconds.\n", ping_rate);
250 			break;
251 		case 's':
252 			flags = 0;
253 			oneshot = 1;
254 			ret = ioctl(fd, WDIOC_GETSTATUS, &flags);
255 			if (!ret)
256 				print_status(flags);
257 			else
258 				printf("WDIOC_GETSTATUS error '%s'\n", strerror(errno));
259 			ret = ioctl(fd, WDIOC_GETTEMP, &temperature);
260 			if (ret)
261 				printf("WDIOC_GETTEMP: '%s'\n", strerror(errno));
262 			else
263 				printf("Temperature %d\n", temperature);
264 
265 			break;
266 		case 't':
267 			flags = strtoul(optarg, NULL, 0);
268 			ret = ioctl(fd, WDIOC_SETTIMEOUT, &flags);
269 			if (!ret)
270 				printf("Watchdog timeout set to %u seconds.\n", flags);
271 			else {
272 				printf("WDIOC_SETTIMEOUT error '%s'\n", strerror(errno));
273 				oneshot = 1;
274 			}
275 			break;
276 		case 'T':
277 			oneshot = 1;
278 			ret = ioctl(fd, WDIOC_GETTIMEOUT, &flags);
279 			if (!ret)
280 				printf("WDIOC_GETTIMEOUT returns %u seconds.\n", flags);
281 			else
282 				printf("WDIOC_GETTIMEOUT error '%s'\n", strerror(errno));
283 			break;
284 		case 'n':
285 			flags = strtoul(optarg, NULL, 0);
286 			ret = ioctl(fd, WDIOC_SETPRETIMEOUT, &flags);
287 			if (!ret)
288 				printf("Watchdog pretimeout set to %u seconds.\n", flags);
289 			else {
290 				printf("WDIOC_SETPRETIMEOUT error '%s'\n", strerror(errno));
291 				oneshot = 1;
292 			}
293 			break;
294 		case 'N':
295 			oneshot = 1;
296 			ret = ioctl(fd, WDIOC_GETPRETIMEOUT, &flags);
297 			if (!ret)
298 				printf("WDIOC_GETPRETIMEOUT returns %u seconds.\n", flags);
299 			else
300 				printf("WDIOC_GETPRETIMEOUT error '%s'\n", strerror(errno));
301 			break;
302 		case 'L':
303 			oneshot = 1;
304 			ret = ioctl(fd, WDIOC_GETTIMELEFT, &flags);
305 			if (!ret)
306 				printf("WDIOC_GETTIMELEFT returns %u seconds.\n", flags);
307 			else
308 				printf("WDIOC_GETTIMELEFT error '%s'\n", strerror(errno));
309 			break;
310 		case 'f':
311 			/* Handled above */
312 			break;
313 		case 'i':
314 			/*
315 			 * watchdog_info was obtained as part of file open
316 			 * validation. So we just show it here.
317 			 */
318 			oneshot = 1;
319 			printf("watchdog_info:\n");
320 			printf(" identity:\t\t%s\n", info.identity);
321 			printf(" firmware_version:\t%u\n",
322 			       info.firmware_version);
323 			print_status(info.options);
324 			break;
325 
326 		default:
327 			usage(argv[0]);
328 			goto end;
329 		}
330 	}
331 
332 	if (oneshot)
333 		goto end;
334 
335 	printf("Watchdog Ticking Away!\n");
336 
337 	signal(SIGINT, term);
338 
339 	while (1) {
340 		keep_alive();
341 		sleep(ping_rate);
342 	}
343 end:
344 	/*
345 	 * Send specific magic character 'V' just in case Magic Close is
346 	 * enabled to ensure watchdog gets disabled on close.
347 	 */
348 	ret = write(fd, &v, 1);
349 	if (ret < 0)
350 		printf("Stopping watchdog ticks failed (%d)...\n", errno);
351 	close(fd);
352 	return 0;
353 }
354