aboutsummary'>refslog'>tree'>commitdiffstats
'>
path: '>root//'>wd/'>wdd.c
blob: 4468178ee6cceaa30dacf1973c9799ff704b003a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
 * 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;
}