diff options
author | John Klug <john.klug@multitech.com> | 2021-01-19 17:21:32 -0600 |
---|---|---|
committer | John Klug <john.klug@multitech.com> | 2021-01-19 17:21:32 -0600 |
commit | f8d0b344ae1b2dc3894c1a597c0565911b762742 (patch) | |
tree | 79e8f174ab88cd9c00258956f2cb2e6bed548956 | |
download | softdog-mon-0.1.tar.gz softdog-mon-0.1.tar.bz2 softdog-mon-0.1.zip |
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | COPYING | 23 | ||||
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | INSTALL | 237 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | NEWS | 0 | ||||
-rw-r--r-- | README | 84 | ||||
-rw-r--r-- | configure.ac | 31 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/hog.c | 35 | ||||
-rw-r--r-- | src/softdog-mon.c | 372 |
11 files changed, 793 insertions, 0 deletions
@@ -0,0 +1 @@ +Multi-Tech Systems @@ -0,0 +1,23 @@ + +MIT License + +Copyright (c) <2021> <Multi-Tech Systems> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ChangeLog @@ -0,0 +1 @@ + @@ -0,0 +1,237 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..8f99788 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS = gnu +SUBDIRS = src + @@ -0,0 +1,84 @@ +Configurable monitor program that uses softdog. + +This program (softdog-mon) must be running all +the time once it is started, or the +system will reset itself without a shutdown. +The problems detected may prevent shutdown, so a +reset is safer. + +The SHUTDOWNTIMEOUT value is the time to allow for +shutdown. Since firmware updates are done during +shutdown, this should be the worst case time for +shutdown. + +Variables passed through the environment: + +# Monitor program will have 1 second granularity. Fixed. +# All times are in seconds. + +# Hardware watchdog is found first, which is watchdog0. +WATCHDOG = /dev/watchdog1 + +# Nice value -20, is highest priority for a user program, 19 is lowest. +NICE = -20 + +# Watchdog timeout in seconds +TIMEOUT = 60 + +# How often to feed in seconds +FEED = 10 + +# File is synchonously open/read/written/closed every 30 seconds +FILESAMPLERATE = 30 + +# File to be read/written +# If I/O hangs, the TIMEOUT value is the maximum seconds until we +# reset the device. +MONITORFILE = /media/card/.softdog_monitor + +# Minimum available system memory in bytes +MINIMUM_AVAILABLE_MEM = 3000000 + +# Minimum free high memory +MINIMUM_FREEHIGH = 0 + +# Rate at which we sample available memory +MEMSAMPLERATE = 3 + +# last samples saved +MEMSAMPLES = 100 + +# maximum number of samples failed in last samples saved +MEMFAILEDSAMPLES = 20 + +# Allow time for flash upgrade during shutdown +# This happens when a SIGTERM signal is received. +# So shutdown has this many seconds to complete. +SHUTDOWNTIMEOUT=600 + +Their is an additional test program called +hog. This can be used to acquire memory and kernel +resources. + +hog 4750000 + +This will start five processes with 4750000 bytes +of memory. The idea is to trigger the watchdog. + + hog 4750000 + +Creates five processes with the amount of memory +specified. + +In a typical test: + +Log into the device several times with ssh, and do sudo -s +and acquire a root shell. + +As root start the program hog, the amount of memory required +will depend on the size of the programs typically running. + +Start top on the several screens logged in. Try to get the +available memory below 3MB. Once 20 samples have failed +the device will reboot. + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..5e6a888 --- /dev/null +++ b/configure.ac @@ -0,0 +1,31 @@ +AC_INIT([softdog-mon],0.1.0) +AC_CONFIG_SRCDIR([src/softdog-mon.c]) +AM_CONFIG_HEADER([config.h]) +AM_INIT_AUTOMAKE + + +AC_PROG_CC +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +AC_HEADER_STDC + +AC_CHECK_HEADERS([ \ +sys/types.h \ +sys/stat.h \ +sys/ioctl.h \ +linux/watchdog.h \ +fcntl.h \ +stdio.h \ +string.h \ +errno.h \ +stdlib.h \ +unistd.h \ +], [],AC_MSG_ERROR([ +required header missing])) + +AC_DEFINE([DEBUG], 0, [set to 1 to enable debug]) + +AC_OUTPUT([Makefile src/Makefile]) + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..dba3d24 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,6 @@ +## Process this file with automake to produce Makefile.in +AUTOMAKE_OPTIONS = gnu +AM_CFLAGS = -Wall + +sbin_PROGRAMS = softdog-mon hog + diff --git a/src/hog.c b/src/hog.c new file mode 100644 index 0000000..d887f4d --- /dev/null +++ b/src/hog.c @@ -0,0 +1,35 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +// Memory hog +int +main(int argc, const char *argv[]) +{ + int j,k; + char *p; + int pid; + int mallocsize; + + if (argc < 2) { + fprintf(stderr,"Need malloc size parameter\n"); + exit(1); + } + mallocsize = atoi(argv[1]); + for (k=0; k<5; k++) { + p = malloc(mallocsize); + if (p) + for(j=0;j<mallocsize;j++) + p[j] = j; + pid = fork(); + + if (pid) + fprintf(stderr,"pid=%d\n",pid); + } + pid = getpid(); + for(j=0;j<mallocsize;j++) + p[j] = pid; + + pause(); + // NOTREACHED + return (0); +} diff --git a/src/softdog-mon.c b/src/softdog-mon.c new file mode 100644 index 0000000..75591dd --- /dev/null +++ b/src/softdog-mon.c @@ -0,0 +1,372 @@ +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/sysinfo.h> +#include <linux/watchdog.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <config.h> +#include <errno.h> +#include <syslog.h> +#include <time.h> +#include <signal.h> + +/* This program does everything in seconds granularity. + * The time to accomplish a task has the number of nanoseconds + * added in to specify the time for the event to be accomplished. + */ + +char *name = PACKAGE; + +void usage() +{ + syslog(LOG_ERR, "%s (" PACKAGE ") " VERSION " (" __DATE__ " " __TIME__ ")\n", name); + syslog(LOG_ERR, "Copyright (C) 2021 by Multi-Tech Systems\n"); + + syslog(LOG_ERR,"USAGE: feedwatchdog device timeout"); + syslog(LOG_ERR,"Times below are in seconds"); + syslog(LOG_ERR,"ENVIRONMENT:"); + syslog(LOG_ERR," PIDFILE"); + syslog(LOG_ERR," FEED How often to feed watchdog in seconds"); + syslog(LOG_ERR," FILESAMPLERATE How often to open/read/write/close test file"); + syslog(LOG_ERR," MONITORFILE Path to test file"); + syslog(LOG_ERR," MINIMUM_AVAILABLE_MEM Memory Available"); + syslog(LOG_ERR," MINIMUM_FREEHIGH Available high memory"); + syslog(LOG_ERR," MEMSAMPLERATE Sample rate to test memory"); + syslog(LOG_ERR," MEMSAMPLES Samples to accumulate"); + syslog(LOG_ERR," MEMFAILEDSAMPLES Number of samples below threshold before reboot"); + syslog(LOG_ERR," SHUTDOWNTIMEOUT Set watchdog timeout seconds at SIGTERM signal"); + exit(1); +} + + +// Returns true if earlier is before or matches later. +int +timed_out(struct timespec *later, struct timespec *earlier) +{ + if (later->tv_sec > earlier->tv_sec) + return 1; // Time has elapsed by seconds + + if (later->tv_sec == earlier->tv_sec) { + if (later->tv_nsec >= earlier->tv_nsec) + return 1; // Seconds match, and nano-seconds have elapsed + } + return 0; +} + + +// Return the soonest of t1, t2, and t3. Prefers t3 to t2. Prefers t2 to t1. +void +min_time(struct timespec *t1, struct timespec *t2, struct timespec *t3, struct timespec **soonest) { + if(timed_out(t1,t2)) + *soonest = t2; + else + *soonest = t1; + if(timed_out(*soonest,t3)) + *soonest = t3; +} + + +char *pidfile; +int feed; // Seconds between feedings of the watchdog +int filesamplerate; +unsigned long long fileval_longlong = 0x5555555555555555ULL; +int timeout; +char *monitorfile; +unsigned long long minimum_available_mem; +unsigned long long minimum_freehigh; +int memsamplerate; +int memsamples; +int memfailedsamples; +char pidstr[32]; +char *available_samples; +char *freehigh_samples; +int memsample_idx; +struct timespec t0; +struct timespec feed_time; // When to next feed the watchdog +struct timespec file_time; // When to next write to the file +struct timespec mem_time; // When to next test memory +int error_count; // Cumulative errors +int shutdown_timeout; +int devfd = -1; + +void +sigterm_handler(int sig) +{ + struct sigaction hdlr_action; + // Shutdown could be long if doing a flash upgrade + if(devfd != -1) + ioctl(devfd, WDIOC_SETTIMEOUT, &shutdown_timeout); + /* syslog is not safe in a signal handler */ + memset(&hdlr_action,0,sizeof hdlr_action); + hdlr_action.sa_handler = SIG_DFL; + sigaction(SIGTERM,&hdlr_action,NULL); + kill(getpid(),SIGTERM); + _exit(0); +} + +int +main(int argc, char *argv[]) +{ + int result, count; + char *tmp; + int pidfd,monitorfd; + char *watchdogdevicepath; + struct timespec t0, *stime = NULL; + struct sysinfo info; + struct sigaction action; + + openlog(name,LOG_PERROR,LOG_DAEMON); + + memset(&action,0,sizeof action); + action.sa_handler = sigterm_handler; + sigaction(SIGTERM,&action,NULL); + + if (argc < 3) + usage(); + timeout = atoi(argv[2]); + if (timeout < 1) { + syslog(LOG_ERR,"feedwatchdog: timeout must be at least 1"); + usage(); + } + pidfile = secure_getenv("PIDFILE"); + if(pidfile == NULL) { + pidfile = "/run/softdog-mon.pid"; + syslog(LOG_ERR,"feedwatchdog: pidfile is NULL"); + pidfd = open(pidfile,O_WRONLY|O_CREAT|O_TRUNC,0644); + } else { + syslog(LOG_ERR,"feedwatchdog: pidfile is %s",pidfile); + pidfd = open(pidfile,O_WRONLY|O_CREAT|O_TRUNC,0644); + } + + if (pidfd == -1) { + syslog(LOG_ERR,"PIDFILE: Could not open %s: %d: %s",pidfile,errno,strerror(errno)); + usage(); + } + + tmp = secure_getenv("FEED"); + if (tmp == NULL) { + syslog(LOG_ERR,"FEED is missing"); + usage(); + } + feed = atoi(tmp); + + tmp = secure_getenv("FILESAMPLERATE"); + if (tmp == NULL) { + syslog(LOG_ERR,"FILESAMPLERATE is missing"); + usage(); + } + filesamplerate = atoi(tmp); + + // Prime the monitor file + monitorfile = secure_getenv("MONITORFILE"); + monitorfd = open(monitorfile,O_WRONLY|O_CREAT|O_SYNC|O_TRUNC,0644); + if (monitorfd == -1) { + syslog(LOG_ERR,"MONITORFILE: Could not open %s: %d: %s",monitorfile,errno,strerror(errno)); + usage(); + } + result = write(monitorfd,&fileval_longlong,sizeof fileval_longlong); + if(result != 8) { + syslog(LOG_ERR,"MONITORFILE: Could not write %s: %d: %s",monitorfile,errno,strerror(errno)); + usage(); + } + close(monitorfd); + + tmp = secure_getenv("MINIMUM_AVAILABLE_MEM"); + if (tmp == NULL) { + syslog(LOG_ERR,"MINIMUM_AVAILABLE_MEM is missing"); + usage(); + } + minimum_available_mem = strtoull(tmp,NULL,0); + + tmp = secure_getenv("MINIMUM_FREEHIGH"); + if (tmp == NULL) { + syslog(LOG_ERR,"MINIMUM_FREEHIGH is missing"); + usage(); + } + minimum_freehigh = strtoull(tmp,NULL,0); + + + tmp = secure_getenv("MEMSAMPLERATE"); + if (tmp == NULL) { + syslog(LOG_ERR,"MEMSAMPLERATE is missing"); + usage(); + } + memsamplerate = atoi(tmp); + + tmp = secure_getenv("MEMSAMPLES"); + if (tmp == NULL) { + syslog(LOG_ERR,"MEMSAMPLES is missing"); + usage(); + } + memsamples = atoi(tmp); + available_samples = (char *)calloc(memsamples,sizeof available_samples[0]); + if(available_samples == NULL) { + syslog(LOG_ERR,"ERROR: Out of memory"); + exit(1); + } + freehigh_samples = (char *)calloc(memsamples,sizeof freehigh_samples[0]); + if(available_samples == NULL) { + syslog(LOG_ERR,"ERROR: Out of memory"); + exit(1); + } + memsample_idx = 0; + + tmp = secure_getenv("MEMFAILEDSAMPLES"); + if (tmp == NULL) { + syslog(LOG_ERR,"MEMFAILEDSAMPLES is missing"); + usage(); + } + memfailedsamples = atoi(tmp); + + tmp = secure_getenv("SHUTDOWNTIMEOUT"); + if (tmp == NULL) { + syslog(LOG_ERR,"SHUTDOWNTIMEOUT is missing"); + usage(); + } + shutdown_timeout = atoi(tmp); + + watchdogdevicepath = argv[1]; + syslog(LOG_ALERT,"%s: Version %s",name,VERSION); + syslog(LOG_ALERT,"All times in seconds, sizes in bytes"); + syslog(LOG_ALERT,"Watchdog Device: %s",watchdogdevicepath); + syslog(LOG_ALERT,"Watchdog Timout in Seconds: %d",timeout); + syslog(LOG_ALERT,"PID File: %s",pidfile); + syslog(LOG_ALERT,"Feed: %d",feed); + syslog(LOG_ALERT,"File Sample Rate %d",filesamplerate); + syslog(LOG_ALERT,"Monitor File (I/O health check) %s",monitorfile); + syslog(LOG_ALERT,"Minimum Available Memory %llu",minimum_available_mem); + syslog(LOG_ALERT,"Minimum Free High Memory %llu",minimum_freehigh); + syslog(LOG_ALERT,"Memory Sample Rate %d",memsamplerate); + syslog(LOG_ALERT,"Memory Samples Collected %d",memsamples); + syslog(LOG_ALERT,"Memory Failed Samples (maximum) %d",memfailedsamples); + syslog(LOG_ALERT,"Shudown timeout %d",shutdown_timeout); + result = clock_gettime(CLOCK_MONOTONIC, &t0); + if (result == -1) { + syslog(LOG_ERR,"ERROR: System Error: The system is not supporting MONOTONIC time"); + exit(1); + } + result = fork(); + switch(result) + { + case -1: + syslog(LOG_ERR,"ERROR: Could not fork: %d: %s", errno, strerror(errno)); + break; + case 0: + close(0); close(1); close(2); + setsid(); + break; + default: + _exit(0); + } + count = snprintf(pidstr,sizeof pidstr,"%u",(int)getpid()); + if ((count > 0) && (pidfd >= 0) && (count < sizeof pidstr)) { + pidstr[count] = '\n'; + result = write(pidfd,pidstr,count+1); + if (result == -1) + syslog(LOG_ERR,"ERROR: Could not write to PID file: %d: %s", errno, strerror(errno)); + } + close(pidfd); + + devfd = open(argv[1],O_RDWR); + if (devfd == -1) { + syslog(LOG_ERR,"feedwatchdog: Could not open %s: %s",argv[1],strerror(errno)); + usage(); + } + + clock_gettime(CLOCK_MONOTONIC, &t0); + feed_time = file_time = mem_time = t0; + syslog(LOG_DEBUG,"****INITIAL t0: size %d:%d %lu:%lu mem_time %lu:%lu",sizeof t0.tv_sec,sizeof t0.tv_nsec,t0.tv_sec,t0.tv_nsec,mem_time.tv_sec,mem_time.tv_nsec); + + ioctl(devfd, WDIOC_SETTIMEOUT, &timeout); + syslog(LOG_ERR,"The timeout was set to %d seconds",timeout); + + + while (1) { + clock_gettime(CLOCK_MONOTONIC, &t0); + if (timed_out(&t0,&feed_time)) { + ioctl(devfd, WDIOC_KEEPALIVE, 0); + feed_time.tv_sec += feed; // Next time to feed the watchdog + } + if (timed_out(&t0,&file_time)) { + unsigned long long readval; + monitorfd = open(monitorfile,O_RDWR|O_SYNC); + if(monitorfd == -1) { + syslog(LOG_ERR,"ERROR: Could not open %s: %d: %s",monitorfile,errno,strerror(errno)); + error_count++; + } else { + result = read(monitorfd,&readval,sizeof readval); + if(result == -1) { + syslog(LOG_ERR,"ERROR: Could not read %s: %d: %s",monitorfile,errno,strerror(errno)); + error_count++; + } + if (result != sizeof readval) { + syslog(LOG_ERR,"ERROR: %s: Expected to read %d, but read %d bytes",monitorfile,sizeof readval,result); + error_count++; + } + if (readval != fileval_longlong) { + syslog(LOG_ERR,"ERROR: %s: Expected to read %llu, but read %llu value",monitorfile,fileval_longlong,readval); + error_count++; + } + fileval_longlong++; + result = lseek(monitorfd,0,SEEK_SET); + if(result == -1) { + syslog(LOG_ERR,"ERROR: Could not rewind %s: %d: %s",monitorfile,errno,strerror(errno)); + error_count++; + } + result = write(monitorfd,&fileval_longlong,sizeof fileval_longlong); + if(result == -1) { + syslog(LOG_ERR,"ERROR: Could write %s: %d: %s",monitorfile,errno,strerror(errno)); + error_count++; + } + if(result != sizeof fileval_longlong) { + syslog(LOG_ERR,"ERROR: %s: Expected to write %d, but wrote %d bytes",monitorfile,sizeof fileval_longlong,result); + error_count++; + } + if(result == sizeof fileval_longlong) + file_time.tv_sec += filesamplerate; + close(monitorfd); + } // Good file descriptor for monitor file. + } // Monitor file timeout (write to monitor file) + + // Memory checks + if(timed_out(&t0,&mem_time)) { + unsigned long long testval; + int i; + int acount,fcount; + + syslog(LOG_DEBUG,"t0: %lu:%lu mem_time %lu:%lu",t0.tv_sec,t0.tv_nsec,mem_time.tv_sec,mem_time.tv_nsec); + sysinfo(&info); + testval = (info.freeram * info.mem_unit); + available_samples[memsample_idx] = (testval < minimum_available_mem); + testval = (info.freehigh * info.mem_unit); + freehigh_samples[memsample_idx] = (testval < minimum_freehigh); + memsample_idx++; + memsample_idx = (memsample_idx % memsamples); + acount = fcount = 0; + for (i=0;i<memsamples;i++) { + acount += available_samples[i]; + fcount += freehigh_samples[i]; + } + syslog(LOG_DEBUG,"mem samples: acount:%u fcount:%u memsamples:%u",acount,fcount,memsamples); + if (acount > memfailedsamples) { + syslog(LOG_ERR,"Memory Available failure: %llu, should be at least %llu",testval,minimum_available_mem); + exit(1); + } + if (fcount > memfailedsamples) { + syslog(LOG_ERR,"High Memory failure: %llu, should be at least %llu",testval,minimum_freehigh); + exit(1); + } + mem_time.tv_sec += memsamplerate; + } // Time to check memory. + // How long do we sleep? + min_time(&mem_time,&file_time,&feed_time,&stime); + clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME,stime,NULL); + } // Loop forever. + return 100; /* NOTREACHED */ +} |