watchdog-test.c 9.3 KB

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