diff --git a/src/Makefile.am b/src/Makefile.am index bfe93ce8..fb0913e4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -54,9 +54,9 @@ ss_server_SOURCES = utils.c \ ss_local_LDADD = $(SS_COMMON_LIBS) ss_tunnel_LDADD = $(SS_COMMON_LIBS) ss_server_LDADD = $(SS_COMMON_LIBS) -ss_local_LDADD += $(top_builddir)/libudns/libudns.la +ss_local_LDADD += $(top_builddir)/libudns/libudns.la -lpthread ss_tunnel_LDADD += $(top_builddir)/libudns/libudns.la -ss_server_LDADD += $(top_builddir)/libudns/libudns.la +ss_server_LDADD += $(top_builddir)/libudns/libudns.la -lpthread ss_local_CFLAGS = $(AM_CFLAGS) -DUDPRELAY_LOCAL ss_tunnel_CFLAGS = $(AM_CFLAGS) -DUDPRELAY_LOCAL -DUDPRELAY_TUNNEL diff --git a/src/Makefile.in b/src/Makefile.in index 20e7a113..fbaca156 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -374,9 +374,11 @@ ss_server_SOURCES = utils.c \ bitcoin.c \ server.c -ss_local_LDADD = $(SS_COMMON_LIBS) $(top_builddir)/libudns/libudns.la +ss_local_LDADD = $(SS_COMMON_LIBS) $(top_builddir)/libudns/libudns.la \ + -lpthread ss_tunnel_LDADD = $(SS_COMMON_LIBS) $(top_builddir)/libudns/libudns.la -ss_server_LDADD = $(SS_COMMON_LIBS) $(top_builddir)/libudns/libudns.la +ss_server_LDADD = $(SS_COMMON_LIBS) $(top_builddir)/libudns/libudns.la \ + -lpthread ss_local_CFLAGS = $(AM_CFLAGS) -DUDPRELAY_LOCAL ss_tunnel_CFLAGS = $(AM_CFLAGS) -DUDPRELAY_LOCAL -DUDPRELAY_TUNNEL ss_server_CFLAGS = $(AM_CFLAGS) -DUDPRELAY_REMOTE diff --git a/src/bitcoin.c b/src/bitcoin.c index 62dac59f..6ec61d57 100644 --- a/src/bitcoin.c +++ b/src/bitcoin.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -589,9 +590,9 @@ struct btc_list { struct btc_client *clients; size_t number; char *file; - time_t last_check_time; - time_t last_modify_time; - pthread_mutex_t lock; + pthread_rwlock_t lock; + pthread_t thread; + int running; }; static int cmp_btc_client(const void *l, const void *r) { @@ -603,98 +604,130 @@ static int cmp_btc_client(const void *l, const void *r) { extern struct btc_list *bitcoin_init_list(const char *file) { struct btc_list *l = calloc(1, sizeof(struct btc_list)); l->clients = NULL; - pthread_mutex_init(&l->lock, NULL); - l->number = 0; - l->file = strdup(file); - l->last_check_time = 0; - l->last_modify_time = 0; + pthread_rwlock_init(&l->lock, NULL); + l->number = 0; + l->file = strdup(file); + l->running = 1; return l; } -extern size_t bitcoin_tryload_list(struct btc_list *list) { +static void *check_list(void *ptr) { + struct btc_list *list = (struct btc_list *)ptr; + time_t last_check_time = 0; + time_t last_modify_time = 0; + struct stat attrib; - FILE *f = NULL; size_t size = 8; - size_t idx = 0; - struct btc_client *clients = NULL; char line[64]; + int running = 1; + + while (1) { + pthread_rwlock_rdlock(&list->lock); + running = list->running; + pthread_rwlock_unlock(&list->lock); - int is_need_update = 0; - pthread_mutex_lock(&list->lock); - // update when: 1. last check time over than 10 seconds - // 2. file has been modified - if (time(NULL) > list->last_check_time + 10) { - list->last_check_time = time(NULL); - stat(list->file, &attrib); - if (list->last_modify_time != attrib.st_mtime) { - is_need_update = 1; + if (running == 0) { + break; // stop thread } - } - pthread_mutex_unlock(&list->lock); - if (is_need_update == 0) { - return 0; - } - f = fopen(list->file, "rb"); - if (f == NULL) { - return 0; - } + // update when: 1. last check time over than 10 seconds + // 2. file has been modified + if (time(NULL) < last_check_time + 10) { + sleep(1); + continue; + } + last_check_time = time(NULL); - clients = calloc(size, sizeof(struct btc_client)); - while (fgets(line, sizeof(line), f)) { - while (strlen(line) > 0 && !isalpha(line[strlen(line) - 1])) { - line[strlen(line) - 1] = '\0'; + if (stat(list->file, &attrib) != 0) { + continue; } - if (strlen(line) > 35 || strlen(line) < 26) { - continue; // bitcoin address length range: [26, 35] + if (last_modify_time == attrib.st_mtime) { + continue; } - if (idx >= size) { - size *= 2; - clients = realloc(clients, size * sizeof(struct btc_client)); + + FILE *f = fopen(list->file, "rb"); + if (f == NULL) { + continue; + } + if (flock(fileno(f), LOCK_EX | LOCK_NB) != 0) { + fclose(f); + continue; } - struct btc_client *c = clients + idx; - strcpy(c->address, line); - idx++; - } - fclose(f); - if (idx == 0) { - free(clients); - return 0; - } - assert(idx <= size); - if (idx != size) { - clients = realloc(clients, idx * sizeof(struct btc_client)); + size_t idx = 0; + struct btc_client *clients = calloc(size, sizeof(struct btc_client)); + while (fgets(line, sizeof(line), f)) { + while (strlen(line) > 0 && !isalpha(line[strlen(line) - 1])) { + line[strlen(line) - 1] = '\0'; + } + if (strlen(line) > 35 || strlen(line) < 26) { + continue; // bitcoin address length range: [26, 35] + } + if (idx >= size) { + size *= 2; + clients = realloc(clients, size * sizeof(struct btc_client)); + } + struct btc_client *c = clients + idx; + strcpy(c->address, line); + idx++; + } + fclose(f); + flock(fileno(f), LOCK_UN); + + assert(idx <= size); + if (idx == 0) { + free(clients); + clients = NULL; + } else { + if (idx != size) { + clients = realloc(clients, idx * sizeof(struct btc_client)); + } + qsort(clients, idx, sizeof(struct btc_client), cmp_btc_client); + } + + // update list + pthread_rwlock_wrlock(&list->lock); + if (list->clients != NULL) { + free(list->clients); + } + list->clients = clients; + list->number = idx; + pthread_rwlock_unlock(&list->lock); + + last_modify_time = attrib.st_mtime; } - stat(list->file, &attrib); - qsort(clients, idx, sizeof(struct btc_client), cmp_btc_client); + return NULL; +} - pthread_mutex_lock(&list->lock); - if (list->clients != NULL) { - free(list->clients); +extern int bitcoin_setup_update_thread(struct btc_list *list) { + if (pthread_create(&list->thread, NULL, check_list, list) == 0) { + return 1; } - list->clients = clients; - list->number = idx; - list->last_check_time = time(NULL); - list->last_modify_time = attrib.st_mtime; - pthread_mutex_unlock(&list->lock); + return 0; // error +} + +extern void bitcoin_clean_update_thread(struct btc_list *list) { + pthread_rwlock_wrlock(&list->lock); + list->running = 0; + pthread_rwlock_unlock(&list->lock); - return idx; + pthread_join(list->thread, NULL); } extern int bitcoin_check_address(struct btc_list *list, const char *address) { - struct btc_client key, *res; + struct btc_client key, *res = NULL; memset(&key, 0, sizeof(struct btc_client)); strncpy(key.address, address, 35); - bitcoin_tryload_list(list); + pthread_rwlock_rdlock(&list->lock); + if (list->number > 0) { + res = bsearch(&key, list->clients, list->number, + sizeof(struct btc_client), cmp_btc_client); + } + pthread_rwlock_unlock(&list->lock); - pthread_mutex_lock(&list->lock); - res = bsearch(&key, list->clients, list->number, - sizeof(struct btc_client), cmp_btc_client); - pthread_mutex_unlock(&list->lock); if (res != NULL) { return 1; } diff --git a/src/bitcoin.h b/src/bitcoin.h index 746d99f2..36b786f4 100644 --- a/src/bitcoin.h +++ b/src/bitcoin.h @@ -34,7 +34,7 @@ extern int bitcoin_verify_message(const char *address, const unsigned char *sig_ struct btc_client; struct btc_list; extern struct btc_list *bitcoin_init_list(); -extern size_t bitcoin_tryload_list(struct btc_list *list); extern int bitcoin_check_address(struct btc_list *list, const char *address); - +extern int bitcoin_setup_update_thread(struct btc_list *list); +extern void bitcoin_clean_update_thread(struct btc_list *list); #endif diff --git a/src/server.c b/src/server.c index 48b5117f..81e37225 100644 --- a/src/server.c +++ b/src/server.c @@ -1161,9 +1161,11 @@ int main(int argc, char **argv) LOGI("initialize acl..."); acl = !init_acl(optarg); } else if (strcmp(long_options[option_index].name, "bitcoin-list") == 0) { + LOGI("bitcoin list file: %s", optarg); bitcoin_list = bitcoin_init_list(optarg); - size_t cnt = bitcoin_tryload_list(bitcoin_list); - LOGI("bitcoin address number: %zu", cnt); + if (bitcoin_setup_update_thread(bitcoin_list) == 0) { + FATAL("setup bitcoin check list thread failure"); + } } break; case 's': @@ -1406,6 +1408,10 @@ int main(int argc, char **argv) if (udprelay) { free_udprelay(); } + + if (bitcoin_list) { + bitcoin_clean_update_thread(bitcoin_list); + } resolv_shutdown(loop);