1/* $NetBSD: wdogctl.c,v 1.20 2011/08/27 19:00:35 joerg 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#include <sys/cdefs.h> 36 37#ifndef lint 38__RCSID("$NetBSD: wdogctl.c,v 1.20 2011/08/27 19:00:35 joerg Exp $"); 39#endif 40 41 42#include <sys/param.h> 43#include <sys/ioctl.h> 44#include <sys/wdog.h> 45 46#include <err.h> 47#include <errno.h> 48#include <fcntl.h> 49#include <stdio.h> 50#include <stdlib.h> 51#include <time.h> 52#include <signal.h> 53#include <syslog.h> 54#include <unistd.h> 55#include <string.h> 56#include <paths.h> 57 58static void enable_kernel(const char *, u_int); 59static void enable_user(const char *, u_int, int); 60static void enable_ext(const char *, u_int); 61static void tickle_ext(void); 62static void disable(void); 63static void prep_wmode(struct wdog_mode *, int, const char *, u_int); 64static void list_timers(void); 65__dead static void usage(void); 66 67static int Aflag; 68 69/* Caution -- ordered list; entries >= CMD_EXT_TICKLE set timers */ 70enum cmd { 71 CMD_NONE, /* No verb given */ 72 CMD_DISABLE, 73 CMD_DOTICKLE, 74 CMD_EXT_TICKLE, 75 CMD_KERN_TICKLE, 76 CMD_NOCANCEL_TICKLE, 77 CMD_USER_TICKLE 78}; 79 80int 81main(int argc, char *argv[]) 82{ 83 enum cmd command = CMD_NONE; 84 int period_flag = 0; 85 int ch, tmp; 86 u_int period = WDOG_PERIOD_DEFAULT; 87 88 while ((ch = getopt(argc, argv, "Adekp:utx")) != -1) { 89 switch (ch) { 90 case 'A': 91 Aflag = 1; 92 break; 93 94 case 'd': 95 if (command != CMD_NONE) 96 usage(); 97 command = CMD_DISABLE; 98 break; 99 100 case 'e': 101 if (command != CMD_NONE) 102 usage(); 103 command = CMD_EXT_TICKLE; 104 break; 105 106 case 'k': 107 if (command != CMD_NONE) 108 usage(); 109 command = CMD_KERN_TICKLE; 110 break; 111 112 case 't': 113 if (command != CMD_NONE) 114 usage(); 115 command = CMD_DOTICKLE; 116 break; 117 118 case 'p': 119 period_flag = 1; 120 tmp = atoi(optarg); 121 if (tmp < 0) 122 usage(); 123 period = (unsigned int)tmp; 124 break; 125 126 case 'x': 127 case 'u': 128 if (command != CMD_NONE) 129 usage(); 130 command = 131 (ch == 'u') ? CMD_USER_TICKLE : CMD_NOCANCEL_TICKLE; 132 break; 133 134 default: 135 usage(); 136 } 137 } 138 139 argc -= optind; 140 argv += optind; 141 142 if (command < CMD_EXT_TICKLE) { 143 if (Aflag || period_flag) 144 usage(); 145 if (argc != 0) 146 usage(); 147 } else if (argc != 1) 148 usage(); 149 150 switch (command) { 151 case CMD_NONE: 152 list_timers(); 153 break; 154 case CMD_DISABLE: 155 disable(); 156 break; 157 case CMD_DOTICKLE: 158 tickle_ext(); 159 break; 160 case CMD_EXT_TICKLE: 161 enable_ext(argv[0], period); 162 break; 163 case CMD_KERN_TICKLE: 164 enable_kernel(argv[0], period); 165 break; 166 case CMD_NOCANCEL_TICKLE: 167 case CMD_USER_TICKLE: 168 enable_user(argv[0], period, command == CMD_USER_TICKLE); 169 break; 170 } 171 exit(EXIT_SUCCESS); 172} 173 174static void 175prep_wmode(struct wdog_mode *wp, int mode, const char *name, u_int period) 176{ 177 if (strlen(name) >= WDOG_NAMESIZE) 178 errx(EXIT_FAILURE, "invalid watchdog timer name: %s", name); 179 180 strlcpy(wp->wm_name, name, sizeof(wp->wm_name)); 181 wp->wm_mode = mode; 182 wp->wm_period = period; 183 if (Aflag) 184 wp->wm_mode |= WDOG_FEATURE_ALARM; 185} 186 187static void 188enable_kernel(const char *name, u_int period) 189{ 190 struct wdog_mode wm; 191 int fd; 192 193 prep_wmode(&wm, WDOG_MODE_KTICKLE, name, period); 194 195 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 196 if (fd == -1) 197 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 198 199 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) 200 err(EXIT_FAILURE, "WDOGIOC_SMODE"); 201 202 (void)close(fd); 203} 204 205static void 206enable_ext(const char *name, u_int period) 207{ 208 struct wdog_mode wm; 209 int fd; 210 211 prep_wmode(&wm, WDOG_MODE_ETICKLE, name, period); 212 213 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 214 if (fd == -1) 215 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 216 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 217 err(EXIT_FAILURE, "WDOGIOC_SMODE"); 218 } 219 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 220 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 221 wm.wm_name); 222 223 (void)close(fd); 224 return; 225} 226 227static void 228enable_user(const char *name, u_int period, int cancel_on_close) 229{ 230 struct wdog_mode wm; 231 struct timespec ts; 232 pid_t tickler; 233 int fd, rv; 234 235 prep_wmode(&wm, 236 (cancel_on_close) ? WDOG_MODE_UTICKLE : WDOG_MODE_ETICKLE, name, 237 period); 238 239 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 240 if (fd == -1) 241 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 242 243 /* ...so we can log failures to tickle the timer. */ 244 openlog("wdogctl", LOG_PERROR|LOG_PID, LOG_DAEMON); 245 246 /* 247 * We fork a child process which detaches from the controlling 248 * terminal once the timer is armed, and tickles the timer 249 * until we send it a SIGTERM. 250 */ 251 tickler = fork(); 252 if (tickler == -1) 253 err(EXIT_FAILURE, "unable to fork tickler process"); 254 else if (tickler != 0) { 255 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 256 (void)kill(tickler, SIGTERM); 257 err(EXIT_FAILURE, "WDOGIOC_SMODE"); 258 } 259 (void)close(fd); 260 return; 261 } 262 263 264 /* 265 * Wait for the watchdog to be armed. When it is, loop, 266 * tickling the timer, then waiting 1/2 the period before 267 * doing it again. 268 * 269 * If the parent fails to enable the watchdog, it will kill 270 * us. 271 */ 272 do { 273 rv = ioctl(fd, WDOGIOC_WHICH, &wm); 274 } while (rv == -1); 275 276 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 277 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 278 wm.wm_name); 279 280 /* 281 * Now detach from the controlling terminal, and just run 282 * in the background. The kernel will keep track of who 283 * we are, each time we tickle the timer. 284 */ 285 if (daemon(0, 0) == -1) { 286 /* 287 * We weren't able to go into the background. When 288 * we exit, the kernel will disable the watchdog so 289 * that the system won't die. 290 */ 291 err(EXIT_FAILURE, "unable to detach from terminal"); 292 } 293 294 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 295 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 296 wm.wm_name); 297 298 for (;;) { 299 ts.tv_sec = wm.wm_period / 2; 300 ts.tv_nsec = 0; 301 (void)nanosleep(&ts, NULL); 302 303 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 304 syslog(LOG_EMERG, 305 "unable to tickle watchdog timer %s: %m", 306 wm.wm_name); 307 } 308 /* NOTREACHED */ 309} 310 311static void 312tickle_ext(void) 313{ 314 int fd; 315 316 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 317 if (fd == -1) 318 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 319 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 320 fprintf(stderr, "Cannot tickle timer\n"); 321 322 (void)close(fd); 323} 324 325static void 326disable(void) 327{ 328 struct wdog_mode wm; 329 pid_t tickler; 330 int fd, mode; 331 332 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 333 if (fd == -1) 334 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 335 336 if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) { 337 printf("No watchdog timer running.\n"); 338 (void)close(fd); 339 return; 340 } 341 mode = wm.wm_mode & WDOG_MODE_MASK; 342 343 /* 344 * If the timer is running in UTICKLE mode, we need 345 * to kill the wdogctl(8) process that is tickling 346 * the timer. 347 */ 348 if (mode == WDOG_MODE_UTICKLE) { 349 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 350 err(EXIT_FAILURE, "WDOGIOC_GTICKLER"); 351 (void)close(fd); 352 (void)kill(tickler, SIGTERM); 353 } else { 354 wm.wm_mode = WDOG_MODE_DISARMED; 355 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 356 err(EXIT_FAILURE, "unable to disarm watchdog %s", 357 wm.wm_name); 358 } 359 (void)close(fd); 360 } 361} 362 363static void 364list_timers(void) 365{ 366 struct wdog_conf wc; 367 struct wdog_mode wm; 368 char *buf, *cp; 369 int fd, count, i, mode; 370 pid_t tickler; 371 372 fd = open(_PATH_WATCHDOG, O_RDONLY, 0644); 373 if (fd == -1) 374 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 375 376 wc.wc_names = NULL; 377 wc.wc_count = 0; 378 379 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 380 err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for count"); 381 382 count = wc.wc_count; 383 if (count == 0) { 384 printf("No watchdog timers present.\n"); 385 goto out; 386 } 387 388 buf = malloc(count * WDOG_NAMESIZE); 389 if (buf == NULL) 390 err(EXIT_FAILURE, "malloc %d byte for watchdog names", 391 count * WDOG_NAMESIZE); 392 393 wc.wc_names = buf; 394 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 395 err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for names"); 396 397 count = wc.wc_count; 398 if (count == 0) { 399 printf("No watchdog timers present.\n"); 400 free(buf); 401 goto out; 402 } 403 404 printf("Available watchdog timers:\n"); 405 for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) { 406 cp[WDOG_NAMESIZE - 1] = '\0'; 407 strlcpy(wm.wm_name, cp, sizeof(wm.wm_name)); 408 409 if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1) 410 continue; 411 mode = wm.wm_mode & WDOG_MODE_MASK; 412 if (mode == WDOG_MODE_UTICKLE) { 413 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 414 tickler = (pid_t) -1; 415 } 416 417 printf("\t%s, %u second period", cp, wm.wm_period); 418 if (mode != WDOG_MODE_DISARMED) { 419 switch(mode) { 420 case WDOG_MODE_KTICKLE: 421 printf(" [armed, kernel tickle"); 422 break; 423 case WDOG_MODE_UTICKLE: 424 printf(" [armed, user tickle"); 425 if (tickler != (pid_t) -1) 426 printf(", pid %d", tickler); 427 break; 428 case WDOG_MODE_ETICKLE: 429 printf(" [armed, external tickle"); 430 break; 431 } 432 printf("]"); 433 } 434 printf("\n"); 435 } 436 out: 437 (void)close(fd); 438} 439 440static void 441usage(void) 442{ 443 444 fprintf(stderr, "usage: %s\n", getprogname()); 445 fprintf(stderr, " %s -d\n", getprogname()); 446 fprintf(stderr, " %s -e [-A] [-p seconds] timer\n", 447 getprogname()); 448 fprintf(stderr, " %s -k [-A] [-p seconds] timer\n", 449 getprogname()); 450 fprintf(stderr, " %s -t\n", getprogname()); 451 fprintf(stderr, " %s -u [-A] [-p seconds] timer\n", 452 getprogname()); 453 fprintf(stderr, " %s -x [-A] [-p seconds] timer\n", 454 getprogname()); 455 456 exit(1); 457} 458