]> git.proxmox.com Git - pve-ha-manager.git/blobdiff - src/watchdog-mux.c
allow to configure watchdog module in /etc/default/pve-ha-manager
[pve-ha-manager.git] / src / watchdog-mux.c
index 7ecd76e2f20e9cdaaa5f0e29ca969513d4ee778f..2b440131d4fb18aada436cc080a6a7e784792977 100644 (file)
@@ -1,3 +1,4 @@
+#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
@@ -49,6 +53,7 @@ alloc_client(int fd, time_t time)
         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];
         }
     }
@@ -64,6 +69,7 @@ free_client(wd_client_t *wd_client)
 
     wd_client->time = 0;
     wd_client->fd = 0;
+    wd_client->magic_close = 0;
 }
 
 static int
@@ -102,7 +108,7 @@ main(void)
     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;
 
@@ -110,9 +116,22 @@ main(void)
         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) {
@@ -139,7 +158,7 @@ main(void)
             wdinfo.identity, wdinfo.firmware_version);
 
     socket_count = sd_listen_fds(0);
-
+    
     if (socket_count > 1) {
 
         perror("too many file descriptors received.\n");
@@ -148,10 +167,12 @@ main(void)
     } 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) {
@@ -161,7 +182,7 @@ main(void)
 
         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) {
@@ -220,13 +241,31 @@ main(void)
 
         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;
@@ -282,10 +321,18 @@ main(void)
                     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                   
@@ -294,9 +341,14 @@ main(void)
                             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);
                         }
@@ -316,10 +368,14 @@ main(void)
         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);
 }