1 /* $NetBSD: wdogctl.c,v 1.7 2001/02/20 23:25:29 cgd Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Zembu Labs, Inc. 5 * All rights reserved. 6 * 7 * Author: Jason R. Thorpe <thorpej@zembu.com> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Zembu Labs, Inc. 20 * 4. Neither the name of Zembu Labs nor the names of its employees may 21 * be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS 25 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- 26 * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- 27 * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/ioctl.h> 38 #include <sys/wdog.h> 39 40 #include <err.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <time.h> 46 #include <signal.h> 47 #include <syslog.h> 48 #include <unistd.h> 49 #include <string.h> 50 51 #define _PATH_WATCHDOG "/dev/watchdog" 52 53 int main(int, char *[]); 54 void enable_kernel(const char *, u_int); 55 void enable_user(const char *, u_int); 56 void disable(void); 57 void list_timers(void); 58 void usage(void); 59 60 int Aflag; 61 62 int 63 main(int argc, char *argv[]) 64 { 65 int cmds = 0, kflag = 0, uflag = 0, dflag = 0, ch; 66 u_int period = WDOG_PERIOD_DEFAULT; 67 68 while ((ch = getopt(argc, argv, "Adkp:u")) != -1) { 69 switch (ch) { 70 case 'A': 71 if (cmds == 0 || dflag) 72 usage(); 73 Aflag = 1; 74 break; 75 76 case 'd': 77 dflag = 1; 78 cmds++; 79 break; 80 81 case 'k': 82 kflag = 1; 83 cmds++; 84 break; 85 86 case 'p': 87 if (cmds == 0 || dflag) 88 usage(); 89 period = atoi(optarg); 90 if (period == -1) 91 usage(); 92 break; 93 94 case 'u': 95 uflag = 1; 96 cmds++; 97 break; 98 99 default: 100 usage(); 101 } 102 } 103 104 argc -= optind; 105 argv += optind; 106 107 if (cmds > 1) 108 usage(); 109 110 if (kflag) { 111 if (argc != 1) 112 usage(); 113 enable_kernel(argv[0], period); 114 } else if (uflag) { 115 if (argc != 1) 116 usage(); 117 enable_user(argv[0], period); 118 } else if (dflag) { 119 if (argc != 0) 120 usage(); 121 disable(); 122 } else 123 list_timers(); 124 125 exit(0); 126 } 127 128 void 129 enable_kernel(const char *name, u_int period) 130 { 131 struct wdog_mode wm; 132 int fd; 133 134 if (strlen(name) >= WDOG_NAMESIZE) 135 errx(1, "invalid watchdog timer name: %s", name); 136 strcpy(wm.wm_name, name); 137 wm.wm_mode = WDOG_MODE_KTICKLE; 138 wm.wm_period = period; 139 140 if (Aflag) 141 wm.wm_mode |= WDOG_FEATURE_ALARM; 142 143 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 144 if (fd == -1) 145 err(1, "open %s", _PATH_WATCHDOG); 146 147 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) 148 err(1, "WDOGIOC_SMODE"); 149 } 150 151 void 152 enable_user(const char *name, u_int period) 153 { 154 struct wdog_mode wm; 155 struct timespec ts; 156 pid_t tickler; 157 int fd, rv; 158 159 if (strlen(name) >= WDOG_NAMESIZE) 160 errx(1, "invalid watchdog timer name: %s", name); 161 strcpy(wm.wm_name, name); 162 wm.wm_mode = WDOG_MODE_UTICKLE; 163 wm.wm_period = period; 164 165 if (Aflag) 166 wm.wm_mode |= WDOG_FEATURE_ALARM; 167 168 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 169 if (fd == -1) 170 err(1, "open %s", _PATH_WATCHDOG); 171 172 /* ...so we can log failures to tickle the timer. */ 173 openlog("wdogctl", LOG_PERROR|LOG_PID, LOG_DAEMON); 174 175 /* 176 * We fork a child process which detaches from the controlling 177 * terminal once the timer is armed, and tickles the timer 178 * until we send it a SIGTERM. 179 */ 180 tickler = fork(); 181 if (tickler == -1) 182 err(1, "unable to fork tickler process"); 183 else if (tickler != 0) { 184 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 185 err(1, "WDOGIOC_SMODE"); 186 (void) kill(tickler, SIGTERM); 187 } 188 (void) close(fd); 189 return; 190 } 191 192 193 /* 194 * Wait for the watchdog to be armed. When it is, loop, 195 * tickling the timer, then waiting 1/2 the period before 196 * doing it again. 197 * 198 * If the parent fails to enable the watchdog, it will kill 199 * us. 200 */ 201 do { 202 rv = ioctl(fd, WDOGIOC_WHICH, &wm); 203 } while (rv == -1); 204 205 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 206 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 207 wm.wm_name); 208 209 /* 210 * Now detach from the controlling terminal, and just run 211 * in the background. The kernel will keep track of who 212 * we are, each time we tickle the timer. 213 */ 214 if (daemon(0, 0) == -1) { 215 /* 216 * We weren't able to go into the background. When 217 * we exit, the kernel will disable the watchdog so 218 * that the system won't die. 219 */ 220 err(1, "unable to detach from terminal"); 221 } 222 223 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 224 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 225 wm.wm_name); 226 227 for (;;) { 228 ts.tv_sec = wm.wm_period / 2; 229 ts.tv_nsec = 0; 230 (void) nanosleep(&ts, NULL); 231 232 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 233 syslog(LOG_EMERG, 234 "unable to tickle watchdog timer %s: %m", 235 wm.wm_name); 236 } 237 /* NOTREACHED */ 238 } 239 240 void 241 disable(void) 242 { 243 struct wdog_mode wm; 244 pid_t tickler; 245 int fd; 246 247 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 248 if (fd == -1) 249 err(1, "open %s", _PATH_WATCHDOG); 250 251 if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) { 252 printf("No watchdog timer running.\n"); 253 return; 254 } 255 256 /* 257 * If the timer is running in UTICKLE mode, we need 258 * to kill the wdogctl(8) process that is tickling 259 * the timer. 260 */ 261 if (wm.wm_mode == WDOG_MODE_UTICKLE) { 262 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 263 err(1, "WDOGIOC_GTICKLER"); 264 (void) close(fd); 265 (void) kill(tickler, SIGTERM); 266 } else { 267 wm.wm_mode = WDOG_MODE_DISARMED; 268 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) 269 err(1, "unable to disarm watchdog %s", wm.wm_name); 270 (void) close(fd); 271 } 272 } 273 274 void 275 list_timers(void) 276 { 277 struct wdog_conf wc; 278 struct wdog_mode wm; 279 char *buf, *cp; 280 int fd, count, i; 281 pid_t tickler; 282 283 fd = open(_PATH_WATCHDOG, O_RDONLY, 0644); 284 if (fd == -1) 285 err(1, "open %s", _PATH_WATCHDOG); 286 287 wc.wc_names = NULL; 288 wc.wc_count = 0; 289 290 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 291 err(1, "ioctl WDOGIOC_GWDOGS for count"); 292 293 count = wc.wc_count; 294 if (count == 0) { 295 printf("No watchdog timers present.\n"); 296 goto out; 297 } 298 299 buf = malloc(count * WDOG_NAMESIZE); 300 if (buf == NULL) 301 err(1, "malloc %d byte for watchdog names", 302 count * WDOG_NAMESIZE); 303 304 wc.wc_names = buf; 305 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 306 err(1, "ioctl WDOGIOC_GWDOGS for names"); 307 308 count = wc.wc_count; 309 if (count == 0) { 310 printf("No watchdog timers present.\n"); 311 free(buf); 312 goto out; 313 } 314 315 printf("Available watchdog timers:\n"); 316 for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) { 317 cp[WDOG_NAMESIZE - 1] = '\0'; 318 strcpy(wm.wm_name, cp); 319 320 if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1) 321 wm.wm_mode = -1; 322 else if (wm.wm_mode == WDOG_MODE_UTICKLE) { 323 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 324 tickler = (pid_t) -1; 325 } 326 327 printf("\t%s, %u second period", cp, wm.wm_period); 328 if (wm.wm_mode != WDOG_MODE_DISARMED) { 329 printf(" [armed, %s tickle", 330 wm.wm_mode == WDOG_MODE_KTICKLE ? 331 "kernel" : "user"); 332 if (wm.wm_mode == WDOG_MODE_UTICKLE && 333 tickler != (pid_t) -1) 334 printf(", pid %d", tickler); 335 printf("]"); 336 } 337 printf("\n"); 338 } 339 out: 340 (void) close(fd); 341 } 342 343 void 344 usage(void) 345 { 346 fprintf(stderr, "Usage: %s\n", getprogname()); 347 fprintf(stderr, " %s -k [-A] [-p seconds] timer\n", 348 getprogname()); 349 fprintf(stderr, " %s -u [-A] [-p seconds] timer\n", 350 getprogname()); 351 fprintf(stderr, " %s -d\n", getprogname()); 352 353 exit(1); 354 } 355