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