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