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