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(bool with_release) 75 { 76 if (wdt_fd < 0) 77 return; 78 79 if (with_release) { 80 if (write(wdt_fd, "V", 1) < 0) 81 ERROR("WDT failed to write release: %m\n"); 82 } 83 84 if (close(wdt_fd) == -1) 85 ERROR("WDT failed to close watchdog: %m\n"); 86 87 wdt_fd = -1; 88 } 89 90 static int watchdog_set_drv_timeout(void) 91 { 92 if (wdt_fd < 0) 93 return -1; 94 95 return ioctl(wdt_fd, WDIOC_SETTIMEOUT, &wdt_drv_timeout); 96 } 97 98 static void watchdog_print_status(void) 99 { 100 struct watchdog_info wdt_info; 101 int bootstatus; 102 103 if (wdt_fd < 0) 104 return; 105 106 if (ioctl(wdt_fd, WDIOC_GETSUPPORT, &wdt_info)) { 107 DEBUG(2, "Watchdog GETSUPPORT failed\n"); 108 return; 109 } 110 111 if (!(wdt_info.options & WDIOF_CARDRESET)) { 112 DEBUG(2, "Watchdog does not have CARDRESET support\n"); 113 return; 114 } 115 116 if (ioctl(wdt_fd, WDIOC_GETBOOTSTATUS, &bootstatus)) { 117 DEBUG(2, "Watchdog GETBOOTSTATUS failed\n"); 118 return; 119 } 120 121 if (bootstatus & WDIOF_CARDRESET) 122 LOG("Watchdog has previously reset the system\n"); 123 else 124 DEBUG(2, "Watchdog did not previously reset the system\n"); 125 } 126 127 void watchdog_set_magicclose(bool val) 128 { 129 wdt_magicclose = val; 130 } 131 132 bool watchdog_get_magicclose(void) 133 { 134 return wdt_magicclose; 135 } 136 137 void watchdog_set_stopped(bool val) 138 { 139 if (val) { 140 uloop_timeout_cancel(&wdt_timeout); 141 142 watchdog_close(wdt_magicclose); 143 } 144 else { 145 watchdog_open(true); 146 watchdog_set_drv_timeout(); 147 watchdog_timeout_cb(&wdt_timeout); 148 } 149 } 150 151 bool watchdog_get_stopped(void) 152 { 153 return !wdt_timeout.pending; 154 } 155 156 int watchdog_timeout(int timeout) 157 { 158 if (timeout) { 159 DEBUG(4, "Set watchdog timeout: %ds\n", timeout); 160 wdt_drv_timeout = timeout; 161 162 if (wdt_fd >= 0) 163 watchdog_set_drv_timeout(); 164 } 165 166 return wdt_drv_timeout; 167 } 168 169 int watchdog_frequency(int frequency) 170 { 171 if (frequency) { 172 DEBUG(4, "Set watchdog frequency: %ds\n", frequency); 173 wdt_frequency = frequency; 174 } 175 176 return wdt_frequency; 177 } 178 179 char* watchdog_fd(void) 180 { 181 static char fd_buf[12]; 182 183 if (wdt_fd < 0) 184 return NULL; 185 186 snprintf(fd_buf, sizeof(fd_buf), "%d", wdt_fd); 187 188 return fd_buf; 189 } 190 191 void watchdog_init(int preinit) 192 { 193 wdt_timeout.cb = watchdog_timeout_cb; 194 195 if (watchdog_open(!preinit) < 0) 196 return; 197 198 LOG("- watchdog -\n"); 199 watchdog_set_drv_timeout(); 200 watchdog_timeout_cb(&wdt_timeout); 201 202 DEBUG(4, "Opened watchdog with timeout %ds\n", watchdog_timeout(0)); 203 204 watchdog_print_status(); 205 } 206 207 208 void watchdog_set_cloexec(bool val) 209 { 210 if (wdt_fd < 0) 211 return; 212 213 int flags = fcntl(wdt_fd, F_GETFD); 214 if (val) 215 flags |= FD_CLOEXEC; 216 else 217 flags &= ~FD_CLOEXEC; 218 fcntl(wdt_fd, F_SETFD, flags); 219 } 220
This page was automatically generated by LXR 0.3.1. • OpenWrt