2
* Copyright (C) 2004-2006 Jean-Yves Lefort <jylefort@brutele.be>
3
* Copyright (C) 2004-2008 Jean-Yves Lefort <jylefort@brutele.be>
4
5
* This program is free software; you can redistribute it and/or modify
5
6
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* the Free Software Foundation; either version 3 of the License, or
7
8
* (at your option) any later version.
9
10
* This program is distributed in the hope that it will be useful,
11
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
13
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
* You should have received a copy of the GNU General Public License along
16
* with this program; if not, write to the Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
21
#include "lm-icmp.h"
26
/* forward declaration to avoid circular includes */
27
#ifndef __TYPEDEF_LM_APPLET__
28
#define __TYPEDEF_LM_APPLET__
29
typedef struct _LMApplet LMApplet;
34
LM_HOST_STATUS_RESOLVING,
35
LM_HOST_STATUS_RUNNING,
41
25
#include <stdint.h>
33
* We use multiple error levels to avoid UI flicker. For instance, if
34
* a single global error was used and ICMP error messages were
35
* continually received, the global error would quickly alternate
36
* between NULL (after an echo request is successfully sent) and the
37
* ICMP error received a couple of milliseconds later.
39
* Note that error levels must be kept sorted by decreasing priority.
46
52
#include <string.h>
47
53
#include <stdarg.h>
48
54
#include <sys/types.h>
49
55
#include <sys/socket.h>
51
57
#include <glib/gi18n.h>
53
58
#include "lm-util.h"
54
59
#include "lm-applet.h"
56
61
#ifndef HAVE_REENTRANT_RESOLVER
57
62
G_LOCK_DEFINE_STATIC(resolver);
66
#define MIN_TIMEOUT 100
67
#define DEFAULT_TIMEOUT 1000
61
class LM:Host from G:Object (abstract)
70
class LM:Host (abstract)
63
72
protected LMApplet *applet;
64
73
property POINTER applet (link, flags = CONSTRUCT_ONLY);
69
78
private struct addrinfo *addrinfo destroywith freeaddrinfo;
70
79
private const LMSocket *sock;
71
private gboolean resolving;
81
public gboolean resolving;
82
property BOOLEAN resolving (link, export);
73
84
public char *ip destroywith g_free;
74
85
property STRING ip (link);
76
public unsigned int host_id;
77
property UINT host_id (flags = CONSTRUCT_ONLY, link);
79
private uint16_t sent_seq; /* seq of last echo request */
80
private uint16_t received_seq; /* seq of last echo reply */
81
private GTimeVal received_time; /* timestamp of last echo reply */
89
private RequestInfo requests[256];
90
private uint8_t sent_seq; /* seq of last echo request */
83
92
public gboolean alive;
84
93
property BOOLEAN alive (link, export);
87
96
private unsigned int send_timeout_id;
88
97
private unsigned int dead_timeout_id;
90
public double roundtrip_time;
91
property DOUBLE roundtrip_time (minimum = 0, export)
99
private LMTimeSpan max_roundtrip_time;
101
public LMTimeSpan roundtrip_time;
102
property INT64 roundtrip_time (export, type = LMTimeSpan)
94
self->last_received = lm_time();
105
self->last_received_ticks = lm_get_ticks();
95
106
lm_source_clear(&selfp->dead_timeout_id);
96
self->roundtrip_time = g_value_get_double(VAL);
108
self->roundtrip_time = g_value_get_int64(VAL);
109
if (self->roundtrip_time > selfp->max_roundtrip_time)
110
selfp->max_roundtrip_time = self->roundtrip_time;
97
112
self_set_alive(self, TRUE);
101
g_value_set_double(VAL, self->roundtrip_time);
116
g_value_set_int64(VAL, self->roundtrip_time);
104
protected char *error destroywith g_free;
105
119
property STRING error
108
lm_source_clear(&selfp->dead_timeout_id);
110
self->error = g_value_dup_string(VAL);
111
self_set_alive(self, FALSE);
112
self_set_status(self, LM_HOST_STATUS_ERROR);
116
g_value_set_string(VAL, self->error);
120
set_error (self, const char *format (check null), ...)
122
g_value_set_string(VAL, self_get_error(self));
125
private char *errors[NUM_ERRORS]
130
for (i = 0; i < G_N_ELEMENTS(VAR); i++)
139
for (i = 0; i < G_N_ELEMENTS(selfp->errors); i++)
140
if (selfp->errors[i])
141
return selfp->errors[i];
146
[G_GNUC_PRINTF(3, 4)]
148
set_error (self, Error error, const char *format (check null), ...)
125
153
va_start(args, format);
126
error = g_strdup_vprintf(format, args);
154
msg = g_strdup_vprintf(format, args);
129
g_object_set(G_OBJECT(self), LM_HOST_PROP_ERROR(error), NULL);
133
protected time_t last_received;
135
protected LMHostStatus status;
136
property INT status (link, export, type = LMHostStatus);
138
override (G:Object) void
139
finalize (G:Object *object (check null type))
141
Self *self = SELF(object);
157
g_free(selfp->errors[error]);
158
selfp->errors[error] = msg;
160
lm_source_clear(&selfp->dead_timeout_id);
161
self_set_alive(self, FALSE);
163
g_object_notify(G_OBJECT(self), "error");
167
clear_error (self, Error error)
169
if (! selfp->errors[error])
172
g_free(selfp->errors[error]);
173
selfp->errors[error] = NULL;
175
g_object_notify(G_OBJECT(self), "error");
178
public LMTimeSpan last_received_ticks;
182
selfp->host_id = lm_shell_allocate_host_id(lm_shell, self);
184
lm_g_object_connect(self, self->applet,
185
"signal::notify::delay", self_delay_changed_h, self,
143
193
if (selfp->resolve_timeout_id)
144
194
g_source_remove(selfp->resolve_timeout_id);
145
195
if (selfp->send_timeout_id)
147
197
if (selfp->dead_timeout_id)
148
198
g_source_remove(selfp->dead_timeout_id);
150
PARENT_HANDLER(object);
153
override (G:Object) GObject *
154
constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params)
159
object = PARENT_HANDLER(type, n_construct_properties, construct_params);
162
lm_g_object_connect(self, self->applet,
163
"swapped-signal::notify::delay", self_install_send_timeout, self,
200
lm_shell_deallocate_host_id(lm_shell, selfp->host_id);
174
g_return_if_fail(selfp->resolving == FALSE);
176
selfp->resolving = TRUE;
206
g_return_if_fail(self->resolving == FALSE);
208
self_set_resolving(self, TRUE);
177
210
g_object_ref(self);
179
self_set_status(self, LM_HOST_STATUS_RESOLVING);
180
211
lm_thread_create(self_resolve_thread_cb, self);
250
281
if (! selfp->sock->init_error)
283
/* complete success */
284
self_clear_error(self, ERROR_RESOLVE);
252
286
if (! selfp->send_timeout_id)
254
/* initial resolution */
255
288
self_send_echo_request(self); /* send first packet */
256
289
self_install_send_timeout(self); /* install send loop */
260
self_set_error(self, _("socket could not be initialized: %s"), selfp->sock->init_error);
293
self_set_error(self, ERROR_RESOLVE, _("socket could not be initialized: %s"), selfp->sock->init_error);
263
self_set_error(self, _("unsupported address family"));
296
self_set_error(self, ERROR_RESOLVE, _("unsupported address family"));
265
/* success (even if no socket), refresh in one hour */
298
/* DNS success (even if no socket), refresh in one hour */
266
299
self_install_resolve_timeout(self, 3600 * 1000);
270
303
selfp->sock = NULL;
271
self_set_error(self, _("unable to resolve hostname: %s"), gai_strerror(status));
304
self_set_error(self, ERROR_RESOLVE, _("unable to resolve hostname: %s"), gai_strerror(status));
273
/* failure, retry in 10 seconds */
306
/* DNS failure, retry in 10 seconds */
274
307
self_install_resolve_timeout(self, 10 * 1000);
310
/* if an error was installed above, remove the send timeout */
311
if (selfp->errors[ERROR_RESOLVE])
312
lm_source_clear(&selfp->send_timeout_id);
277
314
g_object_unref(self);
280
* A note on gdk_flush(): as adviced in the GDK threads
317
* A note on gdk_flush(): as advised in the GDK threads
281
318
* documentation, we only call gdk_flush() from a thread other
282
319
* than our main thread.
316
348
GError *err = NULL;
350
g_return_if_fail(selfp->sock != NULL);
351
g_return_if_fail(selfp->sock->init_error == NULL);
318
352
g_return_if_fail(selfp->addrinfo != NULL);
320
if (lm_icmp_send_echo_request(selfp->sock, selfp->addrinfo, self->host_id, selfp->sent_seq, &err))
354
if (lm_icmp_send_echo_request(selfp->sock, selfp->addrinfo, selfp->host_id, selfp->sent_seq, &err))
358
req = &selfp->requests[selfp->sent_seq];
359
req->sent_time = lm_get_ticks();
322
361
selfp->sent_seq++;
323
self_set_status(self, LM_HOST_STATUS_RUNNING);
363
self_clear_error(self, ERROR_SEND);
325
365
if (! selfp->dead_timeout_id)
326
selfp->dead_timeout_id = g_timeout_add(self->applet->timeout, self_dead_timeout_cb, self);
369
if (selfp->max_roundtrip_time > 0)
371
timeout = (selfp->max_roundtrip_time / 1000) * 2;
372
if (timeout < MIN_TIMEOUT)
373
timeout = MIN_TIMEOUT;
376
timeout = DEFAULT_TIMEOUT;
378
selfp->dead_timeout_id = gdk_threads_add_timeout(timeout, self_dead_timeout_cb, self);
330
self_set_error(self, _("unable to send echo request: %s"), err->message);
383
self_set_error(self, ERROR_SEND, _("unable to send echo request: %s"), err->message);
331
384
g_error_free(err);
389
delay_changed_h (GObject *object, GParamSpec *pspec, gpointer user_data)
391
Self *self = user_data;
393
if (selfp->send_timeout_id)
395
lm_source_clear(&selfp->send_timeout_id);
396
self_install_send_timeout(self);
398
/* else we cannot send packets: do not install the send timeout */
336
402
install_send_timeout (self)
338
if (selfp->send_timeout_id)
339
g_source_remove(selfp->send_timeout_id);
340
selfp->send_timeout_id = g_timeout_add(self->applet->delay, self_send_timeout_cb, self);
404
g_return_if_fail(selfp->send_timeout_id == 0);
405
g_return_if_fail(selfp->sock != NULL);
406
g_return_if_fail(selfp->sock->init_error == NULL);
407
g_return_if_fail(selfp->addrinfo != NULL);
409
selfp->send_timeout_id = gdk_threads_add_timeout(self->applet->delay, self_send_timeout_cb, self);
363
425
Self *self = data;
367
427
self_set_alive(self, FALSE);
368
429
selfp->dead_timeout_id = 0;
372
430
return FALSE; /* remove source */
376
434
reply_received (self, const LMICMPReply *reply (check null))
378
if (reply->seq <= selfp->received_seq && lm_tvcmp(&reply->received, &selfp->received_time, <=))
379
return; /* older reply received out of order, ignore */
381
selfp->received_seq = reply->seq;
382
selfp->received_time = reply->received;
384
if (lm_icmp_reply_is_echo_reply(reply, selfp->sock->domain))
385
self_set_roundtrip_time(self, lm_icmp_reply_get_roundtrip_time(reply));
439
* If there is no send timeout, an error (DNS or socket) has
440
* occurred and we are unable to send packets: ignore this reply,
441
* otherwise we would set alive back to TRUE.
443
if (! selfp->send_timeout_id)
446
req = &selfp->requests[reply->seq];
447
if (! req->sent_time)
448
return; /* no matching request, ignore */
450
if (reply->received_time < req->sent_time)
452
* Negative roundtrip-time: this should not normally happen
453
* since we use a monotonic clock source, but it is possible
454
* that the OS improperly implements it, or that the monitored
455
* host returns garbage, etc. Acknowledge the reply but
456
* otherwise ignore it.
461
self_set_error(self, ERROR_RECEIVE, "%s", reply->error);
387
self_set_error(self, "%s", lm_icmp_reply_get_description(reply, selfp->sock->domain));
464
self_clear_error(self, ERROR_RECEIVE);
465
self_set_roundtrip_time(self, reply->received_time - req->sent_time);
469
/* we have matched the request, clear it */