1 1.21 pgoyette /* $NetBSD: wdogctl.c,v 1.21 2015/05/06 23:08:30 pgoyette Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /*- 4 1.1 thorpej * Copyright (c) 2000 Zembu Labs, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Author: Jason R. Thorpe <thorpej (at) zembu.com> 8 1.1 thorpej * 9 1.1 thorpej * Redistribution and use in source and binary forms, with or without 10 1.1 thorpej * modification, are permitted provided that the following conditions 11 1.1 thorpej * are met: 12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer. 14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 16 1.1 thorpej * documentation and/or other materials provided with the distribution. 17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 18 1.1 thorpej * must display the following acknowledgement: 19 1.1 thorpej * This product includes software developed by Zembu Labs, Inc. 20 1.1 thorpej * 4. Neither the name of Zembu Labs nor the names of its employees may 21 1.1 thorpej * be used to endorse or promote products derived from this software 22 1.1 thorpej * without specific prior written permission. 23 1.1 thorpej * 24 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS 25 1.1 thorpej * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- 26 1.1 thorpej * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- 27 1.1 thorpej * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 1.1 thorpej * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29 1.1 thorpej * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 1.1 thorpej * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 1.1 thorpej * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 1.1 thorpej * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33 1.1 thorpej * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 1.1 thorpej */ 35 1.8 agc #include <sys/cdefs.h> 36 1.8 agc 37 1.8 agc #ifndef lint 38 1.21 pgoyette __RCSID("$NetBSD: wdogctl.c,v 1.21 2015/05/06 23:08:30 pgoyette Exp $"); 39 1.8 agc #endif 40 1.8 agc 41 1.1 thorpej 42 1.1 thorpej #include <sys/param.h> 43 1.1 thorpej #include <sys/ioctl.h> 44 1.1 thorpej #include <sys/wdog.h> 45 1.1 thorpej 46 1.1 thorpej #include <err.h> 47 1.1 thorpej #include <errno.h> 48 1.1 thorpej #include <fcntl.h> 49 1.1 thorpej #include <stdio.h> 50 1.1 thorpej #include <stdlib.h> 51 1.1 thorpej #include <time.h> 52 1.1 thorpej #include <signal.h> 53 1.1 thorpej #include <syslog.h> 54 1.1 thorpej #include <unistd.h> 55 1.2 minoura #include <string.h> 56 1.21 pgoyette #include <paths.h> 57 1.1 thorpej 58 1.20 joerg static void enable_kernel(const char *, u_int); 59 1.20 joerg static void enable_user(const char *, u_int, int); 60 1.20 joerg static void enable_ext(const char *, u_int); 61 1.20 joerg static void tickle_ext(void); 62 1.20 joerg static void disable(void); 63 1.20 joerg static void prep_wmode(struct wdog_mode *, int, const char *, u_int); 64 1.20 joerg static void list_timers(void); 65 1.20 joerg __dead static void usage(void); 66 1.1 thorpej 67 1.20 joerg static int Aflag; 68 1.1 thorpej 69 1.11 smb /* Caution -- ordered list; entries >= CMD_EXT_TICKLE set timers */ 70 1.11 smb enum cmd { 71 1.11 smb CMD_NONE, /* No verb given */ 72 1.11 smb CMD_DISABLE, 73 1.11 smb CMD_DOTICKLE, 74 1.11 smb CMD_EXT_TICKLE, 75 1.11 smb CMD_KERN_TICKLE, 76 1.16 dyoung CMD_NOCANCEL_TICKLE, 77 1.11 smb CMD_USER_TICKLE 78 1.11 smb }; 79 1.11 smb 80 1.1 thorpej int 81 1.1 thorpej main(int argc, char *argv[]) 82 1.1 thorpej { 83 1.11 smb enum cmd command = CMD_NONE; 84 1.11 smb int period_flag = 0; 85 1.18 lukem int ch, tmp; 86 1.1 thorpej u_int period = WDOG_PERIOD_DEFAULT; 87 1.1 thorpej 88 1.16 dyoung while ((ch = getopt(argc, argv, "Adekp:utx")) != -1) { 89 1.1 thorpej switch (ch) { 90 1.1 thorpej case 'A': 91 1.1 thorpej Aflag = 1; 92 1.1 thorpej break; 93 1.1 thorpej 94 1.1 thorpej case 'd': 95 1.11 smb if (command != CMD_NONE) 96 1.11 smb usage(); 97 1.11 smb command = CMD_DISABLE; 98 1.11 smb break; 99 1.11 smb 100 1.11 smb case 'e': 101 1.11 smb if (command != CMD_NONE) 102 1.11 smb usage(); 103 1.11 smb command = CMD_EXT_TICKLE; 104 1.1 thorpej break; 105 1.1 thorpej 106 1.1 thorpej case 'k': 107 1.11 smb if (command != CMD_NONE) 108 1.11 smb usage(); 109 1.11 smb command = CMD_KERN_TICKLE; 110 1.11 smb break; 111 1.11 smb 112 1.11 smb case 't': 113 1.11 smb if (command != CMD_NONE) 114 1.11 smb usage(); 115 1.11 smb command = CMD_DOTICKLE; 116 1.1 thorpej break; 117 1.1 thorpej 118 1.1 thorpej case 'p': 119 1.11 smb period_flag = 1; 120 1.18 lukem tmp = atoi(optarg); 121 1.18 lukem if (tmp < 0) 122 1.1 thorpej usage(); 123 1.18 lukem period = (unsigned int)tmp; 124 1.1 thorpej break; 125 1.1 thorpej 126 1.16 dyoung case 'x': 127 1.1 thorpej case 'u': 128 1.11 smb if (command != CMD_NONE) 129 1.11 smb usage(); 130 1.16 dyoung command = 131 1.16 dyoung (ch == 'u') ? CMD_USER_TICKLE : CMD_NOCANCEL_TICKLE; 132 1.1 thorpej break; 133 1.1 thorpej 134 1.1 thorpej default: 135 1.1 thorpej usage(); 136 1.1 thorpej } 137 1.1 thorpej } 138 1.1 thorpej 139 1.1 thorpej argc -= optind; 140 1.1 thorpej argv += optind; 141 1.1 thorpej 142 1.11 smb if (command < CMD_EXT_TICKLE) { 143 1.11 smb if (Aflag || period_flag) 144 1.11 smb usage(); 145 1.11 smb if (argc != 0) 146 1.1 thorpej usage(); 147 1.14 dyoung } else if (argc != 1) 148 1.14 dyoung usage(); 149 1.1 thorpej 150 1.11 smb switch (command) { 151 1.14 dyoung case CMD_NONE: 152 1.14 dyoung list_timers(); 153 1.14 dyoung break; 154 1.14 dyoung case CMD_DISABLE: 155 1.14 dyoung disable(); 156 1.14 dyoung break; 157 1.14 dyoung case CMD_DOTICKLE: 158 1.14 dyoung tickle_ext(); 159 1.14 dyoung break; 160 1.14 dyoung case CMD_EXT_TICKLE: 161 1.14 dyoung enable_ext(argv[0], period); 162 1.14 dyoung break; 163 1.14 dyoung case CMD_KERN_TICKLE: 164 1.14 dyoung enable_kernel(argv[0], period); 165 1.14 dyoung break; 166 1.16 dyoung case CMD_NOCANCEL_TICKLE: 167 1.14 dyoung case CMD_USER_TICKLE: 168 1.16 dyoung enable_user(argv[0], period, command == CMD_USER_TICKLE); 169 1.14 dyoung break; 170 1.11 smb } 171 1.14 dyoung exit(EXIT_SUCCESS); 172 1.1 thorpej } 173 1.1 thorpej 174 1.20 joerg static void 175 1.11 smb prep_wmode(struct wdog_mode *wp, int mode, const char *name, u_int period) 176 1.11 smb { 177 1.11 smb if (strlen(name) >= WDOG_NAMESIZE) 178 1.14 dyoung errx(EXIT_FAILURE, "invalid watchdog timer name: %s", name); 179 1.11 smb 180 1.11 smb strlcpy(wp->wm_name, name, sizeof(wp->wm_name)); 181 1.11 smb wp->wm_mode = mode; 182 1.11 smb wp->wm_period = period; 183 1.11 smb if (Aflag) 184 1.11 smb wp->wm_mode |= WDOG_FEATURE_ALARM; 185 1.11 smb } 186 1.11 smb 187 1.20 joerg static void 188 1.1 thorpej enable_kernel(const char *name, u_int period) 189 1.1 thorpej { 190 1.1 thorpej struct wdog_mode wm; 191 1.1 thorpej int fd; 192 1.1 thorpej 193 1.11 smb prep_wmode(&wm, WDOG_MODE_KTICKLE, name, period); 194 1.1 thorpej 195 1.1 thorpej fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 196 1.1 thorpej if (fd == -1) 197 1.14 dyoung err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 198 1.1 thorpej 199 1.1 thorpej if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) 200 1.14 dyoung err(EXIT_FAILURE, "WDOGIOC_SMODE"); 201 1.19 wiz 202 1.19 wiz (void)close(fd); 203 1.1 thorpej } 204 1.1 thorpej 205 1.20 joerg static void 206 1.11 smb enable_ext(const char *name, u_int period) 207 1.11 smb { 208 1.11 smb struct wdog_mode wm; 209 1.11 smb int fd; 210 1.11 smb 211 1.11 smb prep_wmode(&wm, WDOG_MODE_ETICKLE, name, period); 212 1.11 smb 213 1.11 smb fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 214 1.11 smb if (fd == -1) 215 1.14 dyoung err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 216 1.11 smb if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 217 1.14 dyoung err(EXIT_FAILURE, "WDOGIOC_SMODE"); 218 1.11 smb } 219 1.11 smb if (ioctl(fd, WDOGIOC_TICKLE) == -1) 220 1.11 smb syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 221 1.11 smb wm.wm_name); 222 1.19 wiz 223 1.19 wiz (void)close(fd); 224 1.11 smb return; 225 1.11 smb } 226 1.11 smb 227 1.20 joerg static void 228 1.16 dyoung enable_user(const char *name, u_int period, int cancel_on_close) 229 1.1 thorpej { 230 1.1 thorpej struct wdog_mode wm; 231 1.1 thorpej struct timespec ts; 232 1.1 thorpej pid_t tickler; 233 1.1 thorpej int fd, rv; 234 1.1 thorpej 235 1.16 dyoung prep_wmode(&wm, 236 1.16 dyoung (cancel_on_close) ? WDOG_MODE_UTICKLE : WDOG_MODE_ETICKLE, name, 237 1.16 dyoung period); 238 1.1 thorpej 239 1.1 thorpej fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 240 1.1 thorpej if (fd == -1) 241 1.14 dyoung err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 242 1.1 thorpej 243 1.1 thorpej /* ...so we can log failures to tickle the timer. */ 244 1.4 lukem openlog("wdogctl", LOG_PERROR|LOG_PID, LOG_DAEMON); 245 1.1 thorpej 246 1.1 thorpej /* 247 1.1 thorpej * We fork a child process which detaches from the controlling 248 1.1 thorpej * terminal once the timer is armed, and tickles the timer 249 1.1 thorpej * until we send it a SIGTERM. 250 1.1 thorpej */ 251 1.1 thorpej tickler = fork(); 252 1.1 thorpej if (tickler == -1) 253 1.14 dyoung err(EXIT_FAILURE, "unable to fork tickler process"); 254 1.1 thorpej else if (tickler != 0) { 255 1.1 thorpej if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 256 1.15 dyoung (void)kill(tickler, SIGTERM); 257 1.14 dyoung err(EXIT_FAILURE, "WDOGIOC_SMODE"); 258 1.1 thorpej } 259 1.14 dyoung (void)close(fd); 260 1.1 thorpej return; 261 1.1 thorpej } 262 1.1 thorpej 263 1.1 thorpej 264 1.1 thorpej /* 265 1.1 thorpej * Wait for the watchdog to be armed. When it is, loop, 266 1.1 thorpej * tickling the timer, then waiting 1/2 the period before 267 1.1 thorpej * doing it again. 268 1.1 thorpej * 269 1.1 thorpej * If the parent fails to enable the watchdog, it will kill 270 1.1 thorpej * us. 271 1.1 thorpej */ 272 1.1 thorpej do { 273 1.1 thorpej rv = ioctl(fd, WDOGIOC_WHICH, &wm); 274 1.1 thorpej } while (rv == -1); 275 1.1 thorpej 276 1.1 thorpej if (ioctl(fd, WDOGIOC_TICKLE) == -1) 277 1.1 thorpej syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 278 1.1 thorpej wm.wm_name); 279 1.1 thorpej 280 1.1 thorpej /* 281 1.1 thorpej * Now detach from the controlling terminal, and just run 282 1.1 thorpej * in the background. The kernel will keep track of who 283 1.1 thorpej * we are, each time we tickle the timer. 284 1.1 thorpej */ 285 1.1 thorpej if (daemon(0, 0) == -1) { 286 1.1 thorpej /* 287 1.1 thorpej * We weren't able to go into the background. When 288 1.1 thorpej * we exit, the kernel will disable the watchdog so 289 1.1 thorpej * that the system won't die. 290 1.1 thorpej */ 291 1.14 dyoung err(EXIT_FAILURE, "unable to detach from terminal"); 292 1.1 thorpej } 293 1.1 thorpej 294 1.1 thorpej if (ioctl(fd, WDOGIOC_TICKLE) == -1) 295 1.1 thorpej syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 296 1.1 thorpej wm.wm_name); 297 1.1 thorpej 298 1.1 thorpej for (;;) { 299 1.1 thorpej ts.tv_sec = wm.wm_period / 2; 300 1.1 thorpej ts.tv_nsec = 0; 301 1.14 dyoung (void)nanosleep(&ts, NULL); 302 1.1 thorpej 303 1.1 thorpej if (ioctl(fd, WDOGIOC_TICKLE) == -1) 304 1.1 thorpej syslog(LOG_EMERG, 305 1.1 thorpej "unable to tickle watchdog timer %s: %m", 306 1.1 thorpej wm.wm_name); 307 1.1 thorpej } 308 1.1 thorpej /* NOTREACHED */ 309 1.1 thorpej } 310 1.1 thorpej 311 1.20 joerg static void 312 1.20 joerg tickle_ext(void) 313 1.11 smb { 314 1.11 smb int fd; 315 1.11 smb 316 1.11 smb fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 317 1.11 smb if (fd == -1) 318 1.14 dyoung err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 319 1.11 smb if (ioctl(fd, WDOGIOC_TICKLE) == -1) 320 1.11 smb fprintf(stderr, "Cannot tickle timer\n"); 321 1.19 wiz 322 1.19 wiz (void)close(fd); 323 1.11 smb } 324 1.11 smb 325 1.20 joerg static void 326 1.1 thorpej disable(void) 327 1.1 thorpej { 328 1.1 thorpej struct wdog_mode wm; 329 1.1 thorpej pid_t tickler; 330 1.13 drochner int fd, mode; 331 1.1 thorpej 332 1.1 thorpej fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 333 1.1 thorpej if (fd == -1) 334 1.14 dyoung err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 335 1.1 thorpej 336 1.1 thorpej if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) { 337 1.1 thorpej printf("No watchdog timer running.\n"); 338 1.19 wiz (void)close(fd); 339 1.1 thorpej return; 340 1.1 thorpej } 341 1.13 drochner mode = wm.wm_mode & WDOG_MODE_MASK; 342 1.1 thorpej 343 1.1 thorpej /* 344 1.1 thorpej * If the timer is running in UTICKLE mode, we need 345 1.1 thorpej * to kill the wdogctl(8) process that is tickling 346 1.1 thorpej * the timer. 347 1.1 thorpej */ 348 1.13 drochner if (mode == WDOG_MODE_UTICKLE) { 349 1.1 thorpej if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 350 1.14 dyoung err(EXIT_FAILURE, "WDOGIOC_GTICKLER"); 351 1.14 dyoung (void)close(fd); 352 1.14 dyoung (void)kill(tickler, SIGTERM); 353 1.1 thorpej } else { 354 1.1 thorpej wm.wm_mode = WDOG_MODE_DISARMED; 355 1.14 dyoung if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 356 1.14 dyoung err(EXIT_FAILURE, "unable to disarm watchdog %s", 357 1.14 dyoung wm.wm_name); 358 1.14 dyoung } 359 1.14 dyoung (void)close(fd); 360 1.1 thorpej } 361 1.1 thorpej } 362 1.1 thorpej 363 1.20 joerg static void 364 1.1 thorpej list_timers(void) 365 1.1 thorpej { 366 1.1 thorpej struct wdog_conf wc; 367 1.1 thorpej struct wdog_mode wm; 368 1.1 thorpej char *buf, *cp; 369 1.13 drochner int fd, count, i, mode; 370 1.1 thorpej pid_t tickler; 371 1.1 thorpej 372 1.1 thorpej fd = open(_PATH_WATCHDOG, O_RDONLY, 0644); 373 1.1 thorpej if (fd == -1) 374 1.14 dyoung err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG); 375 1.1 thorpej 376 1.1 thorpej wc.wc_names = NULL; 377 1.1 thorpej wc.wc_count = 0; 378 1.1 thorpej 379 1.1 thorpej if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 380 1.14 dyoung err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for count"); 381 1.1 thorpej 382 1.1 thorpej count = wc.wc_count; 383 1.1 thorpej if (count == 0) { 384 1.1 thorpej printf("No watchdog timers present.\n"); 385 1.1 thorpej goto out; 386 1.1 thorpej } 387 1.1 thorpej 388 1.1 thorpej buf = malloc(count * WDOG_NAMESIZE); 389 1.1 thorpej if (buf == NULL) 390 1.14 dyoung err(EXIT_FAILURE, "malloc %d byte for watchdog names", 391 1.1 thorpej count * WDOG_NAMESIZE); 392 1.1 thorpej 393 1.1 thorpej wc.wc_names = buf; 394 1.1 thorpej if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 395 1.14 dyoung err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for names"); 396 1.1 thorpej 397 1.1 thorpej count = wc.wc_count; 398 1.1 thorpej if (count == 0) { 399 1.1 thorpej printf("No watchdog timers present.\n"); 400 1.1 thorpej free(buf); 401 1.1 thorpej goto out; 402 1.1 thorpej } 403 1.1 thorpej 404 1.1 thorpej printf("Available watchdog timers:\n"); 405 1.1 thorpej for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) { 406 1.1 thorpej cp[WDOG_NAMESIZE - 1] = '\0'; 407 1.9 itojun strlcpy(wm.wm_name, cp, sizeof(wm.wm_name)); 408 1.1 thorpej 409 1.1 thorpej if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1) 410 1.13 drochner continue; 411 1.13 drochner mode = wm.wm_mode & WDOG_MODE_MASK; 412 1.13 drochner if (mode == WDOG_MODE_UTICKLE) { 413 1.1 thorpej if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 414 1.1 thorpej tickler = (pid_t) -1; 415 1.1 thorpej } 416 1.1 thorpej 417 1.1 thorpej printf("\t%s, %u second period", cp, wm.wm_period); 418 1.13 drochner if (mode != WDOG_MODE_DISARMED) { 419 1.13 drochner switch(mode) { 420 1.14 dyoung case WDOG_MODE_KTICKLE: 421 1.14 dyoung printf(" [armed, kernel tickle"); 422 1.14 dyoung break; 423 1.14 dyoung case WDOG_MODE_UTICKLE: 424 1.14 dyoung printf(" [armed, user tickle"); 425 1.14 dyoung if (tickler != (pid_t) -1) 426 1.14 dyoung printf(", pid %d", tickler); 427 1.14 dyoung break; 428 1.14 dyoung case WDOG_MODE_ETICKLE: 429 1.14 dyoung printf(" [armed, external tickle"); 430 1.14 dyoung break; 431 1.11 smb } 432 1.1 thorpej printf("]"); 433 1.1 thorpej } 434 1.1 thorpej printf("\n"); 435 1.1 thorpej } 436 1.1 thorpej out: 437 1.14 dyoung (void)close(fd); 438 1.1 thorpej } 439 1.1 thorpej 440 1.20 joerg static void 441 1.1 thorpej usage(void) 442 1.1 thorpej { 443 1.12 wiz 444 1.10 jmmv fprintf(stderr, "usage: %s\n", getprogname()); 445 1.12 wiz fprintf(stderr, " %s -d\n", getprogname()); 446 1.11 smb fprintf(stderr, " %s -e [-A] [-p seconds] timer\n", 447 1.11 smb getprogname()); 448 1.7 cgd fprintf(stderr, " %s -k [-A] [-p seconds] timer\n", 449 1.7 cgd getprogname()); 450 1.12 wiz fprintf(stderr, " %s -t\n", getprogname()); 451 1.7 cgd fprintf(stderr, " %s -u [-A] [-p seconds] timer\n", 452 1.7 cgd getprogname()); 453 1.17 wiz fprintf(stderr, " %s -x [-A] [-p seconds] timer\n", 454 1.17 wiz getprogname()); 455 1.1 thorpej 456 1.1 thorpej exit(1); 457 1.1 thorpej } 458