1 /* $NetBSD: wdogctl.c,v 1.8 2003/06/23 11:53:45 agc 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.8 2003/06/23 11:53:45 agc 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 disable(void); 63 void list_timers(void); 64 void usage(void); 65 66 int Aflag; 67 68 int 69 main(int argc, char *argv[]) 70 { 71 int cmds = 0, kflag = 0, uflag = 0, dflag = 0, ch; 72 u_int period = WDOG_PERIOD_DEFAULT; 73 74 while ((ch = getopt(argc, argv, "Adkp:u")) != -1) { 75 switch (ch) { 76 case 'A': 77 if (cmds == 0 || dflag) 78 usage(); 79 Aflag = 1; 80 break; 81 82 case 'd': 83 dflag = 1; 84 cmds++; 85 break; 86 87 case 'k': 88 kflag = 1; 89 cmds++; 90 break; 91 92 case 'p': 93 if (cmds == 0 || dflag) 94 usage(); 95 period = atoi(optarg); 96 if (period == -1) 97 usage(); 98 break; 99 100 case 'u': 101 uflag = 1; 102 cmds++; 103 break; 104 105 default: 106 usage(); 107 } 108 } 109 110 argc -= optind; 111 argv += optind; 112 113 if (cmds > 1) 114 usage(); 115 116 if (kflag) { 117 if (argc != 1) 118 usage(); 119 enable_kernel(argv[0], period); 120 } else if (uflag) { 121 if (argc != 1) 122 usage(); 123 enable_user(argv[0], period); 124 } else if (dflag) { 125 if (argc != 0) 126 usage(); 127 disable(); 128 } else 129 list_timers(); 130 131 exit(0); 132 } 133 134 void 135 enable_kernel(const char *name, u_int period) 136 { 137 struct wdog_mode wm; 138 int fd; 139 140 if (strlen(name) >= WDOG_NAMESIZE) 141 errx(1, "invalid watchdog timer name: %s", name); 142 strcpy(wm.wm_name, name); 143 wm.wm_mode = WDOG_MODE_KTICKLE; 144 wm.wm_period = period; 145 146 if (Aflag) 147 wm.wm_mode |= WDOG_FEATURE_ALARM; 148 149 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 150 if (fd == -1) 151 err(1, "open %s", _PATH_WATCHDOG); 152 153 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) 154 err(1, "WDOGIOC_SMODE"); 155 } 156 157 void 158 enable_user(const char *name, u_int period) 159 { 160 struct wdog_mode wm; 161 struct timespec ts; 162 pid_t tickler; 163 int fd, rv; 164 165 if (strlen(name) >= WDOG_NAMESIZE) 166 errx(1, "invalid watchdog timer name: %s", name); 167 strcpy(wm.wm_name, name); 168 wm.wm_mode = WDOG_MODE_UTICKLE; 169 wm.wm_period = period; 170 171 if (Aflag) 172 wm.wm_mode |= WDOG_FEATURE_ALARM; 173 174 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 175 if (fd == -1) 176 err(1, "open %s", _PATH_WATCHDOG); 177 178 /* ...so we can log failures to tickle the timer. */ 179 openlog("wdogctl", LOG_PERROR|LOG_PID, LOG_DAEMON); 180 181 /* 182 * We fork a child process which detaches from the controlling 183 * terminal once the timer is armed, and tickles the timer 184 * until we send it a SIGTERM. 185 */ 186 tickler = fork(); 187 if (tickler == -1) 188 err(1, "unable to fork tickler process"); 189 else if (tickler != 0) { 190 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) { 191 err(1, "WDOGIOC_SMODE"); 192 (void) kill(tickler, SIGTERM); 193 } 194 (void) close(fd); 195 return; 196 } 197 198 199 /* 200 * Wait for the watchdog to be armed. When it is, loop, 201 * tickling the timer, then waiting 1/2 the period before 202 * doing it again. 203 * 204 * If the parent fails to enable the watchdog, it will kill 205 * us. 206 */ 207 do { 208 rv = ioctl(fd, WDOGIOC_WHICH, &wm); 209 } while (rv == -1); 210 211 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 212 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 213 wm.wm_name); 214 215 /* 216 * Now detach from the controlling terminal, and just run 217 * in the background. The kernel will keep track of who 218 * we are, each time we tickle the timer. 219 */ 220 if (daemon(0, 0) == -1) { 221 /* 222 * We weren't able to go into the background. When 223 * we exit, the kernel will disable the watchdog so 224 * that the system won't die. 225 */ 226 err(1, "unable to detach from terminal"); 227 } 228 229 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 230 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m", 231 wm.wm_name); 232 233 for (;;) { 234 ts.tv_sec = wm.wm_period / 2; 235 ts.tv_nsec = 0; 236 (void) nanosleep(&ts, NULL); 237 238 if (ioctl(fd, WDOGIOC_TICKLE) == -1) 239 syslog(LOG_EMERG, 240 "unable to tickle watchdog timer %s: %m", 241 wm.wm_name); 242 } 243 /* NOTREACHED */ 244 } 245 246 void 247 disable(void) 248 { 249 struct wdog_mode wm; 250 pid_t tickler; 251 int fd; 252 253 fd = open(_PATH_WATCHDOG, O_RDWR, 0644); 254 if (fd == -1) 255 err(1, "open %s", _PATH_WATCHDOG); 256 257 if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) { 258 printf("No watchdog timer running.\n"); 259 return; 260 } 261 262 /* 263 * If the timer is running in UTICKLE mode, we need 264 * to kill the wdogctl(8) process that is tickling 265 * the timer. 266 */ 267 if (wm.wm_mode == WDOG_MODE_UTICKLE) { 268 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 269 err(1, "WDOGIOC_GTICKLER"); 270 (void) close(fd); 271 (void) kill(tickler, SIGTERM); 272 } else { 273 wm.wm_mode = WDOG_MODE_DISARMED; 274 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) 275 err(1, "unable to disarm watchdog %s", wm.wm_name); 276 (void) close(fd); 277 } 278 } 279 280 void 281 list_timers(void) 282 { 283 struct wdog_conf wc; 284 struct wdog_mode wm; 285 char *buf, *cp; 286 int fd, count, i; 287 pid_t tickler; 288 289 fd = open(_PATH_WATCHDOG, O_RDONLY, 0644); 290 if (fd == -1) 291 err(1, "open %s", _PATH_WATCHDOG); 292 293 wc.wc_names = NULL; 294 wc.wc_count = 0; 295 296 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 297 err(1, "ioctl WDOGIOC_GWDOGS for count"); 298 299 count = wc.wc_count; 300 if (count == 0) { 301 printf("No watchdog timers present.\n"); 302 goto out; 303 } 304 305 buf = malloc(count * WDOG_NAMESIZE); 306 if (buf == NULL) 307 err(1, "malloc %d byte for watchdog names", 308 count * WDOG_NAMESIZE); 309 310 wc.wc_names = buf; 311 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1) 312 err(1, "ioctl WDOGIOC_GWDOGS for names"); 313 314 count = wc.wc_count; 315 if (count == 0) { 316 printf("No watchdog timers present.\n"); 317 free(buf); 318 goto out; 319 } 320 321 printf("Available watchdog timers:\n"); 322 for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) { 323 cp[WDOG_NAMESIZE - 1] = '\0'; 324 strcpy(wm.wm_name, cp); 325 326 if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1) 327 wm.wm_mode = -1; 328 else if (wm.wm_mode == WDOG_MODE_UTICKLE) { 329 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1) 330 tickler = (pid_t) -1; 331 } 332 333 printf("\t%s, %u second period", cp, wm.wm_period); 334 if (wm.wm_mode != WDOG_MODE_DISARMED) { 335 printf(" [armed, %s tickle", 336 wm.wm_mode == WDOG_MODE_KTICKLE ? 337 "kernel" : "user"); 338 if (wm.wm_mode == WDOG_MODE_UTICKLE && 339 tickler != (pid_t) -1) 340 printf(", pid %d", tickler); 341 printf("]"); 342 } 343 printf("\n"); 344 } 345 out: 346 (void) close(fd); 347 } 348 349 void 350 usage(void) 351 { 352 fprintf(stderr, "Usage: %s\n", getprogname()); 353 fprintf(stderr, " %s -k [-A] [-p seconds] timer\n", 354 getprogname()); 355 fprintf(stderr, " %s -u [-A] [-p seconds] timer\n", 356 getprogname()); 357 fprintf(stderr, " %s -d\n", getprogname()); 358 359 exit(1); 360 } 361