diff --git a/src/dns_cache.c b/src/dns_cache.c index 3e8e80c4ad..9e40e8b416 100644 --- a/src/dns_cache.c +++ b/src/dns_cache.c @@ -19,9 +19,11 @@ #include "dns_cache.h" #include "stringutil.h" #include "tlog.h" +#include "util.h" #include #include #include +#include #include #include @@ -40,6 +42,8 @@ struct dns_cache_head { pthread_mutex_t lock; }; +typedef int (*dns_cache_read_callback)(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data); + static struct dns_cache_head dns_cache_head; int dns_cache_init(int size, int enable_inactive, int inactive_list_expired) @@ -127,7 +131,7 @@ static void _dns_cache_remove(struct dns_cache *dns_cache) static void _dns_cache_move_inactive(struct dns_cache *dns_cache) { - list_del_init(&dns_cache->list); + list_del(&dns_cache->list); list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list); time(&dns_cache->info.replace_time); } @@ -256,6 +260,24 @@ struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len return (struct dns_cache_data *)cache_packet; } +static void _dns_cache_insert_sorted(struct dns_cache *dns_cache, struct list_head *head) +{ + time_t ttl; + struct dns_cache *tmp = NULL; + + /* ascending order */ + ttl = dns_cache->info.insert_time + dns_cache->info.ttl; + list_for_each_entry_reverse(tmp, head, list) + { + if ((tmp->info.insert_time + tmp->info.ttl) <= ttl) { + list_add(&dns_cache->list, &tmp->list); + return; + } + } + + list_add(&dns_cache->list, head); +} + static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive, int inactive, struct dns_cache_data *cache_data) { @@ -288,12 +310,12 @@ static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int spee dns_cache->info.is_visited = 1; old_cache_data = dns_cache->cache_data; dns_cache->cache_data = cache_data; - list_del_init(&dns_cache->list); + list_del(&dns_cache->list); if (inactive == 0) { time(&dns_cache->info.insert_time); time(&dns_cache->info.replace_time); - list_add_tail(&dns_cache->list, &dns_cache_head.cache_list); + _dns_cache_insert_sorted(dns_cache, &dns_cache_head.cache_list); } else { time(&dns_cache->info.replace_time); list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list); @@ -354,24 +376,6 @@ static void _dns_cache_remove_by_domain(struct dns_cache_key *cache_key) pthread_mutex_unlock(&dns_cache_head.lock); } -static void _dns_cache_insert_sorted(struct dns_cache *dns_cache, struct list_head *head) -{ - time_t ttl; - struct dns_cache *tmp = NULL; - - /* ascending order */ - ttl = dns_cache->info.insert_time + dns_cache->info.ttl; - list_for_each_entry_reverse(tmp, head, list) - { - if ((tmp->info.insert_time + tmp->info.ttl) <= ttl) { - list_add(&dns_cache->list, &tmp->list); - return; - } - } - - list_add_tail(&dns_cache->list, head); -} - static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data *cache_data, struct list_head *head) { uint32_t key = 0; @@ -607,8 +611,6 @@ void dns_cache_update(struct dns_cache *dns_cache) { pthread_mutex_lock(&dns_cache_head.lock); if (!list_empty(&dns_cache->list)) { - list_del_init(&dns_cache->list); - list_add_tail(&dns_cache->list, &dns_cache_head.cache_list); dns_cache->info.hitnum += dns_cache->info.hitnum_update_add; if (dns_cache->info.hitnum > DNS_CACHE_MAX_HITNUM) { dns_cache->info.hitnum = DNS_CACHE_MAX_HITNUM; @@ -720,7 +722,7 @@ void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, unsigned } } - if (ttl < 0) { + if (ttl <= 0) { if (dns_cache_head.enable_inactive && dns_cache->info.no_inactive == 0) { _dns_cache_move_inactive(dns_cache); } else { @@ -745,15 +747,40 @@ void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, unsigned } } -static int _dns_cache_read_record(int fd, uint32_t cache_number) +static int _dns_cache_read_to_cache(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data) { + struct list_head *head = NULL; + if (cache_record->type == CACHE_RECORD_TYPE_ACTIVE) { + head = &dns_cache_head.cache_list; + } else if (cache_record->type == CACHE_RECORD_TYPE_INACTIVE) { + head = &dns_cache_head.inactive_list; + } else { + tlog(TLOG_ERROR, "read cache record type is invalid."); + goto errout; + } + + if (_dns_cache_insert(&cache_record->info, cache_data, head) != 0) { + tlog(TLOG_ERROR, "insert cache data failed."); + cache_data = NULL; + goto errout; + } + + daemon_keepalive(); + + /* keep cache_data */ + return -2; +errout: + return -1; +} + +static int _dns_cache_read_record(int fd, uint32_t cache_number, dns_cache_read_callback callback) +{ unsigned int i = 0; ssize_t ret = 0; struct dns_cache_record cache_record; struct dns_cache_data_head data_head; struct dns_cache_data *cache_data = NULL; - struct list_head *head = NULL; for (i = 0; i < cache_number; i++) { ret = read(fd, &cache_record, sizeof(cache_record)); @@ -767,15 +794,6 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number) goto errout; } - if (cache_record.type == CACHE_RECORD_TYPE_ACTIVE) { - head = &dns_cache_head.cache_list; - } else if (cache_record.type == CACHE_RECORD_TYPE_INACTIVE) { - head = &dns_cache_head.inactive_list; - } else { - tlog(TLOG_ERROR, "read cache record type is invalid."); - goto errout; - } - ret = read(fd, &data_head, sizeof(data_head)); if (ret != sizeof(data_head)) { tlog(TLOG_ERROR, "read data head failed, %s", strerror(errno)); @@ -814,13 +832,15 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number) goto errout; } - if (_dns_cache_insert(&cache_record.info, cache_data, head) != 0) { - tlog(TLOG_ERROR, "insert cache data failed."); + ret = callback(&cache_record, cache_data); + if (ret == -2) { cache_data = NULL; + } else if (ret != 0) { goto errout; + } else { + free(cache_data); + cache_data = NULL; } - - cache_data = NULL; } return 0; @@ -831,7 +851,7 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number) return -1; } -int dns_cache_load(const char *file) +static int _dns_cache_file_read(const char *file, dns_cache_read_callback callback) { int fd = -1; ssize_t ret = 0; @@ -864,7 +884,7 @@ int dns_cache_load(const char *file) } tlog(TLOG_INFO, "load cache file %s, total %d records", file, cache_file.cache_number); - if (_dns_cache_read_record(fd, cache_file.cache_number) != 0) { + if (_dns_cache_read_record(fd, cache_file.cache_number, callback) != 0) { goto errout; } @@ -878,6 +898,11 @@ int dns_cache_load(const char *file) return -1; } +int dns_cache_load(const char *file) +{ + return _dns_cache_file_read(file, _dns_cache_read_to_cache); +} + static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RECORD_TYPE type, struct list_head *head) { struct dns_cache *dns_cache = NULL; @@ -987,6 +1012,23 @@ int dns_cache_save(const char *file, int check_lock) return -1; } +static int _dns_cache_print(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data) +{ + printf("domain: %s, qtype: %d, ttl: %d, speed: %.1fms\n", cache_record->info.domain, cache_record->info.qtype, + cache_record->info.ttl, (float)cache_record->info.speed / 10); + return 0; +} + +int dns_cache_print(const char *file) +{ + if (access(file, F_OK) != 0) { + tlog(TLOG_ERROR, "cache file %s not exist.", file); + return -1; + } + + return _dns_cache_file_read(file, _dns_cache_print); +} + void dns_cache_destroy(void) { struct dns_cache *dns_cache = NULL; diff --git a/src/dns_cache.h b/src/dns_cache.h index eea63c558b..1eb67ab673 100644 --- a/src/dns_cache.h +++ b/src/dns_cache.h @@ -188,6 +188,8 @@ int dns_cache_load(const char *file); int dns_cache_save(const char *file, int check_lock); +int dns_cache_print(const char *file); + const char *dns_cache_file_version(void); #ifdef __cplusplus diff --git a/src/smartdns.c b/src/smartdns.c index 96733bff08..c209a5dd32 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -20,6 +20,7 @@ #include "smartdns.h" #include "art.h" #include "atomic.h" +#include "dns_cache.h" #include "dns_client.h" #include "dns_conf.h" #include "dns_server.h" @@ -31,6 +32,7 @@ #include "util.h" #include #include +#include #include #include #include @@ -756,14 +758,15 @@ int main(int argc, char *argv[]) #endif { int ret = 0; - int is_foreground = 0; + int is_run_as_daemon = 1; int opt = 0; char config_file[MAX_LINE_LEN]; char pid_file[MAX_LINE_LEN]; int signal_ignore = 0; sigset_t empty_sigblock; struct stat sb; - int daemon_ret = 0; + + static struct option long_options[] = {{"cache-print", required_argument, 0, 256}}; safe_strncpy(config_file, SMARTDNS_CONF_FILE, MAX_LINE_LEN); @@ -778,10 +781,10 @@ int main(int argc, char *argv[]) sigprocmask(SIG_SETMASK, &empty_sigblock, NULL); smartdns_close_allfds(); - while ((opt = getopt(argc, argv, "fhc:p:SvxN:")) != -1) { + while ((opt = getopt_long(argc, argv, "fhc:p:SvxN:", long_options, 0)) != -1) { switch (opt) { case 'f': - is_foreground = 1; + is_run_as_daemon = 0; break; case 'c': if (full_path(config_file, sizeof(config_file), optarg) != 0) { @@ -810,6 +813,8 @@ int main(int argc, char *argv[]) case 'h': _help(); return 1; + case 256: + return dns_cache_print(optarg); } } @@ -819,20 +824,21 @@ int main(int argc, char *argv[]) goto errout; } - if (is_foreground == 0 && dns_no_daemon == 0) { - daemon_ret = run_daemon(); - if (daemon_ret < 0) { + if (dns_no_daemon) { + is_run_as_daemon = 0; + } + + if (is_run_as_daemon) { + int daemon_ret = daemon_run(); + if (daemon_ret != -2) { char buff[4096]; char *log_path = realpath(_smartdns_log_path(), buff); - if (log_path != NULL && access(log_path, F_OK) == 0 && daemon_ret != -2) { + if (log_path != NULL && access(log_path, F_OK) == 0 && daemon_ret != -3 && daemon_ret != 0) { fprintf(stderr, "run daemon failed, please check log at %s\n", log_path); } - return 1; - } - if (daemon_ret == 0) { - return 0; + return daemon_ret; } } @@ -841,7 +847,7 @@ int main(int argc, char *argv[]) } if (strncmp(pid_file, "-", 2) != 0 && dns_no_pidfile == 0 && create_pid_file(pid_file) != 0) { - ret = -2; + ret = -3; goto errout; } @@ -863,8 +869,8 @@ int main(int argc, char *argv[]) goto errout; } - if (daemon_ret > 0) { - ret = daemon_kickoff(daemon_ret, 0, dns_conf_log_console | verbose_screen); + if (is_run_as_daemon) { + ret = daemon_kickoff(0, dns_conf_log_console | verbose_screen); if (ret != 0) { goto errout; } @@ -875,8 +881,8 @@ int main(int argc, char *argv[]) _smartdns_exit(); return ret; errout: - if (daemon_ret > 0) { - daemon_kickoff(daemon_ret, ret, dns_conf_log_console | verbose_screen); + if (is_run_as_daemon) { + daemon_kickoff(ret, dns_conf_log_console | verbose_screen); } smartdns_test_notify(2); return 1; diff --git a/src/util.c b/src/util.c index ade576a9fc..793394332b 100644 --- a/src/util.c +++ b/src/util.c @@ -103,8 +103,20 @@ struct ipset_netlink_msg { __be16 res_id; }; +enum daemon_msg_type { + DAEMON_MSG_KICKOFF, + DAEMON_MSG_KEEPALIVE, + DAEMON_MSG_DAEMON_PID, +}; + +struct daemon_msg { + enum daemon_msg_type type; + int value; +}; + static int ipset_fd; static int pidfile_fd; +static int daemon_fd; unsigned long get_tick_count(void) { @@ -1609,14 +1621,20 @@ void close_all_fd(int keepfd) return; } -int daemon_kickoff(int fd, int status, int no_close) +int daemon_kickoff(int status, int no_close) { - if (fd <= 0) { + struct daemon_msg msg; + + if (daemon_fd <= 0) { return -1; } - int ret = write(fd, &status, sizeof(status)); - if (ret != sizeof(status)) { + msg.type = DAEMON_MSG_KICKOFF; + msg.value = status; + + int ret = write(daemon_fd, &msg, sizeof(msg)); + if (ret != sizeof(msg)) { + fprintf(stderr, "notify parent process failed, %s\n", strerror(errno)); return -1; } @@ -1636,12 +1654,40 @@ int daemon_kickoff(int fd, int status, int no_close) } } - close(fd); + close(daemon_fd); + daemon_fd = -1; return 0; } -int run_daemon() +int daemon_keepalive(void) +{ + struct daemon_msg msg; + static time_t last = 0; + time_t now = time(NULL); + + if (daemon_fd <= 0) { + return -1; + } + + if (now == last) { + return 0; + } + + last = now; + + msg.type = DAEMON_MSG_KEEPALIVE; + msg.value = 0; + + int ret = write(daemon_fd, &msg, sizeof(msg)); + if (ret != sizeof(msg)) { + return -1; + } + + return 0; +} + +int daemon_run(void) { pid_t pid = 0; int fds[2] = {0}; @@ -1660,7 +1706,6 @@ int run_daemon() } else if (pid > 0) { struct pollfd pfd; int ret = 0; - int status = 0; close(fds[1]); @@ -1668,22 +1713,35 @@ int run_daemon() pfd.events = POLLIN; pfd.revents = 0; - ret = poll(&pfd, 1, 1000); - if (ret <= 0) { - fprintf(stderr, "run daemon process failed, wait child timeout\n"); - goto errout; - } + do { + ret = poll(&pfd, 1, 3000); + if (ret <= 0) { + fprintf(stderr, "run daemon process failed, wait child timeout, kill child.\n"); + goto errout; + } - if (!(pfd.revents & POLLIN)) { - goto errout; - } + if (!(pfd.revents & POLLIN)) { + goto errout; + } - ret = read(fds[0], &status, sizeof(status)); - if (ret != sizeof(status)) { - goto errout; - } + struct daemon_msg msg; + + ret = read(fds[0], &msg, sizeof(msg)); + if (ret != sizeof(msg)) { + goto errout; + } - return status; + if (msg.type == DAEMON_MSG_KEEPALIVE) { + continue; + } else if (msg.type == DAEMON_MSG_DAEMON_PID) { + pid = msg.value; + continue; + } else if (msg.type == DAEMON_MSG_KICKOFF) { + return msg.value; + } else { + goto errout; + } + } while (true); } setsid(); @@ -1693,6 +1751,11 @@ int run_daemon() fprintf(stderr, "double fork failed, %s\n", strerror(errno)); _exit(1); } else if (pid > 0) { + struct daemon_msg msg; + int unused __attribute__((unused)); + msg.type = DAEMON_MSG_DAEMON_PID; + msg.value = pid; + unused = write(fds[1], &msg, sizeof(msg)); _exit(0); } @@ -1701,8 +1764,9 @@ int run_daemon() goto errout; } close(fds[0]); - return fds[1]; + daemon_fd = fds[1]; + return -2; errout: kill(pid, SIGKILL); return -1; diff --git a/src/util.h b/src/util.h index 417285d0c2..e7753433b1 100644 --- a/src/util.h +++ b/src/util.h @@ -147,9 +147,11 @@ void print_stack(void); void close_all_fd(int keepfd); -int run_daemon(void); +int daemon_run(void); -int daemon_kickoff(int fd, int status, int no_close); +int daemon_kickoff(int status, int no_close); + +int daemon_keepalive(void); int write_file(const char *filename, void *data, int data_len);