1 /* $NetBSD: wdogctl.c,v 1.18 2009/03/16 13:37:45 lukem 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.18 2009/03/16 13:37:45 lukem 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 205 void 206 enable_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 return; 223 } 224 225 void 226 enable_user(const char *name, u_int period, int cancel_on_close) 227 { 228 struct wdog_mode wm; 229 struct timespec ts; 230 pid_t tickler; 231 int fd, rv; 232 233 prep_wmode(&wm, 234 (cancel_on_close) ? WDOG_MODE_UTICKLE : WDOG_MODE_ETICKLE, name, 235 period); 236 237 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 238 if (fd == -1) 239 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 240 241 /* ...so we can log failures to tickle the timer. */ 242 openlog("wdogctl", LOG_PERROR|LOG_PID, LOG_DAEMON); 243 244 /* 245 * We fork a child process which detaches from the controlling 246 * terminal once the timer is armed, and tickles the timer 247 * until we send it a SIGTERM. 248 */ 249 tickler = fork(); 250 if (tickler == -1) 251 err(EXIT_FAILURE, "unable to fork tickler process"); 252 else if (tickler != 0) { 253 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 254 (void)kill(tickler, SIGTERM); 255 err(EXIT_FAILURE, "WDOGIOC_SMODE"); 256 } 257 (void)close(fd); 258 return; 259 } 260 261 262 /* 263 * Wait for the watchdog to be armed. When it is, loop, 264 * tickling the timer, then waiting 1/2 the period before 265 * doing it again. 266 * 267 * If the parent fails to enable the watchdog, it will kill 268 * us. 269 */ 270 do { 271 rv = ioctl(fd, WDOGIOC_WHICH, &wm); 272 } while (rv == -1); 273 274 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 275 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 276 wm.wm_name); 277 278 /* 279 * Now detach from the controlling terminal, and just run 280 * in the background. The kernel will keep track of who 281 * we are, each time we tickle the timer. 282 */ 283 if (daemon(0, 0) == -1) { 284 /* 285 * We weren't able to go into the background. When 286 * we exit, the kernel will disable the watchdog so 287 * that the system won't die. 288 */ 289 err(EXIT_FAILURE, "unable to detach from terminal"); 290 } 291 292 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 293 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 294 wm.wm_name); 295 296 for (;;) { 297 ts.tv_sec = wm.wm_period / 2; 298 ts.tv_nsec = 0; 299 (void)nanosleep(&ts, NULL); 300 301 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 302 syslog(LOG_EMERG, 303 "unable to tickle watchdog timer %s: %m", 304 wm.wm_name); 305 } 306 /* NOTREACHED */ 307 } 308 309 void 310 tickle_ext() 311 { 312 int fd; 313 314 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 315 if (fd == -1) 316 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 317 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 318 fprintf(stderr, "Cannot tickle timer\n"); 319 } 320 321 void 322 disable(void) 323 { 324 struct wdog_mode wm; 325 pid_t tickler; 326 int fd, mode; 327 328 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 329 if (fd == -1) 330 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 331 332 if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) { 333 printf("No watchdog timer running.\n"); 334 return; 335 } 336 mode = wm.wm_mode & WDOG_MODE_MASK; 337 338 /* 339 * If the timer is running in UTICKLE mode, we need 340 * to kill the wdogctl(8) process that is tickling 341 * the timer. 342 */ 343 if (mode == WDOG_MODE_UTICKLE) { 344 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 345 err(EXIT_FAILURE, "WDOGIOC_GTICKLER"); 346 (void)close(fd); 347 (void)kill(tickler, SIGTERM); 348 } else { 349 wm.wm_mode = WDOG_MODE_DISARMED; 350 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 351 err(EXIT_FAILURE, "unable to disarm watchdog %s", 352 wm.wm_name); 353 } 354 (void)close(fd); 355 } 356 } 357 358 void 359 list_timers(void) 360 { 361 struct wdog_conf wc; 362 struct wdog_mode wm; 363 char *buf, *cp; 364 int fd, count, i, mode; 365 pid_t tickler; 366 367 fd = open(_PATH_WATCHDOG, O_RDONLY, 0644); 368 if (fd == -1) 369 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 370 371 wc.wc_names = NULL; 372 wc.wc_count = 0; 373 374 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 375 err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for count"); 376 377 count = wc.wc_count; 378 if (count == 0) { 379 printf("No watchdog timers present.\n"); 380 goto out; 381 } 382 383 buf = malloc(count * WDOG_NAMESIZE); 384 if (buf == NULL) 385 err(EXIT_FAILURE, "malloc %d byte for watchdog names", 386 count * WDOG_NAMESIZE); 387 388 wc.wc_names = buf; 389 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 390 err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for names"); 391 392 count = wc.wc_count; 393 if (count == 0) { 394 printf("No watchdog timers present.\n"); 395 free(buf); 396 goto out; 397 } 398 399 printf("Available watchdog timers:\n"); 400 for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) { 401 cp[WDOG_NAMESIZE - 1] = '\0'; 402 strlcpy(wm.wm_name, cp, sizeof(wm.wm_name)); 403 404 if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1) 405 continue; 406 mode = wm.wm_mode & WDOG_MODE_MASK; 407 if (mode == WDOG_MODE_UTICKLE) { 408 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 409 tickler = (pid_t) -1; 410 } 411 412 printf("\t%s, %u second period", cp, wm.wm_period); 413 if (mode != WDOG_MODE_DISARMED) { 414 switch(mode) { 415 case WDOG_MODE_KTICKLE: 416 printf(" [armed, kernel tickle"); 417 break; 418 case WDOG_MODE_UTICKLE: 419 printf(" [armed, user tickle"); 420 if (tickler != (pid_t) -1) 421 printf(", pid %d", tickler); 422 break; 423 case WDOG_MODE_ETICKLE: 424 printf(" [armed, external tickle"); 425 break; 426 } 427 printf("]"); 428 } 429 printf("\n"); 430 } 431 out: 432 (void)close(fd); 433 } 434 435 void 436 usage(void) 437 { 438 439 fprintf(stderr, "usage: %s\n", getprogname()); 440 fprintf(stderr, " %s -d\n", getprogname()); 441 fprintf(stderr, " %s -e [-A] [-p seconds] timer\n", 442 getprogname()); 443 fprintf(stderr, " %s -k [-A] [-p seconds] timer\n", 444 getprogname()); 445 fprintf(stderr, " %s -t\n", getprogname()); 446 fprintf(stderr, " %s -u [-A] [-p seconds] timer\n", 447 getprogname()); 448 fprintf(stderr, " %s -x [-A] [-p seconds] timer\n", 449 getprogname()); 450 451 exit(1); 452 } 453