]> git.proxmox.com Git - pve-ha-manager.git/blob - src/watchdog-mux.c
use new wd_client_t data structure to store info about clients
[pve-ha-manager.git] / src / watchdog-mux.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <sys/ioctl.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 #include <sys/epoll.h>
13
14 #include <linux/types.h>
15 #include <linux/watchdog.h>
16
17 #include <systemd/sd-daemon.h>
18
19 #define MY_SOCK_PATH "/run/watchdog-mux.sock"
20 #define LISTEN_BACKLOG 50
21 #define MAX_EVENTS 10
22
23 #define WATCHDOG_DEV "/dev/watchdog"
24
25 int watchdog_fd = -1;
26 int watchdog_timeout = 20;
27
28
29 typedef struct {
30 int fd;
31 int time;
32 } wd_client_t;
33
34 #define MAX_CLIENTS 100
35
36 static wd_client_t client_list[MAX_CLIENTS];
37
38 static wd_client_t *
39 alloc_client(int fd)
40 {
41 int i;
42
43 for (i = 0; i < MAX_CLIENTS; i++) {
44 if (client_list[i].fd == 0) {
45 memset(&client_list[i], 1, sizeof(wd_client_t));
46 client_list[i].fd = fd;
47 return &client_list[i];
48 }
49 }
50
51 return NULL;
52 }
53
54 static void
55 free_client(wd_client_t *wd_client)
56 {
57 if (!wd_client)
58 return;
59
60 wd_client->fd = 0;
61 }
62
63 static void
64 watchdog_close(void)
65 {
66 if (watchdog_fd != -1) {
67 if (write(watchdog_fd, "V", 1) == -1) {
68 perror("write magic watchdog close");
69 }
70 if (close(watchdog_fd) == -1) {
71 perror("write magic watchdog close");
72 }
73 }
74
75 watchdog_fd = -1;
76 }
77
78 int
79 main(void)
80 {
81 struct sockaddr_un my_addr, peer_addr;
82 socklen_t peer_addr_size;
83 struct epoll_event ev, events[MAX_EVENTS];
84 int socket_count, listen_sock, nfds, epollfd;
85
86 struct stat fs;
87 if (stat(WATCHDOG_DEV, &fs) == -1) {
88 system("modprobe -q softdog soft_noboot=1"); // fixme
89 }
90
91 if ((watchdog_fd = open(WATCHDOG_DEV, O_WRONLY)) == -1) {
92 perror("watchdog open");
93 exit(EXIT_FAILURE);
94 }
95
96 if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &watchdog_timeout) == -1) {
97 perror("watchdog set timeout");
98 watchdog_close();
99 exit(EXIT_FAILURE);
100 }
101
102 /* read and log watchdog identity */
103 struct watchdog_info wdinfo;
104 if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &wdinfo) == -1) {
105 perror("read watchdog info");
106 watchdog_close();
107 exit(EXIT_FAILURE);
108 }
109
110 wdinfo.identity[sizeof(wdinfo.identity) - 1] = 0; // just to be sure
111 fprintf(stderr, "Watchdog driver '%s', version %x\n",
112 wdinfo.identity, wdinfo.firmware_version);
113
114 socket_count = sd_listen_fds(0);
115
116 if (socket_count > 1) {
117
118 perror("too many file descriptors received.\n");
119 goto err;
120
121 } else if (socket_count == 1) {
122
123 listen_sock = SD_LISTEN_FDS_START + 0;
124
125 } else {
126
127 unlink(MY_SOCK_PATH);
128
129 listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
130 if (listen_sock == -1) {
131 perror("socket create");
132 exit(EXIT_FAILURE);
133 }
134
135 memset(&my_addr, 0, sizeof(struct sockaddr_un));
136 my_addr.sun_family = AF_UNIX;
137 strncpy(my_addr.sun_path, MY_SOCK_PATH, sizeof(my_addr.sun_path) - 1);
138
139 if (bind(listen_sock, (struct sockaddr *) &my_addr,
140 sizeof(struct sockaddr_un)) == -1) {
141 perror("socket bind");
142 exit(EXIT_FAILURE);
143 }
144
145 if (listen(listen_sock, LISTEN_BACKLOG) == -1) {
146 perror("socket listen");
147 goto err;
148 }
149 }
150
151 epollfd = epoll_create(10);
152 if (epollfd == -1) {
153 perror("epoll_create");
154 goto err;
155 }
156
157 ev.events = EPOLLIN;
158 ev.data.ptr = alloc_client(listen_sock);
159 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
160 perror("epoll_ctl: listen_sock");
161 goto err;
162 }
163
164 for (;;) {
165 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 1000);
166 if (nfds == -1) {
167 if (errno == EINTR)
168 continue;
169
170 perror("epoll_pwait");
171 goto err;
172 }
173
174 if (nfds == 0) { // timeout
175
176 if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) == -1) {
177 perror("watchdog update failed");
178 }
179
180 continue;
181 }
182
183 int n;
184 for (n = 0; n < nfds; ++n) {
185 wd_client_t *wd_client = events[n].data.ptr;
186 if (wd_client->fd == listen_sock) {
187 int conn_sock = accept(listen_sock, (struct sockaddr *) &peer_addr, &peer_addr_size);
188 if (conn_sock == -1) {
189 perror("accept");
190 goto err; // fixme
191 }
192 if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) {
193 perror("setnonblocking");
194 goto err; // fixme
195 }
196
197 wd_client_t *new_client = alloc_client(conn_sock);
198 if (new_client == NULL) {
199 fprintf(stderr, "unable to alloc wd_client structure\n");
200 goto err; // fixme;
201 }
202 ev.events = EPOLLIN;
203 ev.data.ptr = new_client;
204 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
205 perror("epoll_ctl: add conn_sock");
206 goto err; // fixme
207 }
208 } else {
209 char buf[4096];
210 int cfd = wd_client->fd;
211
212 ssize_t bytes = read(cfd, buf, sizeof(buf));
213 if (bytes == -1) {
214 perror("read");
215 goto err; // fixme
216 } else if (bytes > 0) {
217 fprintf(stderr, "GOT %zd bytes\n", bytes);
218 } else {
219 if (events[n].events & EPOLLHUP || events[n].events & EPOLLERR) {
220 printf("GOT %016x event\n", events[n].events);
221 if (epoll_ctl(epollfd, EPOLL_CTL_DEL, cfd, NULL) == -1) {
222 perror("epoll_ctl: del conn_sock");
223 goto err; // fixme
224 }
225 if (close(cfd) == -1) {
226 perror("close conn_sock");
227 goto err; // fixme
228 }
229 fprintf(stderr, "close client connection\n");
230 free_client(wd_client);
231 }
232 }
233 }
234 }
235 }
236
237 printf("DONE\n");
238
239 // out:
240
241 watchdog_close();
242 unlink(MY_SOCK_PATH);
243 exit(EXIT_SUCCESS);
244
245 err:
246 unlink(MY_SOCK_PATH);
247 exit(EXIT_FAILURE);
248 }