Browse Source

dns_cache: Support periodically saving cache files

Nick Peng 2 years ago
parent
commit
d3a6d46a44
7 changed files with 96 additions and 6 deletions
  1. 4 0
      etc/smartdns/smartdns.conf
  2. 9 1
      src/dns_cache.c
  3. 1 1
      src/dns_cache.h
  4. 2 0
      src/dns_conf.c
  5. 1 0
      src/dns_conf.h
  6. 78 3
      src/dns_server.c
  7. 1 1
      src/util.c

+ 4 - 0
etc/smartdns/smartdns.conf

@@ -66,6 +66,10 @@ bind [::]:53
 # cache persist file
 # cache-file /tmp/smartdns.cache
 
+# cache persist time
+# cache-checkpoint-time [second]
+# cache-checkpoint-time 86400
+
 # prefetch domain
 # prefetch-domain [yes|no]
 # prefetch-domain yes

+ 9 - 1
src/dns_cache.c

@@ -886,12 +886,20 @@ static int _dns_cache_write_records(int fd, uint32_t *cache_number)
 	return 0;
 }
 
-int dns_cache_save(const char *file)
+int dns_cache_save(const char *file, int check_lock)
 {
 	int fd = -1;
 	uint32_t cache_number = 0;
 	tlog(TLOG_DEBUG, "write cache file %s", file);
 
+	/* check lock */
+	if (check_lock == 1) {
+		if (pthread_mutex_trylock(&dns_cache_head.lock) != 0) {
+			return -1;
+		}
+		pthread_mutex_unlock(&dns_cache_head.lock);
+	}
+
 	fd = open(file, O_TRUNC | O_CREAT | O_WRONLY, 0640);
 	if (fd < 0) {
 		tlog(TLOG_ERROR, "create file %s failed, %s", file, strerror(errno));

+ 1 - 1
src/dns_cache.h

@@ -186,7 +186,7 @@ void dns_cache_destroy(void);
 
 int dns_cache_load(const char *file);
 
-int dns_cache_save(const char *file);
+int dns_cache_save(const char *file, int check_lock);
 
 const char *dns_cache_file_version(void);
 

+ 2 - 0
src/dns_conf.c

@@ -121,6 +121,7 @@ char dns_conf_ca_path[DNS_MAX_PATH];
 
 char dns_conf_cache_file[DNS_MAX_PATH];
 int dns_conf_cache_persist = 2;
+int dns_conf_cache_checkpoint_time;
 
 /* auditing */
 int dns_conf_audit_enable = 0;
@@ -3253,6 +3254,7 @@ static struct config_item _config_item[] = {
 	CONF_SSIZE("cache-size", &dns_conf_cachesize, 0, CONF_INT_MAX),
 	CONF_CUSTOM("cache-file", _config_option_parser_filepath, (char *)&dns_conf_cache_file),
 	CONF_YESNO("cache-persist", &dns_conf_cache_persist),
+	CONF_INT("cache-checkpoint-time", &dns_conf_cache_checkpoint_time, 0, 3600 * 24 * 7),
 	CONF_YESNO("prefetch-domain", &dns_conf_prefetch),
 	CONF_YESNO("serve-expired", &dns_conf_serve_expired),
 	CONF_INT("serve-expired-ttl", &dns_conf_serve_expired_ttl, 0, CONF_INT_MAX),

+ 1 - 0
src/dns_conf.h

@@ -459,6 +459,7 @@ extern char dns_conf_ca_path[DNS_MAX_PATH];
 
 extern char dns_conf_cache_file[DNS_MAX_PATH];
 extern int dns_conf_cache_persist;
+extern int dns_conf_cache_checkpoint_time;
 
 extern struct dns_domain_check_orders dns_conf_check_orders;
 

+ 78 - 3
src/dns_server.c

@@ -44,6 +44,7 @@
 #include <openssl/ssl.h>
 #include <openssl/x509.h>
 #include <pthread.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -52,6 +53,7 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 
 #define DNS_MAX_EVENTS 256
 #define IPV6_READY_CHECK_TIME 180
@@ -322,6 +324,9 @@ struct dns_server {
 	int event_fd;
 	struct list_head conn_list;
 
+	pid_t cache_save_pid;
+	time_t cache_save_time;
+
 	/* dns request list */
 	pthread_mutex_t request_list_lock;
 	struct list_head request_list;
@@ -350,6 +355,7 @@ static int _dns_server_reply_all_pending_list(struct dns_request *request, struc
 static void *_dns_server_get_dns_rule(struct dns_request *request, enum domain_rule rule);
 static const char *_dns_server_get_request_groupname(struct dns_request *request);
 static int _dns_server_tcp_socket_send(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len);
+static int _dns_server_cache_save(int check_lock);
 
 int dns_is_ipv6_ready(void)
 {
@@ -6252,6 +6258,66 @@ static void _dns_server_check_need_exit(void)
 #define _dns_server_check_need_exit()
 #endif
 
+static void _dns_server_save_cache_to_file(void)
+{
+	time_t now;
+	int check_time = dns_conf_cache_checkpoint_time;
+
+	if (dns_conf_cache_persist == 0 || dns_conf_cachesize <= 0 || dns_conf_cache_checkpoint_time <= 0) {
+		return;
+	}
+
+	time(&now);
+	if (server.cache_save_pid > 0) {
+		int ret = waitpid(server.cache_save_pid, NULL, WNOHANG);
+		if (ret == server.cache_save_pid) {
+			server.cache_save_pid = 0;
+		} else if (ret < 0) {
+			tlog(TLOG_ERROR, "waitpid failed, errno %d, error info '%s'", errno, strerror(errno));
+			server.cache_save_pid = 0;
+		} else {
+			if (now - 30 > server.cache_save_time) {
+				kill(server.cache_save_pid, SIGKILL);
+			}
+			return;
+		}
+	}
+
+	if (check_time < 120) {
+		check_time = 120;
+	}
+
+	if (now - check_time < server.cache_save_time) {
+		return;
+	}
+
+	/* server is busy, skip*/
+	pthread_mutex_lock(&server.request_list_lock);
+	if (list_empty(&server.request_list) != 0) {
+		pthread_mutex_unlock(&server.request_list_lock);
+		return;
+	}
+	pthread_mutex_unlock(&server.request_list_lock);
+
+	server.cache_save_time = now;
+
+	int pid = fork();
+	if (pid == 0) {
+		/* child process */
+		for (int i = 3; i < 1024; i++) {
+			close(i);
+		}
+
+		_dns_server_cache_save(1);
+		_exit(0);
+	} else if (pid < 0) {
+		tlog(TLOG_DEBUG, "fork failed, errno %d, error info '%s'", errno, strerror(errno));
+		return;
+	}
+
+	server.cache_save_pid = pid;
+}
+
 static void _dns_server_period_run_second(void)
 {
 	static unsigned int sec = 0;
@@ -6305,6 +6371,8 @@ static void _dns_server_period_run_second(void)
 			tlog(TLOG_INFO, "Update host file data");
 		}
 	}
+
+	_dns_server_save_cache_to_file();
 }
 
 static void _dns_server_period_run(unsigned int msec)
@@ -6902,7 +6970,7 @@ static int _dns_server_cache_init(void)
 	return 0;
 }
 
-static int _dns_server_cache_save(void)
+static int _dns_server_cache_save(int check_lock)
 {
 	char *dns_cache_file = SMARTDNS_CACHE_FILE;
 	if (dns_conf_cache_file[0] != 0) {
@@ -6916,7 +6984,7 @@ static int _dns_server_cache_save(void)
 		return 0;
 	}
 
-	if (dns_cache_save(dns_cache_file) != 0) {
+	if (dns_cache_save(dns_cache_file, check_lock) != 0) {
 		tlog(TLOG_WARN, "save cache failed.");
 		return -1;
 	}
@@ -6974,6 +7042,7 @@ int dns_server_init(void)
 	memset(&server, 0, sizeof(server));
 	pthread_attr_init(&attr);
 	INIT_LIST_HEAD(&server.conn_list);
+	time(&server.cache_save_time);
 
 	epollfd = epoll_create1(EPOLL_CLOEXEC);
 	if (epollfd < 0) {
@@ -7034,8 +7103,14 @@ void dns_server_exit(void)
 		close(server.event_fd);
 		server.event_fd = -1;
 	}
+
+	if (server.cache_save_pid > 0) {
+		kill(server.cache_save_pid, SIGKILL);
+		server.cache_save_pid = 0;
+	}
+
 	_dns_server_close_socket();
-	_dns_server_cache_save();
+	_dns_server_cache_save(0);
 	_dns_server_request_remove_all();
 	pthread_mutex_destroy(&server.request_list_lock);
 	dns_cache_destroy();

+ 1 - 1
src/util.c

@@ -789,7 +789,7 @@ int create_pid_file(const char *pid_file)
 	/*  create pid file, and lock this file */
 	fd = open(pid_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
 	if (fd == -1) {
-		fprintf(stderr, "create pid file failed, %s\n", strerror(errno));
+		fprintf(stderr, "create pid file %s failed, %s\n", pid_file, strerror(errno));
 		return -1;
 	}