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