Due to the implementation of /dev/watchdog
in the kernel, only one process can use it at the same time, so opening /dev/watchdog
from two different processes is not possible.
You can see this right in the source code of the Linux kernel, specifically in drivers/watchdog/watchdog_dev.c
. Here's the relevant snippet of code:
/*
* watchdog_open: open the /dev/watchdog* devices.
* @inode: inode of device
* @file: file handle to device
*
* When the /dev/watchdog* device gets opened, we start the watchdog.
* Watch out: the /dev/watchdog device is single open, so we make sure
* it can only be opened once.
*/
static int watchdog_open(struct inode *inode, struct file *file)
{
/* ... */
/* the watchdog is single open! */
if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status))
return -EBUSY;
/* ... */
If you want to feed the watchdog from two different processes, you can work around this issue by creating a simple "master" program that talks to the watchdog while orchestrating the two subprocesses as you wish. This can be done in different ways (pipes, sockets, threads, etc). A single popen()
per child process seems like a simple solution.
Here's a working example, master.c
:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
int main(int argc, char **argv) {
int watchdog_fd;
FILE *child1_fp, *child2_fp;
if (argc != 3 || !argv[1] || !*argv[1] || !argv[2] || !*argv[2]) {
fprintf(stderr, "Usage: %s 'CHILD_1_COMMAND' 'CHILD_2_COMMAND'\n", argv[0]);
return 1;
}
// Open a fd to talk to the watchdog.
watchdog_fd = open("/dev/watchdog", O_RDWR);
if (watchdog_fd == -1) {
perror("open failed");
return 1;
}
// Start the first process.
child1_fp = popen(argv[1], "r");
if (child1_fp == NULL) {
perror("popen (1) failed");
return 1;
}
// Start the second process.
child2_fp = popen(argv[2], "r");
if (child2_fp == NULL) {
perror("popen (2) failed");
return 1;
}
while (1) {
char tmp;
size_t count;
// Get one byte of data from each of the two processes.
count = fread(&tmp, 1, 1, child1_fp);
count += fread(&tmp, 1, 1, child2_fp);
// If both processes provided the data, ping the watchdog.
if (count == 2) {
if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0)
perror("ioctl failed");
}
}
return 0;
}
And two identical programs a.c
and b.c
just for testing purposes:
#include <stdio.h>
#include <unistd.h>
int main(void) {
setvbuf(stdout, NULL, _IONBF, 0);
while (1) {
putchar('x');
sleep(10);
}
}
Compile and run:
$ gcc -o master master.c
$ gcc -o a a.c
$ gcc -o b b.c
$ ./master ./a ./b
In the above example code, master
pings the watchdog if and only if the two children are alive and running: if one of the two hangs or dies, the master will stop pinging the watchdog. However, it's simple to rework the logic to work differently, and it's also simple to make it work with more than two child processes.