aboutsummaryrefslogtreecommitdiff
path: root/src/pinger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pinger.c')
-rw-r--r--src/pinger.c339
1 files changed, 217 insertions, 122 deletions
diff --git a/src/pinger.c b/src/pinger.c
index 370d5d1..0674747 100644
--- a/src/pinger.c
+++ b/src/pinger.c
@@ -228,7 +228,8 @@ hostlist_concat(HOSTLIST *a, HOSTLIST *b)
}
static HOSTLIST *hostlist;
-static HOSTPING *conf_hostping_tail;
+static HOSTPING *immutable_hostping_tail;
+static RBT_TREE *host_name_tree, *host_addr_tree;
static pthread_rwlock_t hostlist_rwlock = PTHREAD_RWLOCK_INITIALIZER;
typedef enum update_type {
@@ -238,33 +239,109 @@ typedef enum update_type {
} UPDATE_TYPE;
static pthread_mutex_t update_mutex = PTHREAD_MUTEX_INITIALIZER;
-static int check_host(char const *name);
+
+enum {
+ CHECK_HOST_NOT_FOUND,
+ CHECK_HOST_FOUND,
+ CHECK_HOST_IMMUTABLE
+};
+
+static int check_host(char const *name, struct sockaddr *addr, socklen_t len);
static int update_add(UPDATE_TYPE t, void *data);
+static int
+host_name_cmp(HOSTPING *a, HOSTPING *b)
+{
+ return strcmp(a->name, b->name);
+}
+
+static int
+host_addr_cmp(HOSTPING *a, HOSTPING *b)
+{
+ if (a->addrlen != b->addrlen)
+ return a->addrlen - b->addrlen;
+ return memcmp(a->addr, b->addr, b->addrlen);
+}
+
void
pinger_setup(void)
{
hostlist = hostlist_create();
if (!hostlist)
emalloc_die();
+ host_name_tree = rbt_tree_create(host_name_cmp);
+ if (!host_name_tree)
+ emalloc_die();
+ host_addr_tree = rbt_tree_create(host_addr_cmp);
+ if (!host_addr_tree)
+ emalloc_die();
}
-int
+RBT_LOOKUP_RESULT
+pinger_rbt_insert(HOSTPING *hp)
+{
+ RBT_LOOKUP_RESULT res;
+
+ res = rbt_insert(host_name_tree, hp);
+ if (res != RBT_LOOKUP_NOENT)
+ return res;
+ res = rbt_insert(host_addr_tree, hp);
+ if (res != RBT_LOOKUP_NOENT)
+ rbt_delete(host_name_tree, hp);
+ return res;
+}
+
+void
+pinger_rbt_delete(HOSTPING *hp)
+{
+ rbt_delete(host_name_tree, hp);
+ rbt_delete(host_addr_tree, hp);
+}
+
+static inline HOSTPING *
+hostping_lookup_by_name(char const *name)
+{
+ HOSTPING key;
+
+ key.name = (char*) name;
+ return rbt_lookup(host_name_tree, &key);
+}
+
+static inline HOSTPING *
+hostping_lookup_by_addr(struct sockaddr *addr, socklen_t addrlen)
+{
+ HOSTPING key;
+
+ if (!addr)
+ return NULL;
+ key.addr = addr;
+ key.addrlen = addrlen;
+ return rbt_lookup(host_addr_tree, &key);
+}
+
+RBT_LOOKUP_RESULT
pinger_host_add(char const *name, struct sockaddr *addr, socklen_t addrlen)
{
- HOSTPING *hp = hostping_create(name, addr, addrlen);
+ RBT_LOOKUP_RESULT res;
+ HOSTPING *hp;
+
+ hp = hostping_create(name, addr, addrlen);
if (!hp)
- return -1;
- hostlist_add(hostlist, hp);
- return 0;
+ return RBT_LOOKUP_FAILURE;
+ res = pinger_rbt_insert(hp);
+ if (res == RBT_LOOKUP_NOENT)
+ hostlist_add(hostlist, hp);
+ else
+ hostping_free(hp);
+ return res;
}
#define FOR_EACH_HOSTPING(hp) for (hp = hostlist->head; hp; hp = hp->next)
-#define FOR_EACH_LOCAL_HOSTPING(hp) \
- for (hp = conf_hostping_tail \
- ? conf_hostping_tail->next \
- : hostlist->head; \
- hp; \
+#define FOR_EACH_MUTABLE_HOSTPING(hp) \
+ for (hp = immutable_hostping_tail \
+ ? immutable_hostping_tail->next \
+ : hostlist->head; \
+ hp; \
hp = hp->next)
static void
@@ -455,12 +532,9 @@ get_hostname_stat(char const *name, struct json_value **retval)
*retval = NULL;
pthread_rwlock_rdlock(&hostlist_rwlock);
- FOR_EACH_HOSTPING(hp) {
- if (strcmp(hp->name, name) == 0) {
- rc = get_hostping_stat(hp, retval);
- break;
- }
- }
+ hp = hostping_lookup_by_name(name);
+ if (hp)
+ rc = get_hostping_stat(hp, retval);
pthread_rwlock_unlock(&hostlist_rwlock);
return rc;
}
@@ -526,22 +600,6 @@ get_all_hosts(struct json_value **retval)
return rc;
}
-static int
-hostping_match(HOSTPING const *hp, char const *name, struct addrinfo *res)
-{
- if (!hp)
- return 1;
- if (strcmp(hp->name, name) == 0)
- return 0;
- for (; res; res = res->ai_next) {
- if (hp->addrlen == res->ai_addrlen
- && memcmp(hp->addr, res->ai_addr, res->ai_addrlen) == 0) {
- return 0;
- }
- }
- return 1;
-}
-
int
get_host_matches(struct json_value *ar)
{
@@ -549,26 +607,23 @@ get_host_matches(struct json_value *ar)
char const *name;
struct addrinfo *res;
};
- struct addrinfo **aiv;
size_t count = json_array_length(ar);
size_t i;
struct addrinfo hints;
int errors = 0;
- aiv = calloc(count, sizeof(*aiv));
- if (!aiv)
- return -1;
-
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_protocol = IPPROTO_TCP;
- for (i = 0; i < count; i++) {
+ pthread_rwlock_rdlock(&hostlist_rwlock);
+ for (i = 0; !errors && i < count; i++) {
char const *name;
int rc;
- struct addrinfo *res;
- struct json_value *obj, *jv;
-
+ struct addrinfo *res, *rp;
+ struct json_value *obj, *jv, *var;
+ HOSTPING *hp;
+
json_array_get(ar, i, &obj);
json_object_get(obj, "name", &jv);
name = jv->v.s;
@@ -576,7 +631,6 @@ get_host_matches(struct json_value *ar)
rc = getaddrinfo(name, NULL, &hints, &res);
switch (rc) {
case 0:
- aiv[i] = res;
break;
case EAI_FAIL:
@@ -584,7 +638,6 @@ get_host_matches(struct json_value *ar)
case EAI_NODATA:
#endif
case EAI_NONAME:
- aiv[i] = NULL;
if (!(jv = json_new_string("Doesn't resolve"))
|| json_object_set(obj, "error", jv)) {
json_value_free(jv);
@@ -610,40 +663,33 @@ get_host_matches(struct json_value *ar)
}
if (errors)
break;
- }
- if (!errors) {
- HOSTPING *hp;
-
- pthread_rwlock_rdlock(&hostlist_rwlock);
- FOR_EACH_HOSTPING(hp) {
- for (i = 0; i < count; i++) {
- struct json_value *obj, *var, *jv;
- json_array_get(ar, i, &obj);
- json_object_get(obj, "name", &jv);
-
- if (hostping_match(hp, jv->v.s, aiv[i]) == 0) {
- json_object_get(obj, "hosts", &var);
- if (!(jv = json_new_string(hp->name))
- || json_array_append(var, jv)) {
- json_value_free(jv);
- errors = 1;
- break;
- }
+ json_object_get(obj, "hosts", &var);
+
+ hp = hostping_lookup_by_name(name);
+ if (hp) {
+ if (!(jv = json_new_string(hp->name))
+ || json_array_append(var, jv)) {
+ json_value_free(jv);
+ errors = 1;
+ }
+ }
+ for (rp = res; !errors && rp; rp = rp->ai_next) {
+ hp = hostping_lookup_by_addr(rp->ai_addr,
+ rp->ai_addrlen);
+
+ if (hp) {
+ if (!(jv = json_new_string(hp->name))
+ || json_array_append(var, jv)) {
+ json_value_free(jv);
+ errors = 1;
}
}
- if (errors)
- break;
}
- pthread_rwlock_unlock(&hostlist_rwlock);
- }
-
- for (i = 0; i < count; i++) {
- if (aiv[i])
- freeaddrinfo(aiv[i]);
+ freeaddrinfo(res);
}
- free(aiv);
-
+ pthread_rwlock_unlock(&hostlist_rwlock);
+
return errors;
}
@@ -653,7 +699,8 @@ pinger_host_delete_by_name(char const *name)
int rc;
pthread_mutex_lock(&update_mutex);
- if (check_host(name)) {
+
+ if (check_host(name, NULL, 0) == CHECK_HOST_FOUND) {
char *cp = strdup(name);
if (cp) {
if (update_add(UPDATE_DELETE, cp) == 0)
@@ -676,7 +723,7 @@ pinger_host_add_name(char const *name)
int rc = 0;
struct addrinfo hints, *res;
HOSTPING *hp;
-
+
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_protocol = IPPROTO_TCP;
@@ -686,7 +733,7 @@ pinger_host_add_name(char const *name)
}
pthread_mutex_lock(&update_mutex);
- if (check_host(name) == 0) {
+ if (check_host(name, res->ai_addr, res->ai_addrlen) == CHECK_HOST_NOT_FOUND) {
rc = UPD_NOMEM;
hp = hostping_create(name, res->ai_addr, res->ai_addrlen);
if (hp) {
@@ -775,6 +822,22 @@ pinger_hostlist_set(struct json_value *obj, char const **err_text,
if (rc)
RETERR(MHD_HTTP_BAD_REQUEST,
"host name does not resolve", i + 1);
+ switch (check_host(jv->v.s, res->ai_addr, res->ai_addrlen)) {
+ case CHECK_HOST_NOT_FOUND:
+ /* Safe to add */
+ break;
+ case CHECK_HOST_FOUND:
+ if (mode == UPDATE_REPLACE) {
+ /* It's OK in replace mode */
+ break;
+ }
+ /* fall through */
+ case CHECK_HOST_IMMUTABLE:
+ freeaddrinfo(res);
+ RETERR(MHD_HTTP_BAD_REQUEST,
+ "host name or IP address already exists",
+ i + 1);
+ }
hp = hostping_create(jv->v.s, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
if (!hp)
@@ -806,7 +869,7 @@ typedef struct update_entry {
static UPDATE_ENTRY *update_head, *update_tail;
-static void save_local_ip_list(void);
+static void save_mutable_ip_list(void);
static int
update_add(UPDATE_TYPE t, void *data)
@@ -845,26 +908,32 @@ update_add(UPDATE_TYPE t, void *data)
return 0;
}
-static HOSTPING *
-hostping_find_local(char const *name)
+static void
+hostlist_copy_stat(HOSTLIST *hl)
{
HOSTPING *hp;
-
- FOR_EACH_LOCAL_HOSTPING(hp) {
- if (strcmp(hp->name, name) == 0)
- break;
+ for (hp = hl->head; hp; hp = hp->next) {
+ HOSTPING *cur = hostping_lookup_by_name(hp->name);
+ if (cur)
+ hp->stat_last = cur->stat_last;
}
- return hp;
}
static void
-hostlist_copy_stat(HOSTLIST *hl)
+hostlist_index(HOSTLIST *hl)
{
HOSTPING *hp;
for (hp = hl->head; hp; hp = hp->next) {
- HOSTPING *cur = hostping_find_local(hp->name);
- if (cur)
- hp->stat_last = cur->stat_last;
+ pinger_rbt_insert(hp); //FIXME: Handle RBT_LOOKUP_FAILURE (out of memory)
+ }
+}
+
+static void
+hostlist_unindex(HOSTLIST *hl)
+{
+ HOSTPING *hp;
+ for (hp = hl->head; hp; hp = hp->next) {
+ pinger_rbt_delete(hp);
}
}
@@ -881,41 +950,45 @@ p903_update_commit(void)
UPDATE_ENTRY *next = update_head->next;
switch (update_head->type) {
case UPDATE_APPEND:
+ hostlist_index(update_head->v.hlist);
hostlist_concat(hostlist, update_head->v.hlist);
hostlist_free(update_head->v.hlist);
break;
case UPDATE_REPLACE:
hostlist_copy_stat(update_head->v.hlist);
- if (conf_hostping_tail) {
+ if (immutable_hostping_tail) {
HOSTLIST *tmp = update_head->v.hlist;
- hp = conf_hostping_tail->next;
+ hp = immutable_hostping_tail->next;
if (tmp->head)
- tmp->head->prev = conf_hostping_tail;
- conf_hostping_tail->next = tmp->head;
+ tmp->head->prev = immutable_hostping_tail;
+ immutable_hostping_tail->next = tmp->head;
hostlist->tail = tmp->tail;
hostlist->count += tmp->count;
while (hp) {
HOSTPING *next = hp->next;
+ pinger_rbt_delete(hp);
hostping_free(hp);
hp = next;
hostlist->count--;
}
+ hostlist_index(tmp);
free(tmp);
} else {
+ hostlist_unindex(hostlist);
hostlist_free(hostlist);
hostlist = update_head->v.hlist;
+ hostlist_index(hostlist);
}
break;
case UPDATE_DELETE:
- FOR_EACH_LOCAL_HOSTPING(hp) {
- if (strcmp(hp->name, update_head->v.name) == 0) {
- hostlist_remove(hostlist, hp);
- hostping_free(hp);
- break;
- }
+ hp = hostping_lookup_by_name(update_head->v.name);
+ if (hp) {
+ hostlist_remove(hostlist, hp);
+ pinger_rbt_delete(hp);
+ hostping_free(hp);
}
free(update_head->v.name);
}
@@ -925,23 +998,35 @@ p903_update_commit(void)
}
update_tail = NULL;
if (upd)
- save_local_ip_list();
+ save_mutable_ip_list();
pthread_rwlock_unlock(&hostlist_rwlock);
pthread_mutex_unlock(&update_mutex);
}
+/*
+ * Check if the given host is already in the hostlist. Look for
+ * matching name or, if specified, address.
+ * Return value:
+ * CHECK_HOST_NOT_FOUND - Host not found.
+ * CHECK_HOST_FOUND - Host found.
+ * CHECK_HOST_IMMUTABLE - Host found and is immutable.
+ */
static int
-check_host(char const *name)
+check_host(char const *name, struct sockaddr *addr, socklen_t len)
{
HOSTPING *hp;
UPDATE_ENTRY *uent;
int found;
-
- FOR_EACH_HOSTPING(hp) {
- if (strcmp(hp->name, name) == 0)
- return 1;
- }
- found = 0;
+
+ hp = hostping_lookup_by_name(name);
+ if (!hp)
+ hp = hostping_lookup_by_addr(addr, len);
+ if (hp) {
+ if (hp->immutable)
+ return CHECK_HOST_IMMUTABLE;
+ found = 1;
+ } else
+ found = 0;
for (uent = update_head; uent; uent = uent->next) {
switch (uent->type) {
case UPDATE_APPEND:
@@ -958,9 +1043,8 @@ check_host(char const *name)
found--;
}
}
- return found;
+ return found > 0 ? CHECK_HOST_FOUND : CHECK_HOST_NOT_FOUND;
}
-
static int ping_fd;
@@ -1126,16 +1210,23 @@ p903_init(void)
struct protoent *proto;
int i;
FILE *fp;
-
- conf_hostping_tail = hostlist->tail;
- fp = fopen(LOCAL_IP_LIST_FILE, "r");
+ HOSTPING *hp;
+
+ /* Mark all hosts configured so far as immutable and mark the
+ end of the immutable hostlist segment. */
+ for (hp = hostlist->head; hp; hp = hp->next)
+ hp->immutable = 1;
+ immutable_hostping_tail = hostlist->tail;
+
+ /* Read in mutable IP list. */
+ fp = fopen(MUTABLE_IP_LIST_FILE, "r");
if (fp) {
- int rc = file_read_ip_list(fp, LOCAL_IP_LIST_FILE);
+ int rc = file_read_ip_list(fp, MUTABLE_IP_LIST_FILE);
if (rc == CF_RET_FAIL)
exit(1);
fclose(fp);
} else if (errno != ENOENT) {
- fatal("can't open %s: %s", LOCAL_IP_LIST_FILE,
+ fatal("can't open %s: %s", MUTABLE_IP_LIST_FILE,
strerror(errno));
exit(1);
}
@@ -1143,7 +1234,8 @@ p903_init(void)
if (hostlist->count == 0) {
info("no IP addresses configured, starting anyway");
}
-
+
+ /* Open ICMP socket */
proto = getprotobyname("icmp");
if (!proto) {
fatal("no entry for icmp in the system protocol database");
@@ -1155,12 +1247,15 @@ p903_init(void)
exit(1);
}
+ /* Initialize ICMP identifier. */
ping_ident = getpid() & 0xffff;
-
+
+ /* Initialize payload buffer */
data_buffer = emalloc(data_length);
for (i = 0; i < data_length; i++)
data_buffer[i] = i;
+ /* Initialize sequence number database. */
seqidx = calloc(MAX_SEQNO + 1, sizeof(seqidx[0]));
if (!seqidx)
emalloc_die();
@@ -1513,26 +1608,26 @@ err:
}
static void
-save_local_ip_list(void)
+save_mutable_ip_list(void)
{
FILE *fp;
HOSTPING *hp;
info("saving mutable IP address list");
- fp = fopen(LOCAL_IP_LIST_FILE, "w");
+ fp = fopen(MUTABLE_IP_LIST_FILE, "w");
if (!fp) {
if (errno == ENOENT) {
- create_dirs(LOCAL_IP_LIST_FILE);
- fp = fopen(LOCAL_IP_LIST_FILE, "w");
+ create_dirs(MUTABLE_IP_LIST_FILE);
+ fp = fopen(MUTABLE_IP_LIST_FILE, "w");
}
if (!fp) {
fatal("can't open %s for writing: %s",
- LOCAL_IP_LIST_FILE,
+ MUTABLE_IP_LIST_FILE,
strerror(errno));
return;
}
}
- FOR_EACH_LOCAL_HOSTPING(hp) {
+ FOR_EACH_MUTABLE_HOSTPING(hp) {
fprintf(fp, "%s\n", hp->name);
}
fclose(fp);

Return to:

Send suggestions and report system problems to the System administrator.