/*- * Copyright (c) 2003-2004 Sean M. Kelly * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Software watchdog daemon. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void parseargs(int, char *[]); static void sighandler(int); static void watchdog_loop(void); static int watchdog_init(void); static int watchdog_onoff(int onoff); static int watchdog_patpat(u_int timeout); static void usage(void); static int debugging = 0; static int end_program = 0; static const char *pidfile = _PATH_VARRUN "watchdogd.pid"; static u_int timeout = WD_TO_16SEC; static u_int passive = 0; static int is_daemon = 0; static int fd = -1; static int nap = 1; static char *test_cmd = NULL; /* * Ask malloc() to map minimum-sized chunks of virtual address space at a time, * so that mlockall() won't needlessly wire megabytes of unused memory into the * process. This must be done using the malloc_conf string so that it gets set * up before the first allocation, which happens before entry to main(). */ const char * malloc_conf = "lg_chunk:0"; /* * Periodically pat the watchdog, preventing it from firing. */ int main(int argc, char *argv[]) { struct rtprio rtp; struct pidfh *pfh; pid_t otherpid; if (getuid() != 0) errx(EX_SOFTWARE, "not super user"); parseargs(argc, argv); rtp.type = RTP_PRIO_REALTIME; rtp.prio = 0; if (rtprio(RTP_SET, 0, &rtp) == -1) err(EX_OSERR, "rtprio"); if (watchdog_init() == -1) errx(EX_SOFTWARE, "unable to initialize watchdog"); if (is_daemon) { if (watchdog_onoff(1) == -1) err(EX_OSERR, "patting the dog"); pfh = pidfile_open(pidfile, 0600, &otherpid); if (pfh == NULL) { if (errno == EEXIST) { errx(EX_SOFTWARE, "%s already running, pid: %d", getprogname(), otherpid); } warn("Cannot open or create pidfile"); } if (debugging == 0 && daemon(0, 0) == -1) { watchdog_onoff(0); pidfile_remove(pfh); err(EX_OSERR, "daemon"); } signal(SIGHUP, SIG_IGN); signal(SIGINT, sighandler); signal(SIGTERM, sighandler); pidfile_write(pfh); if (madvise(0, 0, MADV_PROTECT) != 0) warn("madvise failed"); if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) warn("mlockall failed"); watchdog_loop(); /* exiting */ pidfile_remove(pfh); return (EX_OK); } else { if (passive) timeout |= WD_PASSIVE; else timeout |= WD_ACTIVE; if (watchdog_patpat(timeout) < 0) err(EX_OSERR, "patting the dog"); return (EX_OK); } } /* * Catch signals and begin shutdown process. */ static void sighandler(int signum) { if (signum == SIGINT || signum == SIGTERM) end_program = 1; } /* * Open the watchdog device. */ static int watchdog_init(void) { fd = open("/dev/" _PATH_WATCHDOG, O_RDWR); if (fd >= 0) return (0); warn("Could not open watchdog device"); return (-1); } /* * Main program loop which is iterated every second. */ static void watchdog_loop(void) { struct stat sb; int failed; while (end_program != 2) { failed = 0; if (test_cmd != NULL) failed = system(test_cmd); else failed = stat("/etc", &sb); if (failed == 0) watchdog_patpat(timeout|WD_ACTIVE); sleep(nap); if (end_program != 0) { if (watchdog_onoff(0) == 0) { end_program = 2; } else { warnx("Could not stop the watchdog, not exitting"); end_program = 0; } } } } /* * Reset the watchdog timer. This function must be called periodically * to keep the watchdog from firing. */ static int watchdog_patpat(u_int t) { return ioctl(fd, WDIOCPATPAT, &t); } /* * Toggle the kernel's watchdog. This routine is used to enable and * disable the watchdog. */ static int watchdog_onoff(int onoff) { if (onoff) return watchdog_patpat((timeout|WD_ACTIVE)); else return watchdog_patpat(0); } /* * Tell user how to use the program. */ static void usage(void) { if (is_daemon) fprintf(stderr, "usage: watchdogd [-d] [-e cmd] [-I file] [-s sleep] [-t timeout]\n"); else fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n"); exit(EX_USAGE); } /* * Handle the few command line arguments supported. */ static void parseargs(int argc, char *argv[]) { int c; char *p; double a; c = strlen(argv[0]); if (argv[0][c - 1] == 'd') is_daemon = 1; while ((c = getopt(argc, argv, is_daemon ? "I:de:s:t:?" : "dt:?")) != -1) { switch (c) { case 'I': pidfile = optarg; break; case 'd': debugging = 1; break; case 'e': test_cmd = strdup(optarg); break; #ifdef notyet case 'p': passive = 1; break; #endif case 's': p = NULL; errno = 0; nap = strtol(optarg, &p, 0); if ((p != NULL && *p != '\0') || errno != 0) errx(EX_USAGE, "-s argument is not a number"); break; case 't': p = NULL; errno = 0; a = strtod(optarg, &p); if ((p != NULL && *p != '\0') || errno != 0) errx(EX_USAGE, "-t argument is not a number"); if (a < 0) errx(EX_USAGE, "-t argument must be positive"); if (a == 0) timeout = WD_TO_NEVER; else timeout = flsll(a * 1e9); if (debugging) printf("Timeout is 2^%d nanoseconds\n", timeout); break; case '?': default: usage(); /* NOTREACHED */ } } if (argc != optind) errx(EX_USAGE, "extra arguments."); if (is_daemon && timeout < WD_TO_1SEC) errx(EX_USAGE, "-t argument is less than one second."); }