~ubuntu-branches/debian/sid/link-monitor-applet/sid

« back to all changes in this revision

Viewing changes to src/lm-host.gob

  • Committer: Bazaar Package Importer
  • Author(s): Adriaan Peeters
  • Date: 2008-03-30 22:26:13 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20080330222613-5aubcuo9mgg2n7st
Tags: upstream-3.0
ImportĀ upstreamĀ versionĀ 3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright (C) 2004-2006 Jean-Yves Lefort <jylefort@brutele.be>
 
2
 * Link Monitor Applet
 
3
 * Copyright (C) 2004-2008 Jean-Yves Lefort <jylefort@brutele.be>
3
4
 *
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.
8
9
 *
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.
13
14
 *
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.
17
18
 */
18
19
 
19
 
requires 2.0.14
20
 
 
21
20
%headertop{
22
21
#include "lm-icmp.h"
23
22
%}
24
23
 
25
 
%h{
26
 
/* forward declaration to avoid circular includes */
27
 
#ifndef __TYPEDEF_LM_APPLET__
28
 
#define __TYPEDEF_LM_APPLET__
29
 
typedef struct _LMApplet LMApplet;
30
 
#endif
31
 
 
32
 
typedef enum
33
 
{
34
 
  LM_HOST_STATUS_RESOLVING,
35
 
  LM_HOST_STATUS_RUNNING,
36
 
  LM_HOST_STATUS_ERROR
37
 
} LMHostStatus;
38
 
%}
39
 
 
40
24
%privateheader{
41
25
#include <stdint.h>
 
26
 
 
27
typedef struct
 
28
{
 
29
  LMTimeSpan    sent_time;
 
30
} RequestInfo;
 
31
 
 
32
/*
 
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.
 
38
 *
 
39
 * Note that error levels must be kept sorted by decreasing priority.
 
40
 */
 
41
typedef enum
 
42
{
 
43
  ERROR_RESOLVE,
 
44
  ERROR_SEND,
 
45
  ERROR_RECEIVE,
 
46
 
 
47
  NUM_ERRORS
 
48
} Error;
42
49
%}
43
50
 
44
51
%{
45
 
#include "config.h"
46
52
#include <string.h>
47
53
#include <stdarg.h>
48
54
#include <sys/types.h>
49
55
#include <sys/socket.h>
50
56
#include <netdb.h>
51
57
#include <glib/gi18n.h>
52
 
#include <eel/eel.h>
53
58
#include "lm-util.h"
54
59
#include "lm-applet.h"
55
60
 
56
61
#ifndef HAVE_REENTRANT_RESOLVER
57
62
G_LOCK_DEFINE_STATIC(resolver);
58
63
#endif
 
64
 
 
65
/* in milliseconds */
 
66
#define MIN_TIMEOUT             100
 
67
#define DEFAULT_TIMEOUT         1000
59
68
%}
60
69
 
61
 
class LM:Host from G:Object (abstract)
 
70
class LM:Host (abstract)
62
71
{
63
72
  protected LMApplet *applet;
64
73
  property POINTER applet (link, flags = CONSTRUCT_ONLY);
68
77
 
69
78
  private struct addrinfo *addrinfo destroywith freeaddrinfo;
70
79
  private const LMSocket *sock;
71
 
  private gboolean resolving;
 
80
 
 
81
  public gboolean resolving;
 
82
  property BOOLEAN resolving (link, export);
72
83
 
73
84
  public char *ip destroywith g_free;
74
85
  property STRING ip (link);
75
86
 
76
 
  public unsigned int host_id;
77
 
  property UINT host_id (flags = CONSTRUCT_ONLY, link);
 
87
  private int host_id;
78
88
 
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 */
82
91
 
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;
89
98
 
90
 
  public double roundtrip_time;
91
 
  property DOUBLE roundtrip_time (minimum = 0, export)
 
99
  private LMTimeSpan max_roundtrip_time;
 
100
 
 
101
  public LMTimeSpan roundtrip_time;
 
102
  property INT64 roundtrip_time (export, type = LMTimeSpan)
92
103
    set
93
104
    {
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);
 
107
 
 
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;
 
111
 
97
112
      self_set_alive(self, TRUE);
98
113
    }
99
114
    get
100
115
    {
101
 
      g_value_set_double(VAL, self->roundtrip_time);
 
116
      g_value_set_int64(VAL, self->roundtrip_time);
102
117
    };
103
118
 
104
 
  protected char *error destroywith g_free;
105
119
  property STRING error
106
 
    set
107
 
    {
108
 
      lm_source_clear(&selfp->dead_timeout_id);
109
 
      g_free(self->error);
110
 
      self->error = g_value_dup_string(VAL);
111
 
      self_set_alive(self, FALSE);
112
 
      self_set_status(self, LM_HOST_STATUS_ERROR);
113
 
    }
114
120
    get
115
121
    {
116
 
      g_value_set_string(VAL, self->error);
117
 
    };
118
 
 
119
 
  public void
120
 
    set_error (self, const char *format (check null), ...)
 
122
      g_value_set_string(VAL, self_get_error(self));
 
123
    };
 
124
 
 
125
  private char *errors[NUM_ERRORS]
 
126
    destroy
 
127
    {
 
128
      int i;
 
129
 
 
130
      for (i = 0; i < G_N_ELEMENTS(VAR); i++)
 
131
        g_free(VAR[i]);
 
132
    };
 
133
 
 
134
  public const char *
 
135
    get_error (self)
 
136
  {
 
137
    int i;
 
138
 
 
139
    for (i = 0; i < G_N_ELEMENTS(selfp->errors); i++)
 
140
      if (selfp->errors[i])
 
141
        return selfp->errors[i];
 
142
 
 
143
    return NULL;
 
144
  }
 
145
 
 
146
  [G_GNUC_PRINTF(3, 4)]
 
147
  private void
 
148
    set_error (self, Error error, const char *format (check null), ...)
121
149
  {
122
150
    va_list args;
123
 
    char *error;
 
151
    char *msg;
124
152
 
125
153
    va_start(args, format);
126
 
    error = g_strdup_vprintf(format, args);
 
154
    msg = g_strdup_vprintf(format, args);
127
155
    va_end(args);
128
156
 
129
 
    g_object_set(G_OBJECT(self), LM_HOST_PROP_ERROR(error), NULL);
130
 
    g_free(error);
131
 
  }
132
 
 
133
 
  protected time_t last_received;
134
 
 
135
 
  protected LMHostStatus status;
136
 
  property INT status (link, export, type = LMHostStatus);
137
 
 
138
 
  override (G:Object) void
139
 
    finalize (G:Object *object (check null type))
140
 
  {
141
 
    Self *self = SELF(object);
142
 
 
 
157
    g_free(selfp->errors[error]);
 
158
    selfp->errors[error] = msg;
 
159
 
 
160
    lm_source_clear(&selfp->dead_timeout_id);
 
161
    self_set_alive(self, FALSE);
 
162
 
 
163
    g_object_notify(G_OBJECT(self), "error");
 
164
  }
 
165
 
 
166
  private void
 
167
    clear_error (self, Error error)
 
168
  {
 
169
    if (! selfp->errors[error])
 
170
      return;
 
171
 
 
172
    g_free(selfp->errors[error]);
 
173
    selfp->errors[error] = NULL;
 
174
 
 
175
    g_object_notify(G_OBJECT(self), "error");
 
176
  }
 
177
 
 
178
  public LMTimeSpan last_received_ticks;
 
179
 
 
180
  constructor (self)
 
181
  {
 
182
    selfp->host_id = lm_shell_allocate_host_id(lm_shell, self);
 
183
 
 
184
    lm_g_object_connect(self, self->applet,
 
185
                        "signal::notify::delay", self_delay_changed_h, self,
 
186
                        NULL);
 
187
 
 
188
    self_resolve(self);
 
189
  }
 
190
 
 
191
  finalize (self)
 
192
  {
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);
149
199
 
150
 
    PARENT_HANDLER(object);
151
 
  }
152
 
 
153
 
  override (G:Object) GObject *
154
 
    constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params)
155
 
  {
156
 
    GObject *object;
157
 
    Self *self;
158
 
 
159
 
    object = PARENT_HANDLER(type, n_construct_properties, construct_params);
160
 
    self = SELF(object);
161
 
 
162
 
    lm_g_object_connect(self, self->applet,
163
 
                        "swapped-signal::notify::delay", self_install_send_timeout, self,
164
 
                        NULL);
165
 
 
166
 
    self_resolve(self);
167
 
 
168
 
    return object;
 
200
    lm_shell_deallocate_host_id(lm_shell, selfp->host_id);
169
201
  }
170
202
 
171
203
  private void
172
204
    resolve (self)
173
205
  {
174
 
    g_return_if_fail(selfp->resolving == FALSE);
175
 
 
176
 
    selfp->resolving = TRUE;
 
206
    g_return_if_fail(self->resolving == FALSE);
 
207
 
 
208
    self_set_resolving(self, TRUE);
 
209
 
177
210
    g_object_ref(self);
178
 
 
179
 
    self_set_status(self, LM_HOST_STATUS_RESOLVING);
180
211
    lm_thread_create(self_resolve_thread_cb, self);
181
212
  }
182
213
 
232
263
 
233
264
    GDK_THREADS_ENTER();
234
265
 
235
 
    selfp->resolving = FALSE;
 
266
    self_set_resolving(self, FALSE);
236
267
 
237
268
    if (selfp->addrinfo)
238
269
      freeaddrinfo(selfp->addrinfo);
249
280
          {
250
281
            if (! selfp->sock->init_error)
251
282
              {
 
283
                /* complete success */
 
284
                self_clear_error(self, ERROR_RESOLVE);
 
285
 
252
286
                if (! selfp->send_timeout_id)
253
287
                  {
254
 
                    /* initial resolution */
255
288
                    self_send_echo_request(self);       /* send first packet */
256
289
                    self_install_send_timeout(self);    /* install send loop */
257
290
                  }
258
291
              }
259
292
            else
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);
261
294
          }
262
295
        else
263
 
          self_set_error(self, _("unsupported address family"));
 
296
          self_set_error(self, ERROR_RESOLVE, _("unsupported address family"));
264
297
 
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);
267
300
      }
268
301
    else
269
302
      {
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));
272
305
 
273
 
        /* failure, retry in 10 seconds */
 
306
        /* DNS failure, retry in 10 seconds */
274
307
        self_install_resolve_timeout(self, 10 * 1000);
275
308
      }
276
309
 
 
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);
 
313
 
277
314
    g_object_unref(self);
278
315
 
279
316
    /*
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.
283
320
     */
288
325
  }
289
326
 
290
327
  private void
291
 
    install_resolve_timeout (self, unsigned int delay)
 
328
    install_resolve_timeout (self, int delay)
292
329
  {
293
330
    g_return_if_fail(selfp->resolve_timeout_id == 0);
294
 
    selfp->resolve_timeout_id = g_timeout_add(delay, self_resolve_timeout_cb, self);
 
331
    selfp->resolve_timeout_id = gdk_threads_add_timeout(delay, self_resolve_timeout_cb, self);
295
332
  }
296
333
 
297
334
  private gboolean
299
336
  {
300
337
    Self *self = data;
301
338
 
302
 
    GDK_THREADS_ENTER();
303
 
 
304
339
    self_resolve(self);
305
340
 
306
341
    selfp->resolve_timeout_id = 0;
307
 
 
308
 
    GDK_THREADS_LEAVE();
309
 
 
310
342
    return FALSE;               /* remove source */
311
343
  }
312
344
 
315
347
  {
316
348
    GError *err = NULL;
317
349
 
 
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);
319
353
 
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))
321
355
      {
 
356
        RequestInfo *req;
 
357
 
 
358
        req = &selfp->requests[selfp->sent_seq];
 
359
        req->sent_time = lm_get_ticks();
 
360
 
322
361
        selfp->sent_seq++;
323
 
        self_set_status(self, LM_HOST_STATUS_RUNNING);
 
362
 
 
363
        self_clear_error(self, ERROR_SEND);
324
364
 
325
365
        if (! selfp->dead_timeout_id)
326
 
          selfp->dead_timeout_id = g_timeout_add(self->applet->timeout, self_dead_timeout_cb, self);
 
366
          {
 
367
            int timeout;
 
368
 
 
369
            if (selfp->max_roundtrip_time > 0)
 
370
              {
 
371
                timeout = (selfp->max_roundtrip_time / 1000) * 2;
 
372
                if (timeout < MIN_TIMEOUT)
 
373
                  timeout = MIN_TIMEOUT;
 
374
              }
 
375
            else
 
376
              timeout = DEFAULT_TIMEOUT;
 
377
 
 
378
            selfp->dead_timeout_id = gdk_threads_add_timeout(timeout, self_dead_timeout_cb, self);
 
379
          }
327
380
      }
328
381
    else
329
382
      {
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);
332
385
      }
333
386
  }
334
387
 
335
388
  private void
 
389
    delay_changed_h (GObject *object, GParamSpec *pspec, gpointer user_data)
 
390
  {
 
391
    Self *self = user_data;
 
392
 
 
393
    if (selfp->send_timeout_id)
 
394
      {
 
395
        lm_source_clear(&selfp->send_timeout_id);
 
396
        self_install_send_timeout(self);
 
397
      }
 
398
    /* else we cannot send packets: do not install the send timeout */
 
399
  }
 
400
 
 
401
  private void
336
402
    install_send_timeout (self)
337
403
  {
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);
 
408
 
 
409
    selfp->send_timeout_id = gdk_threads_add_timeout(self->applet->delay, self_send_timeout_cb, self);
341
410
  }
342
411
 
343
412
  private gboolean
345
414
  {
346
415
    Self *self = data;
347
416
 
348
 
    GDK_THREADS_ENTER();
349
 
 
350
 
    if (selfp->addrinfo && selfp->sock && ! selfp->sock->init_error)
351
 
      self_send_echo_request(self);
352
 
    else
353
 
      self_set_alive(self, FALSE);
354
 
 
355
 
    GDK_THREADS_LEAVE();
 
417
    self_send_echo_request(self);
356
418
 
357
419
    return TRUE;                /* keep source */
358
420
  }
362
424
  {
363
425
    Self *self = data;
364
426
 
365
 
    GDK_THREADS_ENTER();
366
 
 
367
427
    self_set_alive(self, FALSE);
 
428
 
368
429
    selfp->dead_timeout_id = 0;
369
 
 
370
 
    GDK_THREADS_LEAVE();
371
 
 
372
430
    return FALSE;               /* remove source */
373
431
  }
374
432
 
375
433
  public void
376
434
    reply_received (self, const LMICMPReply *reply (check null))
377
435
  {
378
 
    if (reply->seq <= selfp->received_seq && lm_tvcmp(&reply->received, &selfp->received_time, <=))
379
 
      return; /* older reply received out of order, ignore */
380
 
 
381
 
    selfp->received_seq = reply->seq;
382
 
    selfp->received_time = reply->received;
383
 
 
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));
 
436
    RequestInfo *req;
 
437
 
 
438
    /*
 
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.
 
442
     */
 
443
    if (! selfp->send_timeout_id)
 
444
      return;
 
445
 
 
446
    req = &selfp->requests[reply->seq];
 
447
    if (! req->sent_time)
 
448
      return; /* no matching request, ignore */
 
449
 
 
450
    if (reply->received_time < req->sent_time)
 
451
      /*
 
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.
 
457
       */
 
458
      goto end;
 
459
 
 
460
    if (reply->error)
 
461
      self_set_error(self, ERROR_RECEIVE, "%s", reply->error);
386
462
    else
387
 
      self_set_error(self, "%s", lm_icmp_reply_get_description(reply, selfp->sock->domain));
 
463
      {
 
464
        self_clear_error(self, ERROR_RECEIVE);
 
465
        self_set_roundtrip_time(self, reply->received_time - req->sent_time);
 
466
      }
 
467
 
 
468
  end:
 
469
    /* we have matched the request, clear it */
 
470
    req->sent_time = 0;
388
471
  }
389
472
}