Logo Search packages:      
Sourcecode: fair version File versions  Download package

transponder.c

/******************************************************************************

      transponder.c -- main transponder (load advertisement) program
      Copyright (C) 2004  Wessel Dankers <wsl@uvt.nl>

      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

      $Id: transponder.c 23144 2007-12-21 15:36:48Z wsl $
      $URL: https://infix.uvt.nl/its-id/trunk/sources/fair-0.5.1/src/transponder.c $

******************************************************************************/

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include <avl.h>

#include "init.h"
#include "conf.h"
#include "chrono.h"
#include "error.h"
#include "protocol.h"
#include "fair.h"

#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif

typedef struct source {
      avl_node_t node;
      int fd;
} source_t;

static source_t source_0 = {{0}};

static int sourcecmp(const source_t *a, const source_t *b) {
      assert(a && b);
      return a->fd - b->fd;
}

static avl_tree_t sources = {NULL, NULL, NULL, (avl_compare_t)sourcecmp, NULL};

typedef struct adres {
      avl_node_t node;
      source_t *last;
      socklen_t len;
} adres_t;

static adres_t adres_0 = {{0}};

static int adrescmp(adres_t *a, adres_t *b) {
      socklen_t alen, blen;
      assert(a && b);
      alen = a->len;
      blen = b->len;
      return memcmp(a+1, b+1, alen < blen ? alen : blen) ?: alen - blen;
}

static avl_tree_t adressen = {NULL, NULL, NULL, (avl_compare_t)adrescmp, NULL};

const char *listener_new_udp(const char *port) {
      int fd, err;
      const int on = 1;
      struct addrinfo hints = {0}, *ais = NULL, *ai;
      const char *res = NULL;
      source_t *src;
      int gelukt = 0;

      unless(port) return "internal error";

      hints.ai_family = PF_UNSPEC;
      hints.ai_socktype = SOCK_DGRAM;
      hints.ai_flags = AI_PASSIVE;

      err = getaddrinfo(NULL, port, &hints, &ais);
      if(err)
            return gai_strerror(err) ?: "unknown error";

      for(ai = ais; ai; ai = ai->ai_next) {
            fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
            if(fd == -1) {
                  res = gai_strerror(errno) ?: "unknown error";
            } else {
                  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
                  if(bind(fd, ai->ai_addr, ai->ai_addrlen)) {
                        res = strerror(errno) ?: "unknown error";
                  } else {
                        src = xalloc(sizeof *src);
                        *src = source_0;
                        src->fd = fd;
                        avl_init_node(&src->node, src);
                        avl_insert_node(&sources, &src->node);
                        gelukt++;
                        continue;
                  }
                  if(close(fd) == -1)
                        syslog(LOG_ERR, "close(): %m");
            }
      }

      return gelukt ? NULL : res;
}

static void adres_new(const char *adres, const char *poort) {
      int err;
      struct addrinfo hints, *ai = NULL, *aip;
      adres_t *ad;

      unless(adres) return;
      unless(poort) return;
      
      memset(&hints, 0, sizeof hints);
      hints.ai_family = AF_UNSPEC;
      hints.ai_protocol = PROTO_UNSPEC;
      hints.ai_flags = 0;
      hints.ai_socktype = SOCK_DGRAM;

      err = getaddrinfo(adres, poort, &hints, &ai);
      if(err)
            syslog_exit(LOG_CRIT, "getaddrinfo(%s, %s): %s. Program exit.", adres, poort, gai_strerror(err));
      if(!ai)
            syslog_exit(LOG_CRIT, "getaddrinfo(%s, %s): no addresses found. Program exit.", adres, poort);

      for(aip = ai; aip; aip = aip->ai_next) {
            ad = xalloc(sizeof *ad + aip->ai_addrlen);
            *ad = adres_0;
            memcpy(ad+1, aip->ai_addr, ad->len = aip->ai_addrlen);
            avl_init_node(&ad->node, ad);
            avl_insert_node(&adressen, &ad->node);
      }
      freeaddrinfo(ai);
}

static loadping_t *msg = NULL;
static size_t msglen = 0;

static double max_capacity = 100.0;

static bool spam(struct sockaddr *sa, socklen_t salen, int fd) {
      assert(msg);
      assert(sa);
      return sendto(fd, msg, msglen, MSG_DONTWAIT|MSG_NOSIGNAL, sa, salen) != msglen;
}

static void spamall(bool thorough, stamp_t delay) {
      avl_node_t *n, *s;
      adres_t *ad;
      source_t *src;
      int count = 0;

      for(n = adressen.head; n; n = n->next) {
            ad = n->item;
            assert(ad);
            count += ad->last || thorough;
      }

      if(!count)
            return (void)usleep(delay);

      for(n = adressen.head; n; n = n->next) {
            ad = n->item;
            assert(ad);
            if(ad->last) {
                  if(spam((struct sockaddr *)(ad+1), ad->len, ad->last->fd))
                        ad->last = NULL;
            } else if(thorough) {
                  for(s = sources.head; s; s = s->next) {
                        src = s->item;
                        assert(src);
                        if(!spam((struct sockaddr *)(ad+1), ad->len, src->fd)) {
                              ad->last = src;
                              break;
                        }
                  }
            }
            usleep(delay/count);
      }
}

/* FIXME: use _slightly_ more efficient version below only if available;
** getloadavg() otherwise. Thanks to guus for the suggestion.
*/

static int procfd = -1;

static void readload(void) {
      char buf[1024];
      ssize_t r;
      double l15, l5, l1;

      unless(msg) return;
      unless(procfd != -1) return;

      r = lseek(procfd, 0, SEEK_SET);
      if(r == -1)
            return;
      r = read(procfd, buf, sizeof buf - 1);
      if(r <= 0)
            return;
      buf[r] = '\0';
      if(sscanf(buf, "%lf %lf %lf ", &l1, &l5, &l15) != 3)
            return;
      msg->capacity = htons(max_capacity / exp(l1));
}

int main(int argc, char **argv) {
      const char *err;
      char *hostname;
      char *config = NULL;
      int i, opt;

      while((opt = getopt(argc, argv, "c:")) != EOF) {
            switch(opt) {
                  case 'c':
                        config = strdup(optarg);
                        break;
                  default:
                        syslog_exit(LOG_CRIT, "Unknown option. Program exit.");
                        break;
            }
      }

      if(optind >= argc)
            syslog_exit(LOG_CRIT, "Usage: %s [-c config] host [host ...]. Program exit.", argv[0]);

      if(sizeof *msg != PROTOCOL_SIZE)
            syslog_exit(LOG_CRIT, "Internal program error. Program exit.");

      if(!init_signals())
            syslog_exit(LOG_CRIT, "sigaction(): %m. Program exit.");

      if(!conf_load(config, FALSE))
            syslog_exit(LOG_CRIT, "Error reading config file. Program exit.");

      max_capacity = conf_MaxCapacity;

      msg = xalloc(sizeof *msg + HOST_NAME_MAX + 1);
      hostname = (char *)(msg + 1);
      if(gethostname(hostname, HOST_NAME_MAX + 1))
            syslog_exit(LOG_CRIT, "gethostname(): %m. Program exit.");
      msg->version = PROTOCOL_VERSION;
      msg->capacity = htons(0); /* capacity minus current load */
      msg->hostlen = strlen(hostname);
      msglen = sizeof *msg + msg->hostlen;

      procfd = open("/proc/loadavg", O_RDONLY);
      if(procfd == -1)
            syslog_exit(LOG_CRIT, "open(/proc/loadavg): %m. Program exit.");

      assert(conf_WorkerService);
      err = listener_new_udp(conf_WorkerService);
      if(err)
            syslog_exit(LOG_CRIT, "Error listening on UDP port %s: %s", conf_WorkerService, err);

      assert(conf_TransponderService);
      for(i = optind; i < argc; i++)
            adres_new(argv[i], conf_TransponderService);

      if(!conf_Debug && !init_daemon())
            syslog_exit(LOG_CRIT, "daemon(): %m. Program exit.");

      if(conf_DropPrivs && !init_privs())
            syslog_exit(LOG_CRIT, "dropprivs(): %m. Program exit.");

      syslog(LOG_INFO, "transponder started, sending from %s/udp to %s/udp",
            conf_WorkerService, conf_TransponderService);

      for(;;) {
            readload();
            spamall(TRUE, conf_PingInterval);
            for(i = 0; i < 3; i++)
                  spamall(FALSE, conf_PingInterval);
            if(init_hupped) {
                  init_hupped = FALSE;
                  if(conf_reload(config))
                        syslog(LOG_INFO, "Config reloaded.");
                  else
                        syslog(LOG_ERR, "Errors in config file, not reloaded");
            }
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index