+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <systemd/sd-daemon.h>
-#define MY_SOCK_PATH "/run/watchdog-mux.sock"
+#define WD_SOCK_PATH "/run/watchdog-mux.sock"
#define WD_ACTIVE_MARKER "/run/watchdog-mux.active"
-#define LISTEN_BACKLOG 50
+#define LISTEN_BACKLOG 32 /* set same value in watchdog-mux.socket */
+
#define MAX_EVENTS 10
#define WATCHDOG_DEV "/dev/watchdog"
int watchdog_fd = -1;
-int watchdog_timeout = 20;
-
+int watchdog_timeout = 10;
+int client_watchdog_timeout = 60;
+int update_watchdog = 1;
typedef struct {
int fd;
time_t time;
+ int magic_close;
} wd_client_t;
#define MAX_CLIENTS 100
if (client_list[i].fd == 0) {
client_list[i].fd = fd;
client_list[i].time = time;
+ client_list[i].magic_close = 0;
return &client_list[i];
}
}
wd_client->time = 0;
wd_client->fd = 0;
+ wd_client->magic_close = 0;
}
static int
socklen_t peer_addr_size;
struct epoll_event ev, events[MAX_EVENTS];
int socket_count, listen_sock, nfds, epollfd, sigfd;
-
+ int unlink_socket = 0;
struct stat fs;
fprintf(stderr, "watchdog active - unable to restart watchdog-mux\n");
exit(EXIT_FAILURE);
}
-
+
+ /* if you want to debug, set options in /lib/modprobe.d/aliases.conf
+ * options softdog soft_noboot=1
+ */
if (stat(WATCHDOG_DEV, &fs) == -1) {
- system("modprobe -q softdog soft_noboot=1"); // fixme
+ char *wd_module = getenv("WATCHDOG_MODULE");
+ if (wd_module) {
+ char *cmd = NULL;
+ if ((asprintf(&cmd, "modprobe -q %s", wd_module) == -1)) {
+ perror("assemble modprobe command failed");
+ exit(EXIT_FAILURE);
+ }
+ system(cmd);
+ } else {
+ system("modprobe -q softdog"); // load softdog by default
+ }
}
if ((watchdog_fd = open(WATCHDOG_DEV, O_WRONLY)) == -1) {
wdinfo.identity, wdinfo.firmware_version);
socket_count = sd_listen_fds(0);
-
+
if (socket_count > 1) {
perror("too many file descriptors received.\n");
} else if (socket_count == 1) {
listen_sock = SD_LISTEN_FDS_START + 0;
-
+
} else {
- unlink(MY_SOCK_PATH);
+ unlink_socket = 1;
+
+ unlink(WD_SOCK_PATH);
listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (listen_sock == -1) {
memset(&my_addr, 0, sizeof(struct sockaddr_un));
my_addr.sun_family = AF_UNIX;
- strncpy(my_addr.sun_path, MY_SOCK_PATH, sizeof(my_addr.sun_path) - 1);
+ strncpy(my_addr.sun_path, WD_SOCK_PATH, sizeof(my_addr.sun_path) - 1);
if (bind(listen_sock, (struct sockaddr *) &my_addr,
sizeof(struct sockaddr_un)) == -1) {
if (nfds == 0) { // timeout
- if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) == -1) {
- perror("watchdog update failed");
+ // check for timeouts
+ if (update_watchdog) {
+ int i;
+ time_t ctime = time(NULL);
+ for (i = 0; i < MAX_CLIENTS; i++) {
+ if (client_list[i].fd != 0 && client_list[i].time != 0 &&
+ ((ctime - client_list[i].time) > client_watchdog_timeout)) {
+ update_watchdog = 0;
+ fprintf(stderr, "client watchdog expired - disable watchdog updates\n");
+ }
+ }
+ }
+
+ if (update_watchdog) {
+ if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) == -1) {
+ perror("watchdog update failed");
+ }
}
continue;
}
+ if (!update_watchdog)
+ break;
+
int terminate = 0;
int n;
perror("read");
goto err; // fixme
} else if (bytes > 0) {
- fprintf(stderr, "GOT %zd bytes\n", bytes);
+ int i;
+ for (i = 0; i < bytes; i++) {
+ if (buf[i] == 'V') {
+ wd_client->magic_close = 1;
+ } else {
+ wd_client->magic_close = 0;
+ }
+ }
+ wd_client->time = time(NULL);
} else {
if (events[n].events & EPOLLHUP || events[n].events & EPOLLERR) {
- printf("GOT %016x event\n", events[n].events);
+ //printf("GOT %016x event\n", events[n].events);
if (epoll_ctl(epollfd, EPOLL_CTL_DEL, cfd, NULL) == -1) {
perror("epoll_ctl: del conn_sock");
goto err; // fixme
perror("close conn_sock");
goto err; // fixme
}
- fprintf(stderr, "close client connection\n");
- free_client(wd_client);
+ if (!wd_client->magic_close) {
+ fprintf(stderr, "client did not stop watchdog - disable watchdog updates\n");
+ update_watchdog = 0;
+ } else {
+ free_client(wd_client);
+ }
+
if (!active_client_count()) {
rmdir(WD_ACTIVE_MARKER);
}
watchdog_close();
}
- unlink(MY_SOCK_PATH);
+ if (unlink_socket)
+ unlink(WD_SOCK_PATH);
+
exit(EXIT_SUCCESS);
err:
- unlink(MY_SOCK_PATH);
+ if (unlink_socket)
+ unlink(WD_SOCK_PATH);
+
exit(EXIT_FAILURE);
}