+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/types.h>
#include <linux/watchdog.h>
-#include <systemd/sd-daemon.h>
-
#define WD_SOCK_PATH "/run/watchdog-mux.sock"
#define WD_ACTIVE_MARKER "/run/watchdog-mux.active"
-#define LISTEN_BACKLOG 32 /* set same value in watchdog-mux.socket */
+#define LISTEN_BACKLOG 32
#define MAX_EVENTS 10
#define WATCHDOG_DEV "/dev/watchdog"
+#define JOURNALCTL_BIN "/bin/journalctl"
+
int watchdog_fd = -1;
int watchdog_timeout = 10;
int client_watchdog_timeout = 60;
-int update_watchdog = 1;
+int update_watchdog = 1;
typedef struct {
int fd;
count++;
}
}
-
+
return count;
}
-static void
+static void
watchdog_close(void)
{
if (watchdog_fd != -1) {
watchdog_fd = -1;
}
-
-int
+
+static void
+sync_journal_unsafe(void)
+{
+
+ pid_t child = fork();
+
+ // do not care about fork error or collecting the childs exit status,
+ // we are resetting soon anyway and just want to sync out the journal
+ if (child == 0) {
+ execl(JOURNALCTL_BIN, JOURNALCTL_BIN, "--sync", NULL);
+ exit(-1);
+ }
+}
+
+int
main(void)
{
struct sockaddr_un my_addr, peer_addr;
socklen_t peer_addr_size;
struct epoll_event ev, events[MAX_EVENTS];
- int socket_count, listen_sock, nfds, epollfd, sigfd;
- int unlink_socket = 0;
-
+ int listen_sock, nfds, epollfd, sigfd;
+
struct stat fs;
if (stat(WD_ACTIVE_MARKER, &fs) == 0) {
fprintf(stderr, "watchdog active - unable to restart watchdog-mux\n");
- exit(EXIT_FAILURE);
+ 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"); // load softdog by default
+ 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);
+ }
+ fprintf(stderr, "Loading watchdog module '%s'\n", wd_module);
+ system(cmd);
+ free(cmd);
+ } else {
+ system("modprobe -q softdog"); // load softdog by default
+ }
}
if ((watchdog_fd = open(WATCHDOG_DEV, O_WRONLY)) == -1) {
perror("watchdog open");
exit(EXIT_FAILURE);
}
-
+
if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &watchdog_timeout) == -1) {
perror("watchdog set timeout");
watchdog_close();
fprintf(stderr, "Watchdog driver '%s', version %x\n",
wdinfo.identity, wdinfo.firmware_version);
- socket_count = sd_listen_fds(0);
-
- if (socket_count > 1) {
-
- perror("too many file descriptors received.\n");
- goto err;
-
- } else if (socket_count == 1) {
-
- listen_sock = SD_LISTEN_FDS_START + 0;
-
- } else {
-
- unlink_socket = 1;
-
- unlink(WD_SOCK_PATH);
+ /* always unlink socket path then create socket */
+ unlink(WD_SOCK_PATH);
- listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (listen_sock == -1) {
- perror("socket create");
- exit(EXIT_FAILURE);
- }
+ listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (listen_sock == -1) {
+ perror("socket create");
+ exit(EXIT_FAILURE);
+ }
+ memset(&my_addr, 0, sizeof(struct sockaddr_un));
+ my_addr.sun_family = AF_UNIX;
+ 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) {
+ perror("socket bind");
+ exit(EXIT_FAILURE);
+ }
- memset(&my_addr, 0, sizeof(struct sockaddr_un));
- my_addr.sun_family = AF_UNIX;
- 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) {
- perror("socket bind");
- exit(EXIT_FAILURE);
- }
-
- if (listen(listen_sock, LISTEN_BACKLOG) == -1) {
- perror("socket listen");
- goto err;
- }
+ if (listen(listen_sock, LISTEN_BACKLOG) == -1) {
+ perror("socket listen");
+ goto err;
}
-
+
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);
-
+
sigprocmask(SIG_BLOCK, &mask, NULL);
-
+
if ((sigfd = signalfd(-1, &mask, SFD_NONBLOCK)) < 0) {
perror("unable to open signalfd");
goto err;
perror("epoll_ctl add sigfd");
goto err;
}
-
+
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, 1000);
if (nfds == -1) {
if (errno == EINTR)
continue;
-
+
perror("epoll_pwait");
goto err;
}
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");
+ fprintf(stderr, "client watchdog expired - disable watchdog updates\n");
}
}
}
perror("watchdog update failed");
}
}
-
+
continue;
}
if (!update_watchdog)
break;
-
+
int terminate = 0;
-
+
int n;
for (n = 0; n < nfds; ++n) {
wd_client_t *wd_client = events[n].data.ptr;
fprintf(stderr, "unable to alloc wd_client structure\n");
goto err; // fixme;
}
-
+
mkdir(WD_ACTIVE_MARKER, 0600);
-
+
ev.events = EPOLLIN;
ev.data.ptr = new_client;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
perror("epoll_ctl: add conn_sock");
- goto err; // fixme
+ goto err; // fixme
}
} else if (wd_client->fd == sigfd) {
fprintf(stderr, "got terminate request\n");
}
}
-
+
} else {
char buf[4096];
int cfd = wd_client->fd;
-
+
ssize_t bytes = read(cfd, buf, sizeof(buf));
if (bytes == -1) {
perror("read");
- goto err; // fixme
+ goto err; // fixme
} else if (bytes > 0) {
int i;
for (i = 0; i < bytes; i++) {
//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
+ goto err; // fixme
}
if (close(cfd) == -1) {
perror("close conn_sock");
- goto err; // fixme
+ goto err; // fixme
}
if (!wd_client->magic_close) {
fprintf(stderr, "client did not stop watchdog - disable watchdog updates\n");
+ sync_journal_unsafe();
update_watchdog = 0;
} else {
free_client(wd_client);
}
-
+
if (!active_client_count()) {
rmdir(WD_ACTIVE_MARKER);
}
int active_count = active_client_count();
if (active_count > 0) {
fprintf(stderr, "exit watchdog-mux with active connections\n");
+ sync_journal_unsafe();
} else {
fprintf(stderr, "clean exit\n");
watchdog_close();
}
-
- if (unlink_socket)
- unlink(WD_SOCK_PATH);
-
+
+ unlink(WD_SOCK_PATH);
+
exit(EXIT_SUCCESS);
err:
- if (unlink_socket)
- unlink(WD_SOCK_PATH);
+ unlink(WD_SOCK_PATH);
exit(EXIT_FAILURE);
}