1 /*
2 * virkeepalive.c: keepalive handling
3 *
4 * Copyright (C) 2011-2013 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include "viralloc.h"
24 #include "virthread.h"
25 #include "virfile.h"
26 #include "virlog.h"
27 #include "virerror.h"
28 #include "virnetsocket.h"
29 #include "virkeepaliveprotocol.h"
30 #include "virkeepalive.h"
31 #include "virprobe.h"
32
33 #define VIR_FROM_THIS VIR_FROM_RPC
34
35 VIR_LOG_INIT("rpc.keepalive");
36
37 struct _virKeepAlive {
38 virObjectLockable parent;
39
40 int interval;
41 unsigned int count;
42 unsigned int countToDeath;
43 gint64 lastPacketReceived;
44 gint64 intervalStart;
45 int timer;
46
47 virKeepAliveSendFunc sendCB;
48 virKeepAliveDeadFunc deadCB;
49 virKeepAliveFreeFunc freeCB;
50 void *client;
51 };
52
53
54 static virClass *virKeepAliveClass;
55 static void virKeepAliveDispose(void *obj);
56
virKeepAliveOnceInit(void)57 static int virKeepAliveOnceInit(void)
58 {
59 if (!VIR_CLASS_NEW(virKeepAlive, virClassForObjectLockable()))
60 return -1;
61
62 return 0;
63 }
64
65 VIR_ONCE_GLOBAL_INIT(virKeepAlive);
66
67 static virNetMessage *
virKeepAliveMessage(virKeepAlive * ka,int proc)68 virKeepAliveMessage(virKeepAlive *ka, int proc)
69 {
70 virNetMessage *msg;
71 const char *procstr = NULL;
72
73 switch (proc) {
74 case KEEPALIVE_PROC_PING:
75 procstr = "request";
76 break;
77 case KEEPALIVE_PROC_PONG:
78 procstr = "response";
79 break;
80 default:
81 VIR_WARN("Refusing to send unknown keepalive message: %d", proc);
82 return NULL;
83 }
84
85 if (!(msg = virNetMessageNew(false)))
86 goto error;
87
88 msg->header.prog = KEEPALIVE_PROGRAM;
89 msg->header.vers = KEEPALIVE_PROTOCOL_VERSION;
90 msg->header.type = VIR_NET_MESSAGE;
91 msg->header.proc = proc;
92
93 if (virNetMessageEncodeHeader(msg) < 0 ||
94 virNetMessageEncodePayloadEmpty(msg) < 0) {
95 virNetMessageFree(msg);
96 goto error;
97 }
98
99 VIR_DEBUG("Sending keepalive %s to client %p", procstr, ka->client);
100 PROBE(RPC_KEEPALIVE_SEND,
101 "ka=%p client=%p prog=%d vers=%d proc=%d",
102 ka, ka->client, msg->header.prog, msg->header.vers, msg->header.proc);
103
104 return msg;
105
106 error:
107 VIR_WARN("Failed to generate keepalive %s", procstr);
108 return NULL;
109 }
110
111
112 static bool
virKeepAliveTimerInternal(virKeepAlive * ka,virNetMessage ** msg)113 virKeepAliveTimerInternal(virKeepAlive *ka,
114 virNetMessage **msg)
115 {
116 gint64 now = g_get_monotonic_time() / G_USEC_PER_SEC;
117 int timeval;
118
119 if (ka->interval <= 0 || ka->intervalStart == 0)
120 return false;
121
122 if (now - ka->intervalStart < ka->interval) {
123 timeval = ka->interval - (now - ka->intervalStart);
124 virEventUpdateTimeout(ka->timer, timeval * 1000);
125 return false;
126 }
127
128 timeval = now - ka->lastPacketReceived;
129 PROBE(RPC_KEEPALIVE_TIMEOUT,
130 "ka=%p client=%p countToDeath=%d idle=%d",
131 ka, ka->client, ka->countToDeath, timeval);
132
133 if (ka->countToDeath == 0) {
134 VIR_DEBUG("No response from client %p after %d keepalive messages "
135 "in %d seconds",
136 ka->client, ka->count, timeval);
137 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
138 _("connection closed due to keepalive timeout"));
139 return true;
140 } else {
141 ka->countToDeath--;
142 ka->intervalStart = now;
143 *msg = virKeepAliveMessage(ka, KEEPALIVE_PROC_PING);
144 virEventUpdateTimeout(ka->timer, ka->interval * 1000);
145 return false;
146 }
147 }
148
149
150 static void
virKeepAliveTimer(int timer G_GNUC_UNUSED,void * opaque)151 virKeepAliveTimer(int timer G_GNUC_UNUSED, void *opaque)
152 {
153 virKeepAlive *ka = opaque;
154 virNetMessage *msg = NULL;
155 bool dead;
156 void *client;
157
158 virObjectRef(ka);
159 virObjectLock(ka);
160
161 client = ka->client;
162 dead = virKeepAliveTimerInternal(ka, &msg);
163
164 virObjectUnlock(ka);
165
166 if (!dead && !msg)
167 goto cleanup;
168
169 if (dead) {
170 ka->deadCB(client);
171 } else if (ka->sendCB(client, msg) < 0) {
172 VIR_WARN("Failed to send keepalive request to client %p", client);
173 virNetMessageFree(msg);
174 }
175
176 cleanup:
177 virObjectUnref(ka);
178 }
179
180
181 virKeepAlive *
virKeepAliveNew(int interval,unsigned int count,void * client,virKeepAliveSendFunc sendCB,virKeepAliveDeadFunc deadCB,virKeepAliveFreeFunc freeCB)182 virKeepAliveNew(int interval,
183 unsigned int count,
184 void *client,
185 virKeepAliveSendFunc sendCB,
186 virKeepAliveDeadFunc deadCB,
187 virKeepAliveFreeFunc freeCB)
188 {
189 virKeepAlive *ka;
190
191 VIR_DEBUG("client=%p, interval=%d, count=%u", client, interval, count);
192
193 if (virKeepAliveInitialize() < 0)
194 return NULL;
195
196 if (!(ka = virObjectLockableNew(virKeepAliveClass)))
197 return NULL;
198
199 ka->interval = interval;
200 ka->count = count;
201 ka->countToDeath = count;
202 ka->timer = -1;
203 ka->client = client;
204 ka->sendCB = sendCB;
205 ka->deadCB = deadCB;
206 ka->freeCB = freeCB;
207
208 PROBE(RPC_KEEPALIVE_NEW,
209 "ka=%p client=%p",
210 ka, ka->client);
211
212 return ka;
213 }
214
215
216 void
virKeepAliveDispose(void * obj)217 virKeepAliveDispose(void *obj)
218 {
219 virKeepAlive *ka = obj;
220
221 PROBE(RPC_KEEPALIVE_DISPOSE,
222 "ka=%p", ka);
223
224 ka->freeCB(ka->client);
225 }
226
227
228 int
virKeepAliveStart(virKeepAlive * ka,int interval,unsigned int count)229 virKeepAliveStart(virKeepAlive *ka,
230 int interval,
231 unsigned int count)
232 {
233 int ret = -1;
234 gint64 delay;
235 int timeout;
236 gint64 now;
237
238 virObjectLock(ka);
239
240 if (ka->timer >= 0) {
241 VIR_DEBUG("Keepalive messages already enabled");
242 ret = 0;
243 goto cleanup;
244 }
245
246 if (interval > 0) {
247 if (ka->interval > 0) {
248 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
249 _("keepalive interval already set"));
250 goto cleanup;
251 }
252 /* Guard against overflow */
253 if (interval > INT_MAX / 1000) {
254 virReportError(VIR_ERR_INTERNAL_ERROR,
255 _("keepalive interval %d too large"), interval);
256 goto cleanup;
257 }
258 ka->interval = interval;
259 ka->count = count;
260 ka->countToDeath = count;
261 }
262
263 if (ka->interval <= 0) {
264 VIR_DEBUG("Keepalive messages disabled by configuration");
265 ret = 0;
266 goto cleanup;
267 }
268
269 PROBE(RPC_KEEPALIVE_START,
270 "ka=%p client=%p interval=%d count=%u",
271 ka, ka->client, interval, count);
272
273 now = g_get_monotonic_time() / G_USEC_PER_SEC;
274 delay = now - ka->lastPacketReceived;
275 if (delay > ka->interval)
276 timeout = 0;
277 else
278 timeout = ka->interval - delay;
279 ka->intervalStart = now - (ka->interval - timeout);
280 ka->timer = virEventAddTimeout(timeout * 1000, virKeepAliveTimer,
281 ka, virObjectFreeCallback);
282 if (ka->timer < 0)
283 goto cleanup;
284
285 /* the timer now has another reference to this object */
286 virObjectRef(ka);
287 ret = 0;
288
289 cleanup:
290 virObjectUnlock(ka);
291 return ret;
292 }
293
294
295 void
virKeepAliveStop(virKeepAlive * ka)296 virKeepAliveStop(virKeepAlive *ka)
297 {
298 virObjectLock(ka);
299
300 PROBE(RPC_KEEPALIVE_STOP,
301 "ka=%p client=%p",
302 ka, ka->client);
303
304 if (ka->timer > 0) {
305 virEventRemoveTimeout(ka->timer);
306 ka->timer = -1;
307 }
308
309 virObjectUnlock(ka);
310 }
311
312
313 int
virKeepAliveTimeout(virKeepAlive * ka)314 virKeepAliveTimeout(virKeepAlive *ka)
315 {
316 int timeout;
317
318 if (!ka)
319 return -1;
320
321 virObjectLock(ka);
322
323 if (ka->interval <= 0 || ka->intervalStart == 0) {
324 timeout = -1;
325 } else {
326 timeout = ka->interval - (time(NULL) - ka->intervalStart);
327 if (timeout < 0)
328 timeout = 0;
329 /* Guard against overflow */
330 if (timeout > INT_MAX / 1000)
331 timeout = INT_MAX / 1000;
332 }
333
334 virObjectUnlock(ka);
335
336 if (timeout < 0)
337 return -1;
338 else
339 return timeout * 1000;
340 }
341
342
343 bool
virKeepAliveTrigger(virKeepAlive * ka,virNetMessage ** msg)344 virKeepAliveTrigger(virKeepAlive *ka,
345 virNetMessage **msg)
346 {
347 bool dead;
348
349 *msg = NULL;
350 if (!ka)
351 return false;
352
353 virObjectLock(ka);
354 dead = virKeepAliveTimerInternal(ka, msg);
355 virObjectUnlock(ka);
356
357 return dead;
358 }
359
360
361 bool
virKeepAliveCheckMessage(virKeepAlive * ka,virNetMessage * msg,virNetMessage ** response)362 virKeepAliveCheckMessage(virKeepAlive *ka,
363 virNetMessage *msg,
364 virNetMessage **response)
365 {
366 bool ret = false;
367
368 VIR_DEBUG("ka=%p, client=%p, msg=%p",
369 ka, ka ? ka->client : "(null)", msg);
370
371 *response = NULL;
372 if (!ka)
373 return false;
374
375 virObjectLock(ka);
376
377 ka->countToDeath = ka->count;
378 ka->intervalStart = g_get_monotonic_time() / G_USEC_PER_SEC;
379 ka->lastPacketReceived = ka->intervalStart;
380
381 if (msg->header.prog == KEEPALIVE_PROGRAM &&
382 msg->header.vers == KEEPALIVE_PROTOCOL_VERSION &&
383 msg->header.type == VIR_NET_MESSAGE) {
384 PROBE(RPC_KEEPALIVE_RECEIVED,
385 "ka=%p client=%p prog=%d vers=%d proc=%d",
386 ka, ka->client, msg->header.prog,
387 msg->header.vers, msg->header.proc);
388 ret = true;
389 switch (msg->header.proc) {
390 case KEEPALIVE_PROC_PING:
391 VIR_DEBUG("Got keepalive request from client %p", ka->client);
392 *response = virKeepAliveMessage(ka, KEEPALIVE_PROC_PONG);
393 break;
394
395 case KEEPALIVE_PROC_PONG:
396 VIR_DEBUG("Got keepalive response from client %p", ka->client);
397 break;
398
399 default:
400 VIR_DEBUG("Ignoring unknown keepalive message %d from client %p",
401 msg->header.proc, ka->client);
402 }
403 }
404
405 if (ka->timer >= 0)
406 virEventUpdateTimeout(ka->timer, ka->interval * 1000);
407
408 virObjectUnlock(ka);
409
410 return ret;
411 }
412