From 995d5dce95ea21cfe6e29643b22c15e87a4f5187 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Tue, 21 Feb 2023 22:38:55 +0800 Subject: [PATCH] dns-client: fix tcp connect timeout issue. --- src/dns_client.c | 141 +++++++++++++++++++++++++++++++++++++---------- src/dns_server.c | 6 ++ src/tlog.c | 70 +++++++++++++++++++++-- src/tlog.h | 13 +++-- 4 files changed, 191 insertions(+), 39 deletions(-) diff --git a/src/dns_client.c b/src/dns_client.c index 83812af..a12fb22 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -41,13 +41,14 @@ #include #include #include -#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -196,7 +197,6 @@ struct dns_client { /* query list */ struct list_head dns_request_list; - pthread_cond_t run_cond; atomic_t run_period; atomic_t dns_server_num; @@ -208,6 +208,8 @@ struct dns_client { pthread_mutex_t domain_map_lock; DECLARE_HASHTABLE(domain_map, 6); DECLARE_HASHTABLE(group, 4); + + int fd_wakeup; }; /* dns replied server info */ @@ -264,6 +266,8 @@ static pthread_mutex_t pending_server_mutex = PTHREAD_MUTEX_INITIALIZER; static int dns_client_has_bootstrap_dns = 0; static int _dns_client_send_udp(struct dns_server_info *server_info, void *packet, int len); +static void _dns_client_clear_wakeup_event(void); +static void _dns_client_do_wakeup_event(void); static ssize_t _ssl_read(struct dns_server_info *server, void *buff, int num) { @@ -1337,9 +1341,8 @@ static int _dns_client_server_pending(char *server_ip, int port, dns_server_type atomic_set(&client.run_period, 1); pthread_mutex_unlock(&pending_server_mutex); - pthread_mutex_lock(&client.domain_map_lock); - pthread_cond_signal(&client.run_cond); - pthread_mutex_unlock(&client.domain_map_lock); + _dns_client_do_wakeup_event(); + return 0; errout: if (pending) { @@ -1514,7 +1517,7 @@ static void _dns_client_check_tcp(void) } if (server_info->status == DNS_SERVER_STATUS_CONNECTING) { - if (server_info->last_send + DNS_TCP_CONNECT_TIMEOUT < now) { + if (server_info->last_recv + DNS_TCP_CONNECT_TIMEOUT < now) { tlog(TLOG_DEBUG, "server %s connect timeout.", server_info->ip); _dns_client_close_socket(server_info); } @@ -3569,7 +3572,7 @@ int dns_client_query(const char *domain, int qtype, dns_client_callback callback pthread_mutex_lock(&client.domain_map_lock); if (hash_hashed(&query->domain_node)) { if (list_empty(&client.dns_request_list)) { - pthread_cond_signal(&client.run_cond); + _dns_client_do_wakeup_event(); } list_add_tail(&query->dns_request_list, &client.dns_request_list); @@ -3818,15 +3821,12 @@ static void _dns_client_period_run_second(void) _dns_client_add_pending_servers(); } -static void _dns_client_period_run(void) +static void _dns_client_period_run(unsigned int msec) { struct dns_query_struct *query = NULL; struct dns_query_struct *tmp = NULL; - static unsigned int msec = 0; - msec++; LIST_HEAD(check_list); - unsigned long now = get_tick_count(); /* get query which timed out to check list */ @@ -3869,9 +3869,11 @@ static void *_dns_client_work(void *arg) int i = 0; unsigned long now = {0}; unsigned long last = {0}; + unsigned int msec = 0; unsigned int sleep = 100; int sleep_time = 0; unsigned long expect_time = 0; + int unused __attribute__((unused)); sleep_time = sleep; now = get_tick_count() - sleep; @@ -3884,11 +3886,17 @@ static void *_dns_client_work(void *arg) if (sleep_time <= 0) { sleep_time = 0; } + + int cnt = sleep_time / sleep; + msec -= cnt; + expect_time -= cnt * sleep; + sleep_time -= cnt * sleep; } if (now >= expect_time) { + msec++; if (last != now) { - _dns_client_period_run(); + _dns_client_period_run(msec); } sleep_time = sleep - (now - expect_time); @@ -3896,19 +3904,21 @@ static void *_dns_client_work(void *arg) sleep_time = 0; expect_time = now; } + + /* When client is idle, the sleep time is 1000ms, to reduce CPU usage */ + pthread_mutex_lock(&client.domain_map_lock); + if (list_empty(&client.dns_request_list)) { + int cnt = 10 - (msec % 10) - 1; + sleep_time += sleep * cnt; + msec += cnt; + /* sleep to next second */ + expect_time += sleep * cnt; + } + pthread_mutex_unlock(&client.domain_map_lock); expect_time += sleep; } last = now; - pthread_mutex_lock(&client.domain_map_lock); - if (list_empty(&client.dns_request_list) && atomic_read(&client.run_period) == 0) { - pthread_cond_wait(&client.run_cond, &client.domain_map_lock); - expect_time = get_tick_count(); - pthread_mutex_unlock(&client.domain_map_lock); - continue; - } - pthread_mutex_unlock(&client.domain_map_lock); - num = epoll_wait(client.epoll_fd, events, DNS_MAX_EVENTS, sleep_time); if (num < 0) { usleep(100000); @@ -3918,6 +3928,11 @@ static void *_dns_client_work(void *arg) for (i = 0; i < num; i++) { struct epoll_event *event = &events[i]; struct dns_server_info *server_info = (struct dns_server_info *)event->data.ptr; + if (event->data.fd == client.fd_wakeup) { + _dns_client_clear_wakeup_event(); + continue; + } + if (server_info == NULL) { tlog(TLOG_WARN, "server info is invalid."); continue; @@ -3971,10 +3986,71 @@ int dns_client_set_ecs(char *ip, int subnet) return 0; } +static int _dns_client_create_wakeup_event(void) +{ + int fd_wakeup = -1; + + fd_wakeup = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (fd_wakeup < 0) { + tlog(TLOG_ERROR, "create eventfd failed, %s\n", strerror(errno)); + goto errout; + } + + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = EPOLLIN; + event.data.fd = fd_wakeup; + if (epoll_ctl(client.epoll_fd, EPOLL_CTL_ADD, fd_wakeup, &event) < 0) { + tlog(TLOG_ERROR, "add eventfd to epoll failed, %s\n", strerror(errno)); + goto errout; + } + + return fd_wakeup; + +errout: + if (fd_wakeup > 0) { + close(fd_wakeup); + } + + return -1; +} + +static void _dns_client_close_wakeup_event(void) +{ + if (client.fd_wakeup > 0) { + close(client.fd_wakeup); + client.fd_wakeup = -1; + } +} + +static void _dns_client_clear_wakeup_event(void) +{ + uint64_t val = 0; + int unused __attribute__((unused)); + + if (client.fd_wakeup <= 0) { + return; + } + + unused = read(client.fd_wakeup, &val, sizeof(val)); +} + +static void _dns_client_do_wakeup_event(void) +{ + uint64_t val = 1; + int unused __attribute__((unused)); + if (client.fd_wakeup <= 0) { + return; + } + + unused = write(client.fd_wakeup, &val, sizeof(val)); +} + int dns_client_init(void) { pthread_attr_t attr; int epollfd = -1; + int fd_wakeup = -1; int ret = 0; if (client.epoll_fd > 0) { @@ -4002,8 +4078,6 @@ int dns_client_init(void) hash_init(client.group); INIT_LIST_HEAD(&client.dns_request_list); - pthread_cond_init(&client.run_cond, NULL); - if (dns_client_add_group(DNS_SERVER_GROUP_DEFAULT) != 0) { tlog(TLOG_ERROR, "add default server group failed."); goto errout; @@ -4020,6 +4094,14 @@ int dns_client_init(void) goto errout; } + fd_wakeup = _dns_client_create_wakeup_event(); + if (fd_wakeup < 0) { + tlog(TLOG_ERROR, "create wakeup event failed, %s\n", strerror(errno)); + goto errout; + } + + client.fd_wakeup = fd_wakeup; + return 0; errout: if (client.tid) { @@ -4029,13 +4111,16 @@ errout: client.tid = 0; } - if (epollfd) { + if (epollfd > 0) { close(epollfd); } + if (fd_wakeup > 0) { + close(fd_wakeup); + } + pthread_mutex_destroy(&client.server_list_lock); pthread_mutex_destroy(&client.domain_map_lock); - pthread_cond_destroy(&client.run_cond); return -1; } @@ -4045,14 +4130,13 @@ void dns_client_exit(void) if (client.tid) { void *ret = NULL; atomic_set(&client.run, 0); - pthread_mutex_lock(&client.domain_map_lock); - pthread_cond_signal(&client.run_cond); - pthread_mutex_unlock(&client.domain_map_lock); + _dns_client_do_wakeup_event(); pthread_join(client.tid, &ret); client.tid = 0; } /* free all resources */ + _dns_client_close_wakeup_event(); _dns_client_remove_all_pending_servers(); _dns_client_server_remove_all(); _dns_client_query_remove_all(); @@ -4060,7 +4144,6 @@ void dns_client_exit(void) pthread_mutex_destroy(&client.server_list_lock); pthread_mutex_destroy(&client.domain_map_lock); - pthread_cond_destroy(&client.run_cond); if (client.ssl_ctx) { SSL_CTX_free(client.ssl_ctx); client.ssl_ctx = NULL; diff --git a/src/dns_server.c b/src/dns_server.c index fc53e85..e5799b2 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -2948,6 +2948,12 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); } break; default: + if (ttl == 0) { + /* Get TTL */ + char tmpname[DNS_MAX_CNAME_LEN]; + char tmpbuf[DNS_MAX_CNAME_LEN]; + dns_get_CNAME(rrs, tmpname, DNS_MAX_CNAME_LEN, &ttl, tmpbuf, DNS_MAX_CNAME_LEN); + } break; } } diff --git a/src/tlog.c b/src/tlog.c index 929a71e..70f67bc 100644 --- a/src/tlog.c +++ b/src/tlog.c @@ -110,6 +110,7 @@ struct tlog { tlog_log_output_func output_func; struct tlog_log *wait_on_log; int is_wait; + char gzip_cmd[PATH_MAX]; }; struct tlog_segment_log_head { @@ -520,7 +521,7 @@ static int _tlog_vprintf(struct tlog_log *log, vprint_callback print_callback, v return -1; } - if (unlikely(log->logcount <= 0 && log->logscreen == 0) ) { + if (unlikely(log->logcount <= 0 && log->logscreen == 0)) { return 0; } @@ -1018,7 +1019,6 @@ errout: static int _tlog_archive_log_compressed(struct tlog_log *log) { char gzip_file[TLOG_BUFF_LEN]; - char gzip_cmd[PATH_MAX * 2]; char log_file[TLOG_BUFF_LEN]; char pending_file[TLOG_BUFF_LEN]; @@ -1046,12 +1046,11 @@ static int _tlog_archive_log_compressed(struct tlog_log *log) } /* start gzip process to compress log file */ - snprintf(gzip_cmd, sizeof(gzip_cmd), "gzip -1 %s", pending_file); if (log->zip_pid <= 0) { int pid = vfork(); if (pid == 0) { _tlog_close_all_fd(); - execl("/bin/sh", "sh", "-c", gzip_cmd, NULL); + execl(tlog.gzip_cmd, "-1", pending_file, NULL); _exit(1); } else if (pid < 0) { goto errout; @@ -1363,12 +1362,26 @@ static struct tlog_log *_tlog_wait_log_locked(struct tlog_log *last_log) int ret = 0; struct timespec tm; struct tlog_log *log = NULL; + struct tlog_log *next = NULL; + int need_wait_pid = 0; + + for (next = tlog.log; next != NULL; next = next->next) { + if (next->zip_pid > 0) { + need_wait_pid = 1; + break; + } + } clock_gettime(CLOCK_REALTIME, &tm); tm.tv_sec += 2; tlog.is_wait = 1; tlog.wait_on_log = last_log; - ret = pthread_cond_timedwait(&tlog.cond, &tlog.lock, &tm); + if (need_wait_pid != 0) { + ret = pthread_cond_timedwait(&tlog.cond, &tlog.lock, &tm); + } else { + ret = pthread_cond_wait(&tlog.cond, &tlog.lock); + } + tlog.is_wait = 0; tlog.wait_on_log = NULL; errno = ret; @@ -1676,6 +1689,15 @@ int tlog_setlevel(tlog_level level) return 0; } +int tlog_log_enabled(tlog_level level) +{ + if (level >= TLOG_END) { + return 0; + } + + return (tlog_set_level >= level) ? 1 : 0; +} + tlog_level tlog_getlevel(void) { return tlog_set_level; @@ -1686,6 +1708,35 @@ void tlog_set_logfile(const char *logfile) tlog_rename_logfile(tlog.root, logfile); } +static void _tlog_get_gzip_cmd_path(void) +{ + char *copy_path = NULL; + char gzip_cmd_path[PATH_MAX]; + const char *env_path = getenv("PATH"); + char *save_ptr = NULL; + + if (env_path == NULL) { + env_path = "/bin:/usr/bin:/usr/local/bin"; + } + + copy_path = strdup(env_path); + if (copy_path == NULL) { + return; + } + + for (char *tok = strtok_r(copy_path, ":", &save_ptr); tok; tok = strtok_r(NULL, ":", &save_ptr)) { + snprintf(gzip_cmd_path, sizeof(gzip_cmd_path), "%s/gzip", tok); + if (access(gzip_cmd_path, X_OK) != 0) { + continue; + } + + snprintf(tlog.gzip_cmd, sizeof(tlog.gzip_cmd), "%s", gzip_cmd_path); + break; + } + + free(copy_path); +} + tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int buffsize, unsigned int flag) { struct tlog_log *log = NULL; @@ -1726,6 +1777,10 @@ tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int bu log->file_perm = S_IRUSR | S_IWUSR | S_IRGRP; log->archive_perm = S_IRUSR | S_IRGRP; + if (log->nocompress == 0 && tlog.gzip_cmd[0] == '\0') { + log->nocompress = 1; + } + tlog_rename_logfile(log, logfile); if (log->nocompress) { strncpy(log->suffix, TLOG_SUFFIX_LOG, sizeof(log->suffix)); @@ -1859,6 +1914,7 @@ int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize memset(&tlog, 0, sizeof(tlog)); tlog.is_wait = 0; + _tlog_get_gzip_cmd_path(); pthread_attr_init(&attr); pthread_cond_init(&tlog.cond, NULL); pthread_mutex_init(&tlog.lock, NULL); @@ -1871,6 +1927,10 @@ int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize } tlog_reg_output_func(log, _tlog_root_write_log); + if ((flag & TLOG_NOCOMPRESS) == 0 && tlog.gzip_cmd[0] == '\0') { + fprintf(stderr, "can not find gzip command, disable compress.\n"); + } + tlog.root = log; ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL); if (ret != 0) { diff --git a/src/tlog.h b/src/tlog.h index 426c37f..a692de3 100644 --- a/src/tlog.h +++ b/src/tlog.h @@ -79,9 +79,9 @@ level: Current log Levels format: Log formats */ #ifndef BASE_FILE_NAME -#define BASE_FILE_NAME \ - (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 \ - : __FILE__) +#define BASE_FILE_NAME \ + (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 \ + : __FILE__) #endif #define tlog(level, format, ...) tlog_ext(level, BASE_FILE_NAME, __LINE__, __func__, NULL, format, ##__VA_ARGS__) @@ -95,6 +95,9 @@ extern int tlog_write_log(char *buff, int bufflen); /* set log level */ extern int tlog_setlevel(tlog_level level); +/* is log level enabled*/ +extern int tlog_log_enabled(tlog_level level); + /* get log level */ extern tlog_level tlog_getlevel(void); @@ -137,7 +140,7 @@ read _tlog_format for example. typedef int (*tlog_format_func)(char *buff, int maxlen, struct tlog_loginfo *info, void *userptr, const char *format, va_list ap); extern int tlog_reg_format_func(tlog_format_func func); -/* register log output callback +/* register log output callback Note: info is invalid when flag TLOG_SEGMENT is not set. */ typedef int (*tlog_log_output_func)(struct tlog_loginfo *info, const char *buff, int bufflen, void *private_data); @@ -213,7 +216,7 @@ file: log file permission, default is 640 archive: archive file permission, default is 440 */ -extern void tlog_set_permission(struct tlog_log *log, mode_t file, mode_t archive); +extern void tlog_set_permission(struct tlog_log *log, mode_t file, mode_t archive); #ifdef __cplusplus class Tlog {