diff options
Diffstat (limited to 'src/pinger.c')
-rw-r--r-- | src/pinger.c | 339 |
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); |