]> git.proxmox.com Git - pve-ha-manager.git/blame - src/watchdog-mux.c
/watchdog-mux: add signal handling
[pve-ha-manager.git] / src / watchdog-mux.c
CommitLineData
da8f8bbc
DM
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <fcntl.h>
5#include <string.h>
1fe42db7 6#include <errno.h>
98099e4f 7#include <time.h>
da8f8bbc 8#include <sys/ioctl.h>
7336614a
DM
9#include <sys/types.h>
10#include <sys/stat.h>
da8f8bbc
DM
11#include <sys/socket.h>
12#include <sys/un.h>
13#include <sys/epoll.h>
98099e4f
DM
14#include <signal.h>
15#include <sys/signalfd.h>
da8f8bbc
DM
16
17#include <linux/types.h>
18#include <linux/watchdog.h>
19
7336614a 20#include <systemd/sd-daemon.h>
e99d3682 21
ba878e35 22#define MY_SOCK_PATH "/run/watchdog-mux.sock"
98099e4f
DM
23#define WD_ACTIVE_MARKER "/run/watchdog-mux.active"
24
da8f8bbc
DM
25#define LISTEN_BACKLOG 50
26#define MAX_EVENTS 10
27
28#define WATCHDOG_DEV "/dev/watchdog"
29
30int watchdog_fd = -1;
31int watchdog_timeout = 20;
32
4915a0e9
DM
33
34typedef struct {
35 int fd;
98099e4f 36 time_t time;
4915a0e9
DM
37} wd_client_t;
38
39#define MAX_CLIENTS 100
40
41static wd_client_t client_list[MAX_CLIENTS];
42
43static wd_client_t *
98099e4f 44alloc_client(int fd, time_t time)
4915a0e9
DM
45{
46 int i;
47
48 for (i = 0; i < MAX_CLIENTS; i++) {
49 if (client_list[i].fd == 0) {
4915a0e9 50 client_list[i].fd = fd;
98099e4f 51 client_list[i].time = time;
4915a0e9
DM
52 return &client_list[i];
53 }
54 }
55
56 return NULL;
57}
58
59static void
60free_client(wd_client_t *wd_client)
61{
62 if (!wd_client)
63 return;
64
98099e4f 65 wd_client->time = 0;
4915a0e9
DM
66 wd_client->fd = 0;
67}
68
98099e4f
DM
69static int
70active_client_count(void)
71{
72 int i, count = 0;
73
74 for (i = 0; i < MAX_CLIENTS; i++) {
75 if (client_list[i].fd != 0 && client_list[i].time != 0) {
76 count++;
77 }
78 }
79
80 return count;
81}
82
da8f8bbc
DM
83static void
84watchdog_close(void)
85{
86 if (watchdog_fd != -1) {
87 if (write(watchdog_fd, "V", 1) == -1) {
88 perror("write magic watchdog close");
89 }
90 if (close(watchdog_fd) == -1) {
91 perror("write magic watchdog close");
92 }
93 }
94
95 watchdog_fd = -1;
96}
97
98int
99main(void)
100{
da8f8bbc
DM
101 struct sockaddr_un my_addr, peer_addr;
102 socklen_t peer_addr_size;
103 struct epoll_event ev, events[MAX_EVENTS];
98099e4f 104 int socket_count, listen_sock, nfds, epollfd, sigfd;
da8f8bbc 105
98099e4f 106
da8f8bbc 107 struct stat fs;
98099e4f
DM
108
109 if (stat(WD_ACTIVE_MARKER, &fs) == 0) {
110 fprintf(stderr, "watchdog active - unable to restart watchdog-mux\n");
111 exit(EXIT_FAILURE);
112 }
113
da8f8bbc
DM
114 if (stat(WATCHDOG_DEV, &fs) == -1) {
115 system("modprobe -q softdog soft_noboot=1"); // fixme
116 }
117
118 if ((watchdog_fd = open(WATCHDOG_DEV, O_WRONLY)) == -1) {
119 perror("watchdog open");
120 exit(EXIT_FAILURE);
121 }
122
123 if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &watchdog_timeout) == -1) {
124 perror("watchdog set timeout");
125 watchdog_close();
126 exit(EXIT_FAILURE);
127 }
128
129 /* read and log watchdog identity */
130 struct watchdog_info wdinfo;
131 if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &wdinfo) == -1) {
132 perror("read watchdog info");
133 watchdog_close();
134 exit(EXIT_FAILURE);
135 }
136
137 wdinfo.identity[sizeof(wdinfo.identity) - 1] = 0; // just to be sure
138 fprintf(stderr, "Watchdog driver '%s', version %x\n",
139 wdinfo.identity, wdinfo.firmware_version);
140
e99d3682 141 socket_count = sd_listen_fds(0);
da8f8bbc 142
e99d3682 143 if (socket_count > 1) {
da8f8bbc 144
ba878e35 145 perror("too many file descriptors received.\n");
e99d3682
DM
146 goto err;
147
148 } else if (socket_count == 1) {
da8f8bbc 149
e99d3682
DM
150 listen_sock = SD_LISTEN_FDS_START + 0;
151
152 } else {
da8f8bbc 153
e99d3682
DM
154 unlink(MY_SOCK_PATH);
155
156 listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
157 if (listen_sock == -1) {
158 perror("socket create");
159 exit(EXIT_FAILURE);
160 }
161
162 memset(&my_addr, 0, sizeof(struct sockaddr_un));
163 my_addr.sun_family = AF_UNIX;
164 strncpy(my_addr.sun_path, MY_SOCK_PATH, sizeof(my_addr.sun_path) - 1);
165
166 if (bind(listen_sock, (struct sockaddr *) &my_addr,
167 sizeof(struct sockaddr_un)) == -1) {
168 perror("socket bind");
169 exit(EXIT_FAILURE);
170 }
da8f8bbc 171
e99d3682
DM
172 if (listen(listen_sock, LISTEN_BACKLOG) == -1) {
173 perror("socket listen");
174 goto err;
175 }
da8f8bbc 176 }
e99d3682 177
da8f8bbc
DM
178 epollfd = epoll_create(10);
179 if (epollfd == -1) {
180 perror("epoll_create");
181 goto err;
182 }
183
184 ev.events = EPOLLIN;
98099e4f 185 ev.data.ptr = alloc_client(listen_sock, 0);
da8f8bbc 186 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
98099e4f
DM
187 perror("epoll_ctl add listen_sock");
188 goto err;
189 }
190
191 sigset_t mask;
192 sigemptyset(&mask);
193 sigaddset(&mask, SIGINT);
194 sigaddset(&mask, SIGTERM);
195 sigaddset(&mask, SIGHUP);
196
197 sigprocmask(SIG_BLOCK, &mask, NULL);
198
199 if ((sigfd = signalfd(-1, &mask, SFD_NONBLOCK)) < 0) {
200 perror("unable to open signalfd");
da8f8bbc
DM
201 goto err;
202 }
203
98099e4f
DM
204 ev.events = EPOLLIN;
205 ev.data.ptr = alloc_client(sigfd, 0);
206 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sigfd, &ev) == -1) {
207 perror("epoll_ctl add sigfd");
208 goto err;
209 }
210
da8f8bbc 211 for (;;) {
1fe42db7 212 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 1000);
da8f8bbc 213 if (nfds == -1) {
1fe42db7
DM
214 if (errno == EINTR)
215 continue;
216
da8f8bbc
DM
217 perror("epoll_pwait");
218 goto err;
219 }
220
1fe42db7
DM
221 if (nfds == 0) { // timeout
222
223 if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) == -1) {
224 perror("watchdog update failed");
225 }
226
227 continue;
228 }
229
98099e4f
DM
230 int terminate = 0;
231
da8f8bbc
DM
232 int n;
233 for (n = 0; n < nfds; ++n) {
4915a0e9
DM
234 wd_client_t *wd_client = events[n].data.ptr;
235 if (wd_client->fd == listen_sock) {
da8f8bbc
DM
236 int conn_sock = accept(listen_sock, (struct sockaddr *) &peer_addr, &peer_addr_size);
237 if (conn_sock == -1) {
238 perror("accept");
239 goto err; // fixme
240 }
241 if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) {
242 perror("setnonblocking");
243 goto err; // fixme
244 }
245
98099e4f 246 wd_client_t *new_client = alloc_client(conn_sock, time(NULL));
4915a0e9
DM
247 if (new_client == NULL) {
248 fprintf(stderr, "unable to alloc wd_client structure\n");
249 goto err; // fixme;
250 }
98099e4f
DM
251
252 mkdir(WD_ACTIVE_MARKER, 0600);
253
da8f8bbc 254 ev.events = EPOLLIN;
4915a0e9 255 ev.data.ptr = new_client;
da8f8bbc
DM
256 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
257 perror("epoll_ctl: add conn_sock");
258 goto err; // fixme
259 }
98099e4f
DM
260 } else if (wd_client->fd == sigfd) {
261
262 /* signal handling */
263
264 int rv = 0;
265 struct signalfd_siginfo si;
266
267 if ((rv = read(sigfd, &si, sizeof(si))) && rv >= 0) {
268 if (si.ssi_signo == SIGHUP) {
269 perror("got SIGHUP - ignored");
270 } else {
271 terminate = 1;
272 fprintf(stderr, "got terminate request\n");
273 }
274 }
275
da8f8bbc
DM
276 } else {
277 char buf[4096];
4915a0e9 278 int cfd = wd_client->fd;
98099e4f 279
da8f8bbc
DM
280 ssize_t bytes = read(cfd, buf, sizeof(buf));
281 if (bytes == -1) {
282 perror("read");
283 goto err; // fixme
284 } else if (bytes > 0) {
4915a0e9 285 fprintf(stderr, "GOT %zd bytes\n", bytes);
da8f8bbc
DM
286 } else {
287 if (events[n].events & EPOLLHUP || events[n].events & EPOLLERR) {
288 printf("GOT %016x event\n", events[n].events);
289 if (epoll_ctl(epollfd, EPOLL_CTL_DEL, cfd, NULL) == -1) {
290 perror("epoll_ctl: del conn_sock");
291 goto err; // fixme
292 }
293 if (close(cfd) == -1) {
294 perror("close conn_sock");
295 goto err; // fixme
296 }
4915a0e9
DM
297 fprintf(stderr, "close client connection\n");
298 free_client(wd_client);
98099e4f
DM
299
300 if (!active_client_count()) {
301 rmdir(WD_ACTIVE_MARKER);
302 }
da8f8bbc
DM
303 }
304 }
305 }
306 }
98099e4f
DM
307 if (terminate)
308 break;
da8f8bbc
DM
309 }
310
98099e4f
DM
311 int active_count = active_client_count();
312 if (active_count > 0) {
313 fprintf(stderr, "exit watchdog-mux with active connections\n");
314 } else {
315 fprintf(stderr, "clean exit\n");
316 watchdog_close();
317 }
318
da8f8bbc
DM
319 unlink(MY_SOCK_PATH);
320 exit(EXIT_SUCCESS);
321
322err:
323 unlink(MY_SOCK_PATH);
324 exit(EXIT_FAILURE);
325}