aboutsummaryrefslogblamecommitdiff
path: root/wd/wdd.c
blob: 4468178ee6cceaa30dacf1973c9799ff704b003a (plain) (tree)
1
2
3
4
5
6
7
8
9
  
                                                           






                      

                                                                    

                            

































                                                               


                                                                             
   
                                                           
               
            




                                          
                            

                  







                     

 
                                  
            
                  


                                   






                                                             





















                                                       









                                                                                      






                                  
 

                                             
                                                          




                                                            

                                             




                                                              

                     









                                         



















                                                                       






                                           
/*
 * wdd - simple watchdog daemon - 2003-2004 - willy tarreau
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const char dev_wd_str[] = "/dev/watchdog";	/* standard entry */
const char dev_misc_str[] = "/dev/misc/watchdog";  /* devfs entry */
const char root_str[] = "/";

/*
 * This function checks if the system can allocate memory
 * In case of failure, we exit so that the watchdog device
 * notices it and can reboot.
 */
static inline void try_malloc() {
    void *heap;

    heap = (void*)sbrk(NULL);
    if (brk(heap + 4096))
	exit(1);
    memset(heap, 0, 4096);
    if (brk(heap))
	exit(1);
}

/*
 * This function checks if the system can fork
 * In case of failure, we exit so that the watchdog device
 * notices it and can reboot.
 */
static inline void try_fork() {
    int pid;
    pid = fork();
    if (pid < 0) /* exit on error */
	exit(1);
    else if (pid == 0) /* child returns cleanly */
	exit(0);
    if (waitpid(pid, NULL, 0) != pid) /* father checks child */
	exit(1);
}


/*
 * This function checks if the system can stat a given directory entry on the
 * VFS. In case of failure, we either report the problem, or exit so that the
 * watchdog device notices it and can reboot.
 */
static inline int try_stat(const char *file, int do_exit) {
    void *heap;
    int ret;

    heap = (void*)sbrk(NULL);
    if (brk(heap + sizeof (struct stat)))
	exit(1);
    memset(heap, 0, sizeof (struct stat));
    ret =  stat(file, heap);
    if (brk(heap))
	exit(1);

    if (ret == -1) {
	if (do_exit)
	    exit(1);
	else
	    return 0;
    }
    return 1;
}

int main (int argc, char **argv) {
    int dev;
    int curr_file;
    int curr_count, stat_count = 0;
    char *touch_file = NULL;
    struct stat file_stat;

    if (argc > 1) {
	/* we'll do a quick check on all the arguments to
	 * ensure that they are valid at load time, and avoid
	 * an accidental start of the watchdog which could be
	 * a disaster in case of a file name error.
	 */
	    while (argc > 1 && argv[1][0] == '-') {
		    argc--; argv++;
		    if (argv[0][1] == '-') {
			    /* -- */
			    break;
		    }
		    else if (argv[0][1] == 'c') {
			    /* -c <count> */
			    if (argc < 2)
				    break;
			    stat_count = atol(argv[1]);
			    argc--; argv++;
		    }
		    else if (argv[0][1] == 'f') {
			    /* -f <file> */
			    if (argc < 2)
				    break;
			    touch_file = argv[1];
			    argc--; argv++;
		    }
	    }

	for (curr_file = 1; curr_file < argc; ) {
	    if (try_stat(argv[curr_file], 0))
		curr_file++;
	    else {
		/* remove this file from the list, and make it noticeable from 'ps' */
		*argv[curr_file] = '!';
		argv[curr_file] = argv[--argc];
	    }
	}
    }

    if (fork() > 0)
	return 0;
    for (dev = 2; dev >= 0; dev--)
	close(dev);
    chdir(root_str);
    setsid();

    curr_count = stat_count;
    memset(&file_stat, 0, sizeof(file_stat));
    curr_file = 1; /* start with first file in the list */
    /* let's try indefinitely to open the watchdog device */
    /* note that dev is -1 now ;-) */
    while (1) {
	if (dev == -1)
	    dev = open(dev_wd_str, O_RDWR);
	if (dev == -1)
	    dev = open(dev_misc_str, O_RDWR);
	if ((dev != -1) && (write(dev, dev_wd_str, 1) != 1)) {
	    /* write error, we'll restart */
	    close(dev);
	    dev = -1;
	}
	try_malloc();
	try_fork();

	if (argc > 1) {
	    try_stat(argv[curr_file], 1);
	    curr_file++;
	    if (curr_file >= argc)
		curr_file = 1;
	} else {
	    try_stat(root_str, 1);
	}

	/* we may want to check that the touch_file has been touched */
	if (touch_file && stat_count) {
		struct stat tmp;

		/* an absent file sets an empty struct stat */
		if (stat(touch_file, &tmp) < 0)
			memset(&tmp, 0, sizeof(tmp));

		if (memcmp(&file_stat, &tmp, sizeof(tmp)) != 0) {
			/* the file has been touched */
			memcpy(&file_stat, &tmp, sizeof(tmp));
			curr_count = stat_count;
		} else {
			/* still no change */
			curr_count--;
			if (!curr_count)
				exit(1);
		}
	}

	/* avoid a fast loop */
	sleep(1);
    }
    /* we never get there theorically... */
    return 0;
}