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