From 72dd4e6cd3a9dfdd546db4a641a783858f31ff16 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Mon, 21 May 2018 08:09:03 +0800 Subject: [PATCH] update code --- Makefile | 3 +- dns_client.c | 60 ++-- dns_server.c | 12 +- fast_ping.c | 253 ++++++------- fast_ping.h | 8 +- include/atomic.h | 23 ++ smartdns.c | 27 +- tlog.c | 908 +++++++++++++++++++++++++++++++++++++++++++++++ tlog.h | 89 +++++ util.c | 34 ++ util.h | 4 + 11 files changed, 1257 insertions(+), 164 deletions(-) create mode 100644 tlog.c create mode 100644 tlog.h diff --git a/Makefile b/Makefile index 79ae261..d7b9d53 100755 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ BIN=smartdns -OBJS=smartdns.o fast_ping.o lib/bitops.o dns_client.o dns_server.o dns.o util.o +OBJS=smartdns.o fast_ping.o lib/bitops.o dns_client.o dns_server.o dns.o util.o tlog.o CFLAGS=-g -O0 -Wall CFLAGS +=-Iinclude +CFLAGS += -DBASE_FILE_NAME=\"$(notdir $<)\" CXXFLAGS=-g -O0 -Wall -std=c++11 CXXFLAGS +=-Iinclude diff --git a/dns_client.c b/dns_client.c index a49bafc..2be9c8b 100644 --- a/dns_client.c +++ b/dns_client.c @@ -23,6 +23,7 @@ #include "hashtable.h" #include "list.h" #include "util.h" +#include "tlog.h" #include #include #include @@ -73,6 +74,7 @@ struct dns_client { struct dns_server_info { struct list_head list; + struct ping_host_struct *ping_host; dns_server_type_t type; unsigned short ss_family; socklen_t addr_len; @@ -85,6 +87,7 @@ struct dns_server_info { struct dns_query_struct { atomic_t refcnt; + unsigned short sid; struct list_head dns_request_list; struct hlist_node domain_node; char domain[DNS_MAX_CNAME_LEN]; @@ -96,6 +99,7 @@ struct dns_query_struct { }; static struct dns_client client; +static atomic_t dns_client_sid = ATOMIC_INIT(0); static struct addrinfo *_dns_client_getaddr(const char *host, char *port, int type, int protocol) { @@ -163,13 +167,14 @@ int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_typ goto errout; } memcpy(&server_info->addr, gai->ai_addr, gai->ai_addrlen); + + if (fast_ping_start(server_ip, 0, 60000, NULL, server_info) == NULL) { + goto errout; + } + pthread_mutex_lock(&client.server_list_lock); list_add(&server_info->list, &client.dns_server_list); pthread_mutex_unlock(&client.server_list_lock); - - if (fast_ping_start(server_ip, 0, 60000, NULL, server_info) != 0) { - goto errout; - } return 0; errout: if (server_info) { @@ -194,7 +199,7 @@ int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_ } list_del(&server_info->list); pthread_mutex_unlock(&client.server_list_lock); - if (fast_ping_stop(server_ip) != 0) { + if (fast_ping_stop(server_info->ping_host) != 0) { printf("stop ping failed.\n"); } free(server_info); @@ -279,7 +284,10 @@ void _dns_client_query_release(struct dns_query_struct *query) list_del(&query->dns_request_list); hash_del(&query->domain_node); pthread_mutex_unlock(&client.domain_map_lock); + tlog(TLOG_ERROR, "-------------%p, %d\n", query, atomic_read(&query->refcnt)); _dns_client_query_complete(query); + //TODO double free BUG:// + memset(query, 0, sizeof(*query)); free(query); } @@ -288,7 +296,7 @@ void _dns_client_query_get(struct dns_query_struct *query) atomic_inc(&query->refcnt); } -void dns_client_ping_result(const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr) +void dns_client_ping_result(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr) { struct dns_query_struct *query = userptr; @@ -344,13 +352,16 @@ void _dns_client_period_run() return; } -static struct dns_query_struct *_dns_client_get_request(char *domain) +static struct dns_query_struct *_dns_client_get_request(unsigned short sid, char *domain) { struct dns_query_struct *query = NULL; struct hlist_node *tmp = NULL; + unsigned int key; + key = hash_string(domain); + key = jhash(&sid, sizeof(sid), key); pthread_mutex_lock(&client.domain_map_lock); - hash_for_each_possible_safe(client.domain_map, query, tmp, domain_node, hash_string(domain)) + hash_for_each_possible_safe(client.domain_map, query, tmp, domain_node, key) { if (strncmp(query->domain, domain, DNS_MAX_CNAME_LEN) != 0) { continue; @@ -374,7 +385,7 @@ static int _dns_client_process_answer(char *domain, struct dns_packet *packet) int ret = -1; int A_num = 0; - query = _dns_client_get_request(domain); + query = _dns_client_get_request(packet->head.id, domain); if (query == NULL) { return -1; } @@ -389,7 +400,7 @@ static int _dns_client_process_answer(char *domain, struct dns_packet *packet) printf("%s %d : %d.%d.%d.%d\n", name, ttl, addr[0], addr[1], addr[2], addr[3]); sprintf(name, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); _dns_client_query_get(query); - if (fast_ping_start(name, 1, 900, dns_client_ping_result, query) != 0) { + if (fast_ping_start(name, 1, 700, dns_client_ping_result, query) == NULL) { _dns_client_query_release(query); } A_num++; @@ -400,7 +411,7 @@ static int _dns_client_process_answer(char *domain, struct dns_packet *packet) sprintf(name, "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]); _dns_client_query_get(query); - if (fast_ping_start(name, 1, 900, dns_client_ping_result, query) != 0) { + if (fast_ping_start(name, 1, 700, dns_client_ping_result, query) == NULL) { _dns_client_query_release(query); } } break; @@ -451,8 +462,8 @@ static int _dns_client_recv(unsigned char *inpacket, int inpacket_len, struct so return -1; } - printf("qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d\n", packet->head.qdcount, packet->head.ancount, packet->head.nscount, - packet->head.nrcount, inpacket_len); + printf("qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d, id = %d\n", packet->head.qdcount, packet->head.ancount, packet->head.nscount, + packet->head.nrcount, inpacket_len, packet->head.id); rrs = dns_get_rrs_start(packet, DNS_RRS_QD, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { @@ -574,7 +585,7 @@ static int _dns_client_send_query(struct dns_query_struct *query, char *doamin) head.qr = DNS_OP_QUERY; head.rd = 0; head.ra = 0; - head.id = 1; + head.id = query->sid; dns_packet_init(packet, DNS_PACKSIZE, &head); dns_add_domain(packet, doamin, DNS_T_A, DNS_C_IN); @@ -591,6 +602,7 @@ int dns_client_query(char *domain, dns_client_callback callback, void *user_ptr) { struct dns_query_struct *query = NULL; int ret = 0; + unsigned int key = 0; query = malloc(sizeof(*query)); if (query == NULL) { @@ -605,6 +617,7 @@ int dns_client_query(char *domain, dns_client_callback callback, void *user_ptr) query->callback = callback; query->result.ttl_v4 = -1; query->result.ttl_v6 = -1; + query->sid = atomic_inc_return(&dns_client_sid); _dns_client_query_get(query); ret = _dns_client_send_query(query, domain); @@ -612,9 +625,11 @@ int dns_client_query(char *domain, dns_client_callback callback, void *user_ptr) goto errout_del_list; } + key = hash_string(domain); + key = jhash(&query->sid, sizeof(query->sid), key); pthread_mutex_lock(&client.domain_map_lock); list_add_tail(&query->dns_request_list, &client.dns_request_list); - hash_add(client.domain_map, &query->domain_node, hash_string(domain)); + hash_add(client.domain_map, &query->domain_node, key); pthread_mutex_unlock(&client.domain_map_lock); return 0; @@ -730,14 +745,6 @@ int dns_client_init() goto errout; } - client.run = 1; - client.udp = fd; - ret = pthread_create(&client.tid, &attr, _dns_client_work, NULL); - if (ret != 0) { - fprintf(stderr, "create client work thread failed, %s\n", strerror(errno)); - goto errout; - } - pthread_mutex_init(&client.server_list_lock, 0); INIT_LIST_HEAD(&client.dns_server_list); @@ -747,6 +754,13 @@ int dns_client_init() INIT_LIST_HEAD(&client.dns_request_list); client.epoll_fd = epollfd; + client.run = 1; + client.udp = fd; + ret = pthread_create(&client.tid, &attr, _dns_client_work, NULL); + if (ret != 0) { + fprintf(stderr, "create client work thread failed, %s\n", strerror(errno)); + goto errout; + } if (dns_client_start()) { fprintf(stderr, "start client failed.\n"); diff --git a/dns_server.c b/dns_server.c index cca2a5b..422bd23 100644 --- a/dns_server.c +++ b/dns_server.c @@ -20,6 +20,7 @@ #include "dns.h" #include "util.h" #include "atomic.h" +#include "tlog.h" #include "hashtable.h" #include "list.h" #include "dns_client.h" @@ -157,13 +158,13 @@ static int _dns_add_rrs(struct dns_packet *packet, struct dns_request *request) } } - ret = dns_add_PTR(packet, DNS_RRS_AN, request->domain, 60 * 60, hostname); + ret = dns_add_PTR(packet, DNS_RRS_AN, request->domain, 30, hostname); } break; case DNS_T_A: - ret = dns_add_A(packet, DNS_RRS_AN, request->domain, 60 * 60, request->ipv4_addr); + ret = dns_add_A(packet, DNS_RRS_AN, request->domain, 30, request->ipv4_addr); break; case DNS_T_AAAA: - ret = dns_add_AAAA(packet, DNS_RRS_AN, request->domain, 60 * 60, request->ipv6_addr); + ret = dns_add_AAAA(packet, DNS_RRS_AN, request->domain, 30, request->ipv6_addr); break; default: break; @@ -224,7 +225,6 @@ static int dns_server_resolve_callback(char *domain, struct dns_result *result, return -1; } - memcpy(request->ipv4_addr, result->addr_ipv4, 4); //memcpy(request->ipv6_addr, result->addr_ipv6, 16); request->qtype = DNS_T_A; @@ -241,7 +241,7 @@ static int dns_server_resolve_callback(char *domain, struct dns_result *result, } - printf("free query server %p\n", request); + tlog(TLOG_ERROR, "free query server %p\n", request); memset(request, 0, sizeof(*request)); free(request); @@ -308,7 +308,7 @@ static int _dns_server_recv(unsigned char *inpacket, int inpacket_len, struct so break; } - printf("query server %p\n", request); + tlog(TLOG_ERROR, "query server %p\n", request); atomic_set(&request->refcnt, 1); dns_client_query(request->domain, dns_server_resolve_callback, request); diff --git a/fast_ping.c b/fast_ping.c index 8a53dbe..e813bec 100755 --- a/fast_ping.c +++ b/fast_ping.c @@ -19,6 +19,8 @@ #include "fast_ping.h" #include "atomic.h" #include "hashtable.h" +#include "tlog.h" +#include "util.h" #include #include #include @@ -43,6 +45,8 @@ struct fast_ping_packet_msg { struct timeval tv; + unsigned int sid; + unsigned int seq; }; struct fast_ping_packet { @@ -57,7 +61,7 @@ struct ping_host_struct { atomic_t ref; struct hlist_node host_node; struct hlist_node addr_node; - int type; + FAST_PING_TYPE type; void *userptr; fast_ping_result ping_callback; @@ -70,7 +74,12 @@ struct ping_host_struct { int timeout; int count; int send; - struct sockaddr addr; + unsigned int sid; + union { + struct sockaddr addr; + struct sockaddr_in6 in6; + struct sockaddr_in in; + }; socklen_t addr_len; struct fast_ping_packet packet; }; @@ -93,6 +102,7 @@ struct fast_ping_struct { }; static struct fast_ping_struct ping; +static atomic_t ping_sid = ATOMIC_INIT(0); uint16_t _fast_ping_checksum(uint16_t *header, size_t len) { @@ -270,6 +280,8 @@ static int _fast_ping_sendping_v6(struct ping_host_struct *ping_host) icmp6->icmp6_seq = htons(ping_host->seq); gettimeofday(&packet->msg.tv, 0); + packet->msg.sid = ping_host->sid; + packet->msg.seq = ping_host->seq; icmp6->icmp6_cksum = _fast_ping_checksum((void *)packet, sizeof(struct fast_ping_packet)); len = sendto(ping_host->fd, &ping_host->packet, sizeof(struct fast_ping_packet), 0, (struct sockaddr *)&ping_host->addr, ping_host->addr_len); @@ -299,6 +311,8 @@ static int _fast_ping_sendping_v4(struct ping_host_struct *ping_host) icmp->icmp_seq = htons(ping_host->seq); gettimeofday(&packet->msg.tv, 0); + packet->msg.sid = ping_host->sid; + packet->msg.seq = ping_host->seq; icmp->icmp_cksum = _fast_ping_checksum((void *)packet, sizeof(struct fast_ping_packet)); len = sendto(ping_host->fd, packet, sizeof(struct fast_ping_packet), 0, (struct sockaddr *)&ping_host->addr, ping_host->addr_len); @@ -317,9 +331,9 @@ static int _fast_ping_sendping(struct ping_host_struct *ping_host) { int ret = -1; - if (ping_host->type == AF_INET) { + if (ping_host->type == FAST_PING_ICMP) { ret = _fast_ping_sendping_v4(ping_host); - } else if (ping_host->type == AF_INET6) { + } else if (ping_host->type == FAST_PING_ICMP6) { ret = _fast_ping_sendping_v6(ping_host); } @@ -333,15 +347,15 @@ static int _fast_ping_sendping(struct ping_host_struct *ping_host) return 0; } -static int _fast_ping_create_sock(int protocol) +static int _fast_ping_create_sock(FAST_PING_TYPE type) { int fd = -1; struct ping_host_struct *icmp_host = NULL; struct epoll_event event; - switch (protocol) { - case IPPROTO_ICMP: - fd = socket(AF_INET, SOCK_RAW, protocol); + switch (type) { + case FAST_PING_ICMP: + fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (fd < 0) { fprintf(stderr, "create icmp socket failed.\n"); goto errout; @@ -349,8 +363,8 @@ static int _fast_ping_create_sock(int protocol) _fast_ping_install_filter_v4(fd); icmp_host = &ping.icmp_host; break; - case IPPROTO_ICMPV6: - fd = socket(AF_INET6, SOCK_RAW, protocol); + case FAST_PING_ICMP6: + fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (fd < 0) { fprintf(stderr, "create icmp socket failed.\n"); goto errout; @@ -358,6 +372,8 @@ static int _fast_ping_create_sock(int protocol) _fast_ping_install_filter_v6(fd); icmp_host = &ping.icmp6_host; break; + default: + return -1; } event.events = EPOLLIN; @@ -367,7 +383,7 @@ static int _fast_ping_create_sock(int protocol) } icmp_host->fd = fd; - icmp_host->type = AF_PACKET; + icmp_host->type = type; return fd; errout: @@ -375,17 +391,17 @@ errout: return -1; } -static int _fast_ping_create_icmp(int protocol) +static int _fast_ping_create_icmp(FAST_PING_TYPE type) { int fd = 0; int *set_fd = NULL; pthread_mutex_lock(&ping.lock); - switch (protocol) { - case IPPROTO_ICMP: + switch (type) { + case FAST_PING_ICMP: set_fd = &ping.fd_icmp; break; - case IPPROTO_ICMPV6: + case FAST_PING_ICMP6: set_fd = &ping.fd_icmp6; break; default: @@ -397,7 +413,7 @@ static int _fast_ping_create_icmp(int protocol) goto out; } - fd = _fast_ping_create_sock(protocol); + fd = _fast_ping_create_sock(type); if (fd < 0) { goto errout; } @@ -414,7 +430,8 @@ errout: return -1; } -void fast_ping_print_result(const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr) +void fast_ping_print_result(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, + struct timeval *tv, void *userptr) { if (result == PING_RESULT_RESPONSE) { double rtt = tv->tv_sec * 1000.0 + tv->tv_usec / 1000.0; @@ -424,7 +441,7 @@ void fast_ping_print_result(const char *host, FAST_PING_RESULT result, struct so } } -int fast_ping_start(const char *host, int count, int timeout, fast_ping_result ping_callback, void *userptr) +struct ping_host_struct *fast_ping_start(const char *host, int count, int timeout, fast_ping_result ping_callback, void *userptr) { struct ping_host_struct *ping_host = NULL; struct addrinfo *gai = NULL; @@ -433,25 +450,28 @@ int fast_ping_start(const char *host, int count, int timeout, fast_ping_result p uint32_t hostkey; uint32_t addrkey; int fd = -1; + FAST_PING_TYPE type; domain = _fast_ping_getdomain(host); if (domain < 0) { - return -1; + return NULL; } switch (domain) { case AF_INET: icmp_proto = IPPROTO_ICMP; + type = FAST_PING_ICMP; break; case AF_INET6: icmp_proto = IPPROTO_ICMPV6; + type = FAST_PING_ICMP6; break; default: - return -1; + return NULL; break; } - fd = _fast_ping_create_icmp(icmp_proto); + fd = _fast_ping_create_icmp(type); if (fd < 0) { goto errout; } @@ -469,24 +489,28 @@ int fast_ping_start(const char *host, int count, int timeout, fast_ping_result p int interval = 1000; memset(ping_host, 0, sizeof(*ping_host)); strncpy(ping_host->host, host, PING_MAX_HOSTLEN); - ping_host->type = domain; ping_host->fd = fd; ping_host->timeout = timeout; ping_host->count = count; + ping_host->type = type; ping_host->userptr = userptr; + atomic_set(&ping_host->ref, 0); + ping_host->sid = atomic_inc_return(&ping_sid); if (ping_callback) { ping_host->ping_callback = ping_callback; } else { ping_host->ping_callback = fast_ping_print_result; } ping_host->interval = (timeout > interval) ? timeout : interval; - memcpy(&ping_host->addr, gai->ai_addr, gai->ai_addrlen); ping_host->addr_len = gai->ai_addrlen; - - atomic_set(&ping_host->ref, 0); + if (gai->ai_addrlen > sizeof(struct sockaddr_in6)) { + goto errout; + } + memcpy(&ping_host->addr, gai->ai_addr, gai->ai_addrlen); hostkey = hash_string(ping_host->host); addrkey = jhash(&ping_host->addr, ping_host->addr_len, 0); + addrkey = jhash(&ping_host->sid, sizeof(ping_host->sid), addrkey); pthread_mutex_lock(&ping.map_lock); _fast_ping_host_get(ping_host); hash_add(ping.hostmap, &ping_host->host_node, hostkey); @@ -496,7 +520,7 @@ int fast_ping_start(const char *host, int count, int timeout, fast_ping_result p freeaddrinfo(gai); _fast_ping_sendping(ping_host); - return 0; + return ping_host; errout: if (fd > 0) { close(fd); @@ -510,26 +534,11 @@ errout: free(ping_host); } - return -1; + return NULL; } -int fast_ping_stop(const char *host) +int fast_ping_stop(struct ping_host_struct *ping_host) { - struct ping_host_struct *ping_host; - uint32_t key; - key = hash_string(host); - pthread_mutex_lock(&ping.map_lock); - hash_for_each_possible(ping.hostmap, ping_host, host_node, key) - { - if (strncmp(host, ping_host->host, PING_MAX_HOSTLEN) == 0) { - break; - } - } - if (ping_host == NULL) { - pthread_mutex_unlock(&ping.map_lock); - return -1; - } - pthread_mutex_unlock(&ping.map_lock); _fast_ping_host_put(ping_host); return 0; } @@ -543,46 +552,38 @@ static void tv_sub(struct timeval *out, struct timeval *in) out->tv_sec -= in->tv_sec; } -static int _fast_ping_icmp6_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len, struct timeval *tvrecv) +static struct fast_ping_packet *_fast_ping_icmp6_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len) { int icmp_len; struct fast_ping_packet *packet = (struct fast_ping_packet *)packet_data; struct icmp6_hdr *icmp6 = &packet->icmp6; - struct timeval tvresult = *tvrecv; if (icmp6->icmp6_type != ICMP6_ECHO_REPLY) { - return -1; + return NULL; } icmp_len = data_len; if (icmp_len < 16) { - return -1; + return NULL; } if (icmp6->icmp6_id != ping.ident) { - return -1; + return NULL; } - struct timeval *tvsend = &packet->msg.tv; - tv_sub(&tvresult, tvsend); - - if (ping_host->ping_callback) { - ping_host->ping_callback(ping_host->host, PING_RESULT_RESPONSE, &ping_host->addr, ping_host->addr_len, ping_host->seq, &tvresult, ping_host->userptr); - } - return 0; + return packet; } -static int _fast_ping_icmp_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len, struct timeval *tvrecv) +static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len) { struct ip *ip = (struct ip *)packet_data; struct fast_ping_packet *packet; struct icmp *icmp; - struct timeval tvresult = *tvrecv; int hlen; int icmp_len; if (ip->ip_p != IPPROTO_ICMP) { - return -1; + return NULL; } hlen = ip->ip_hl << 2; @@ -591,84 +592,56 @@ static int _fast_ping_icmp_packet(struct ping_host_struct *ping_host, u_char *pa icmp_len = data_len - hlen; if (icmp_len < 16) { - return -1; + return NULL; } if (icmp->icmp_type != ICMP_ECHOREPLY) { - return -1; + return NULL; } if (icmp->icmp_id != ping.ident) { - return -1; + return NULL; } - struct timeval *tvsend = &packet->msg.tv; - tv_sub(&tvresult, tvsend); - if (ping_host->ping_callback) { - ping_host->ping_callback(ping_host->host, PING_RESULT_RESPONSE, &ping_host->addr, ping_host->addr_len, ping_host->seq, &tvresult, ping_host->userptr); - } - - return 0; + return packet; } -static int _fast_ping_recvping(struct ping_host_struct *ping_host, u_char *inpacket, int len, struct timeval *tvrecv) +struct fast_ping_packet *_fast_ping_recv_packet(struct ping_host_struct *ping_host, u_char *inpacket, int len, struct timeval *tvrecv) { + struct fast_ping_packet *packet = NULL; - if (ping_host->type == AF_INET6) { - if (_fast_ping_icmp6_packet(ping_host, inpacket, len, tvrecv)) { + if (ping_host->type == FAST_PING_ICMP6) { + packet = _fast_ping_icmp6_packet(ping_host, inpacket, len); + if (packet == NULL) { goto errout; } - } else if (ping_host->type == AF_INET) { - if (_fast_ping_icmp_packet(ping_host, inpacket, len, tvrecv)) { + } else if (ping_host->type == FAST_PING_ICMP) { + packet = _fast_ping_icmp_packet(ping_host, inpacket, len); + if (packet == NULL) { goto errout; } - } - - return 0; -errout: - return -1; -} - -#if 0 -static int _fast_ping_gethost_by_addr(char *host, struct sockaddr *addr, socklen_t addr_len) -{ - struct sockaddr_storage *addr_store = (struct sockaddr_storage *)addr; - host[0] = 0; - switch (addr_store->ss_family) { - case AF_INET: { - struct sockaddr_in *addr_in; - addr_in = (struct sockaddr_in *)addr; - inet_ntop(AF_INET, &addr_in->sin_addr, host, addr_len); - } break; - case AF_INET6: { - struct sockaddr_in6 *addr_in6; - addr_in6 = (struct sockaddr_in6 *)addr; - if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { - struct sockaddr_in addr_in4; - memset(&addr_in4, 0, sizeof(addr_in4)); - memcpy(&addr_in4.sin_addr.s_addr, addr_in6->sin6_addr.s6_addr + 12, sizeof(addr_in4.sin_addr.s_addr)); - } else { - inet_ntop(AF_INET6, &addr_in6->sin6_addr, host, addr_len); - } - } break; - default: + } else { goto errout; - break; } - return 0; -errout: - return -1; -} -#endif -static int _fast_ping_process(struct ping_host_struct *ping_host, struct timeval *now) + return packet; +errout: + return NULL; +} + +static int _fast_ping_process_icmp(struct ping_host_struct *ping_host, struct timeval *now) { int len; u_char inpacket[ICMP_INPACKET_SIZE]; struct sockaddr_storage from; struct ping_host_struct *recv_ping_host; + struct fast_ping_packet *packet = NULL; socklen_t from_len = sizeof(from); uint32_t addrkey; + struct timeval tvresult = *now; + struct timeval *tvsend = NULL; + unsigned int sid; + unsigned int seq; len = recvfrom(ping_host->fd, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len); if (len < 0) { @@ -676,11 +649,21 @@ static int _fast_ping_process(struct ping_host_struct *ping_host, struct timeval goto errout; } + packet = _fast_ping_recv_packet(ping_host, inpacket, len, now); + if (packet == NULL) { + tlog(TLOG_ERROR, "recv ping packet failed."); + goto errout; + } + addrkey = jhash(&from, from_len, 0); + tvsend = &packet->msg.tv; + sid = packet->msg.sid; + seq = packet->msg.seq; + addrkey = jhash(&sid, sizeof(sid), addrkey); pthread_mutex_lock(&ping.map_lock); hash_for_each_possible(ping.addrmap, recv_ping_host, addr_node, addrkey) { - if (recv_ping_host->addr_len == from_len && memcmp(&recv_ping_host->addr, &from, from_len) == 0) { + if (recv_ping_host->addr_len == from_len && memcmp(&recv_ping_host->addr, &from, from_len) == 0 && recv_ping_host->sid == sid) { break; } } @@ -690,13 +673,19 @@ static int _fast_ping_process(struct ping_host_struct *ping_host, struct timeval return -1; } + if (recv_ping_host->seq != seq) { + tlog(TLOG_ERROR, "seq num mismatch, expect %u, real %u", recv_ping_host->seq, seq); + return -1; + } + + tv_sub(&tvresult, tvsend); + if (recv_ping_host->ping_callback) { + recv_ping_host->ping_callback(recv_ping_host, recv_ping_host->host, PING_RESULT_RESPONSE, &recv_ping_host->addr, recv_ping_host->addr_len, + recv_ping_host->seq, &tvresult, recv_ping_host->userptr); + } + recv_ping_host->send = 0; - _fast_ping_recvping(recv_ping_host, inpacket, len, now); - - if (recv_ping_host->count >= 0) { - recv_ping_host->count--; - } if (recv_ping_host->count == 0) { _fast_ping_host_put(recv_ping_host); } @@ -705,6 +694,22 @@ errout: return -1; } +static int _fast_ping_process(struct ping_host_struct *ping_host, struct timeval *now) +{ + int ret = -1; + + switch (ping_host->type) { + case FAST_PING_ICMP6: + case FAST_PING_ICMP: + ret = _fast_ping_process_icmp(ping_host, now); + break; + default: + break; + } + + return ret; +} + static void _fast_ping_period_run() { struct ping_host_struct *ping_host; @@ -722,7 +727,8 @@ static void _fast_ping_period_run() tv_sub(&interval, &ping_host->last); millisecond = interval.tv_sec * 1000 + interval.tv_usec / 1000; if (millisecond > ping_host->timeout && ping_host->send == 1) { - ping_host->ping_callback(ping_host->host, PING_RESULT_TIMEOUT, &ping_host->addr, ping_host->addr_len, ping_host->seq, &interval, ping_host->userptr); + ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_TIMEOUT, &ping_host->addr, ping_host->addr_len, ping_host->seq, &interval, + ping_host->userptr); ping_host->send = 0; } @@ -806,19 +812,18 @@ int fast_ping_init() goto errout; } - ping.run = 1; - ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL); - if (ret != 0) { - fprintf(stderr, "create ping work thread failed, %s\n", strerror(errno)); - goto errout; - } - pthread_mutex_init(&ping.map_lock, 0); pthread_mutex_init(&ping.lock, 0); hash_init(ping.hostmap); hash_init(ping.addrmap); ping.epoll_fd = epollfd; ping.ident = getpid(); + ping.run = 1; + ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL); + if (ret != 0) { + fprintf(stderr, "create ping work thread failed, %s\n", strerror(errno)); + goto errout; + } return 0; errout: diff --git a/fast_ping.h b/fast_ping.h index 22dc4ae..471afd7 100755 --- a/fast_ping.h +++ b/fast_ping.h @@ -9,6 +9,7 @@ extern "C" { typedef enum { FAST_PING_ICMP = 1, + FAST_PING_ICMP6 = 2, FAST_PING_TCP, FAST_PING_UDP } FAST_PING_TYPE; @@ -18,11 +19,12 @@ typedef enum { PING_RESULT_TIMEOUT = 2, } FAST_PING_RESULT; -typedef void (*fast_ping_result)(const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr); +struct ping_host_struct; +typedef void (*fast_ping_result)(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr); -int fast_ping_start(const char *host, int count, int timeout, fast_ping_result ping_callback, void *userptr); +struct ping_host_struct *fast_ping_start(const char *host, int count, int timeout, fast_ping_result ping_callback, void *userptr); -int fast_ping_stop(const char *host); +int fast_ping_stop(struct ping_host_struct *ping_host); int fast_ping_init(); diff --git a/include/atomic.h b/include/atomic.h index 2249b39..728c1b0 100755 --- a/include/atomic.h +++ b/include/atomic.h @@ -91,6 +91,29 @@ static inline void atomic_dec( atomic_t *v ) (void)__sync_fetch_and_sub(&v->counter, 1); } +/** + * Increment atomic variable + * @param v pointer of type atomic_t + * + * Atomically increments @v by 1. + */ +static inline int atomic_inc_return( atomic_t *v ) +{ + return __sync_fetch_and_add(&v->counter, 1); +} + +/** + * @brief decrement atomic variable + * @param v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static inline int atomic_dec_return( atomic_t *v ) +{ + return __sync_fetch_and_sub(&v->counter, 1); +} + /** * @brief Decrement and test * @param v pointer of type atomic_t diff --git a/smartdns.c b/smartdns.c index cffcd67..cb2c199 100755 --- a/smartdns.c +++ b/smartdns.c @@ -25,12 +25,17 @@ #include #include - int smartdns_init() { int ret; - ret = fast_ping_init(); + ret = tlog_init(".", "smartdns.log", 1024 * 1024, 8, 1, 0, 0); + if (ret != 0) { + fprintf(stderr, "start tlog failed.\n"); + goto errout; + } + + ret = fast_ping_init(); if (ret != 0) { fprintf(stderr, "start ping failed.\n"); goto errout; @@ -48,9 +53,10 @@ int smartdns_init() goto errout; } - //dns_add_server("192.168.1.1", 53, DNS_SERVER_UDP); + dns_add_server("192.168.1.1", 53, DNS_SERVER_UDP); dns_add_server("114.114.114.114", 53, DNS_SERVER_UDP); dns_add_server("123.207.137.88", 53, DNS_SERVER_UDP); + fast_ping_start("192.168.1.1", 10, 1000, 0, 0); return 0; errout: @@ -65,10 +71,9 @@ int smartdns_run() void smartdns_exit() { fast_ping_exit(); - dns_client_exit(); - dns_server_exit(); + tlog_exit(); } struct data { @@ -193,13 +198,21 @@ int rbtree_test() } #endif +#include + +void sig_handle(int sig) +{ + tlog(TLOG_ERROR, "process exit.\n"); + sleep(1); + _exit(0); +} int main(int argc, char *argv[]) { int ret; atexit(smartdns_exit); - - ret = smartdns_init(); + signal(SIGABRT, sig_handle); + ret = smartdns_init(); if (ret != 0) { fprintf(stderr, "init smartdns failed.\n"); goto errout; diff --git a/tlog.c b/tlog.c new file mode 100644 index 0000000..4a8f328 --- /dev/null +++ b/tlog.c @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2018 Ruilin Peng (Nick) + */ + +#include "tlog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TLOG_BUFF_SIZE (1024 * 128) +#define TLOG_MAX_LINE_LEN (1024) +#define TLOG_TMP_LEN 128 +#define TLOG_LOG_SIZE (1024 * 1024 * 50) +#define TLOG_LOG_COUNT 32 + +struct oldest_log { + char name[TLOG_TMP_LEN]; + time_t mtime; +}; + +struct tlog { + char *buff; + int buffsize; + int start; + int end; + int ext_end; + + int run; + pthread_t tid; + pthread_mutex_t lock; + pthread_cond_t cond; + pthread_cond_t client_cond; + int waiters; + int is_wait; + + int fd; + int fd_lock; + + off_t filesize; + char logdir[PATH_MAX]; + char logname[PATH_MAX]; + int logsize; + int logcount; + int block; + int dropped; + int zip_pid; + int multi_log; + int logscreen; +}; + +typedef int (*list_callback)(const char *name, struct dirent *entry, void *user); + +struct tlog tlog; +static tlog_level tlog_set_level = TLOG_INFO; +tlog_format_func tlog_format; +static unsigned int tlog_localtime_lock = 0; + +static const char *tlog_level_str[] = { + "DEBUG", + "INFO", + "NOTICE", + "WARN", + "ERROR", + "FATAL", +}; + +static inline void _tlog_spin_lock(unsigned int *lock) +{ + while (1) { + int i; + for (i = 0; i < 10000; i++) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return; + } + } + sched_yield(); + } +} + +static inline void _tlog_spin_unlock(unsigned int *lock) +{ + __sync_bool_compare_and_swap(lock, 1, 0); +} + +static int _tlog_mkdir(const char *path) +{ + char path_c[PATH_MAX]; + char *path_end; + char str; + int len; + if (access(path, F_OK) == 0) { + return 0; + } + + strncpy(path_c, path, sizeof(path_c) - 1); + len = strnlen(path_c, sizeof(path_c) - 1); + path_c[len] = '/'; + path_c[len + 1] = '\0'; + path_end = path_c; + + /* create directory recursively */ + while (*path_end != 0) { + if (*path_end != '/') { + path_end++; + continue; + } + + str = *path_end; + *path_end = '\0'; + if (access(path_c, F_OK) == 0) { + *path_end = str; + path_end++; + continue; + } + + if (mkdir(path_c, 0750) != 0) { + fprintf(stderr, "create directory %s failed, %s\n", path_c, strerror(errno)); + return -1; + } + + *path_end = str; + path_end++; + } + + return 0; +} + +static struct tm *_tlog_localtime(time_t *timep, struct tm *tm) +{ + static time_t last_time = {0}; + static struct tm last_tm = {0}; + + /* localtime_r has a global timezone lock, it's about 8 times slower than gmtime + * this code is used to speed up localtime_r call. + */ + _tlog_spin_lock(&tlog_localtime_lock); + if (*timep == last_time) { + *tm = last_tm; + } else { + _tlog_spin_unlock(&tlog_localtime_lock); + tm = localtime_r(timep, tm); + _tlog_spin_lock(&tlog_localtime_lock); + if (tm) { + last_time = *timep; + last_tm = *tm; + } + } + _tlog_spin_unlock(&tlog_localtime_lock); + + return tm; +} + +static int _tlog_getmtime(struct tlog_time *log_mtime, const char *file) +{ + struct tm tm; + struct stat sb; + + if (stat(file, &sb) != 0) { + return -1; + } + + if (_tlog_localtime(&sb.st_mtime, &tm) == NULL) { + return -1; + } + + log_mtime->year = tm.tm_year + 1900; + log_mtime->mon = tm.tm_mon + 1; + log_mtime->mday = tm.tm_mday; + log_mtime->hour = tm.tm_hour; + log_mtime->min = tm.tm_min; + log_mtime->sec = tm.tm_sec; + log_mtime->usec = 0; + + return 0; +} + +static int _tlog_gettime(struct tlog_time *cur_time) +{ + struct tm tm; + struct timeval tmval; + + if (gettimeofday(&tmval, NULL) != 0) { + return -1; + } + + if (_tlog_localtime(&tmval.tv_sec, &tm) == NULL) { + return -1; + } + + cur_time->year = tm.tm_year + 1900; + cur_time->mon = tm.tm_mon + 1; + cur_time->mday = tm.tm_mday; + cur_time->hour = tm.tm_hour; + cur_time->min = tm.tm_min; + cur_time->sec = tm.tm_sec; + cur_time->usec = tmval.tv_usec; + + return 0; +} + +static int _tlog_format(char *buff, int maxlen, struct tlog_info *info, void *userptr, const char *format, va_list ap) +{ + int len = 0; + int total_len = 0; + struct tlog_time *tm = &info->time; + + if (tlog.multi_log) { + /* format prefix */ + len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5d][%4s][%17s:%-4d] ", + tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, getpid(), + info->level, info->file, info->line); + } else { + /* format prefix */ + len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5s][%17s:%-4d] ", + tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, + info->level, info->file, info->line); + } + + if (len < 0 || len == maxlen) { + return -1; + } + buff += len; + total_len += len; + maxlen -= len; + + /* format log message */ + len = vsnprintf(buff, maxlen, format, ap); + if (len < 0 || len == maxlen) { + return -1; + } + buff += len; + total_len += len; + + /* return total length */ + return total_len; +} + +static int _tlog_log_buffer(char *buff, int maxlen, tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap) +{ + int len; + struct tlog_info info; + + if (tlog_format == NULL) { + return -1; + } + + if (level >= TLOG_END) { + return -1; + } + + info.file = file; + info.line = line; + info.func = func; + info.level = tlog_level_str[level]; + + if (_tlog_gettime(&info.time) != 0) { + return -1; + } + + len = tlog_format(buff, maxlen, &info, userptr, format, ap); + if (len < 0) { + return -1; + } + + /* add new line character*/ + if (*(buff + len - 1) != '\n' && len + 1 < maxlen - len) { + *(buff + len) = '\n'; + len++; + } + + return len; +} + +int tlog_vext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap) +{ + int len; + int maxlen = 0; + + if (tlog.buff == NULL) { + return -1; + } + + if (level < tlog_set_level) { + return 0; + } + + pthread_mutex_lock(&tlog.lock); + do { + if (tlog.end == tlog.start) { + if (tlog.ext_end == 0) { + /* if buffer is empty */ + maxlen = tlog.buffsize - tlog.end; + } + } else if (tlog.end > tlog.start) { + maxlen = tlog.buffsize - tlog.end; + } else { + /* if reverse */ + maxlen = tlog.start - tlog.end; + } + + /* if free buffer length is less than min line length */ + if (maxlen < TLOG_MAX_LINE_LEN) { + if (tlog.end != tlog.start) { + pthread_cond_signal(&tlog.cond); + } + + /* if drop message, increase statistics and return */ + if (tlog.block == 0) { + tlog.dropped++; + pthread_mutex_unlock(&tlog.lock); + return -1; + } + tlog.waiters++; + /* block wait for free buffer */ + int ret = pthread_cond_wait(&tlog.client_cond, &tlog.lock); + tlog.waiters--; + if (ret < 0) { + pthread_mutex_unlock(&tlog.lock); + return -1; + } + } + } while (maxlen < TLOG_MAX_LINE_LEN); + + /* write log to buffer */ + len = _tlog_log_buffer(tlog.buff + tlog.end, maxlen, level, file, line, func, userptr, format, ap); + if (len <= 0) { + pthread_mutex_unlock(&tlog.lock); + return -1; + } + tlog.end += len; + /* if remain buffer is not enough for a line, move end to start of buffer. */ + if (tlog.end > tlog.buffsize - TLOG_MAX_LINE_LEN) { + tlog.ext_end = tlog.end; + tlog.end = 0; + } + if (tlog.is_wait) { + pthread_cond_signal(&tlog.cond); + } + pthread_mutex_unlock(&tlog.lock); + return len; +} + +int tlog_ext(tlog_level level, const char *file, int line, const char *func, void *userptr, + const char *format, ...) +{ + int len; + va_list ap; + + va_start(ap, format); + len = tlog_vext(level, file, line, func, userptr, format, ap); + va_end(ap); + + return len; +} + +static int _tlog_rename_logfile(const char *gzip_file) +{ + char archive_file[PATH_MAX]; + struct tlog_time logtime; + int i = 0; + + if (_tlog_getmtime(&logtime, gzip_file) != 0) { + return -1; + } + + snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d.gz", + tlog.logdir, tlog.logname, logtime.year, logtime.mon, logtime.mday, + logtime.hour, logtime.min, logtime.sec); + + while (access(archive_file, F_OK) == 0) { + i++; + snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d-%d.gz", + tlog.logdir, tlog.logname, logtime.year, logtime.mon, logtime.mday, + logtime.hour, logtime.min, logtime.sec, i); + } + + if (rename(gzip_file, archive_file) != 0) { + return -1; + } + + return 0; +} + +static int _tlog_list_dir(const char *path, list_callback callback, void *userptr) +{ + DIR *dir = NULL; + struct dirent *ent; + int ret = 0; + + dir = opendir(path); + if (dir == NULL) { + fprintf(stderr, "open directory failed, %s\n", strerror(errno)); + goto errout; + } + + while ((ent = readdir(dir)) != NULL) { + if (strncmp(".", ent->d_name, 2) == 0 || strncmp("..", ent->d_name, 3) == 0) { + continue; + } + ret = callback(path, ent, userptr); + if (ret != 0) { + goto errout; + } + } + + closedir(dir); + return 0; +errout: + if (dir) { + closedir(dir); + dir = NULL; + } + return -1; +} + +static int _tlog_count_log_callback(const char *path, struct dirent *entry, void *userptr) +{ + int *lognum = (int *)userptr; + + if (strstr(entry->d_name, ".gz") == NULL) { + return 0; + } + + int len = strnlen(tlog.logname, sizeof(tlog.logname)); + if (strncmp(tlog.logname, entry->d_name, len) != 0) { + return 0; + } + + (*lognum)++; + return 0; +} + +static int _tlog_get_oldest_callback(const char *path, struct dirent *entry, void *userptr) +{ + struct stat sb; + char filename[PATH_MAX]; + struct oldest_log *oldestlog = userptr; + + /* if not a gz file, skip */ + if (strstr(entry->d_name, ".gz") == NULL) { + return 0; + } + + /* if not tlog gz file, skip */ + int len = strnlen(tlog.logname, sizeof(tlog.logname)); + if (strncmp(tlog.logname, entry->d_name, len) != 0) { + return 0; + } + + /* get log file mtime */ + snprintf(filename, sizeof(filename), "%s/%s", path, entry->d_name); + if (stat(filename, &sb) != 0) { + return -1; + } + + if (oldestlog->mtime == 0 || oldestlog->mtime > sb.st_mtime) { + oldestlog->mtime = sb.st_mtime; + strncpy(oldestlog->name, entry->d_name, sizeof(oldestlog->name)); + return 0; + } + + return 0; +} + +static int _tlog_remove_oldestlog(void) +{ + struct oldest_log oldestlog; + oldestlog.name[0] = 0; + oldestlog.mtime = 0; + + /* get oldest log file name */ + if (_tlog_list_dir(tlog.logdir, _tlog_get_oldest_callback, &oldestlog) != 0) { + return -1; + } + + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "%s/%s", tlog.logdir, oldestlog.name); + + /* delete */ + unlink(filename); + + return 0; +} + +static int _tlog_remove_oldlog(void) +{ + int lognum = 0; + int i = 0; + + /* get total log file number */ + if (_tlog_list_dir(tlog.logdir, _tlog_count_log_callback, &lognum) != 0) { + fprintf(stderr, "get log file count failed.\n"); + return -1; + } + + /* remove last N log files */ + for (i = 0; i < lognum - tlog.logcount; i++) { + _tlog_remove_oldestlog(); + } + + return 0; +} + +static void _tlog_log_unlock(void) +{ + char lock_file[PATH_MAX]; + if (tlog.fd_lock <= 0) { + return; + } + + snprintf(lock_file, sizeof(lock_file), "%s/%s.lock", tlog.logdir, tlog.logname); + unlink(lock_file); + close(tlog.fd_lock); + tlog.fd_lock = -1; +} + +static int _tlog_log_lock(void) +{ + char lock_file[PATH_MAX]; + int fd; + + if (tlog.multi_log == 0) { + return 0; + } + + snprintf(lock_file, sizeof(lock_file), "%s/%s.lock", tlog.logdir, tlog.logname); + fd = open(lock_file, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd == -1) { + fprintf(stderr, "create pid file failed, %s", strerror(errno)); + return -1; + } + + if (lockf(fd, F_TLOCK, 0) < 0) { + goto errout; + } + + tlog.fd_lock = fd; + return 0; + +errout: + if (fd > 0) { + close(fd); + } + return -1; +} + +static void _tlog_wait_pid(int wait_hang) +{ + int status; + if (tlog.zip_pid <= 0) { + return; + } + + int option = (wait_hang == 0) ? WNOHANG : 0; + /* check and obtain gzip process status*/ + if (waitpid(tlog.zip_pid, &status, option) <= 0) { + return; + } + + /* gzip process exited */ + tlog.zip_pid = -1; + char gzip_file[PATH_MAX]; + + /* rename ziped file */ + snprintf(gzip_file, sizeof(gzip_file), "%s/%s.pending.gz", tlog.logdir, tlog.logname); + if (_tlog_rename_logfile(gzip_file) != 0) { + _tlog_log_unlock(); + return; + } + + /* remove oldes file */ + _tlog_remove_oldlog(); + _tlog_log_unlock(); +} + +static int _tlog_archive_log(void) +{ + char gzip_file[PATH_MAX]; + char gzip_cmd[PATH_MAX]; + char log_file[PATH_MAX]; + char pending_file[PATH_MAX]; + + snprintf(gzip_file, sizeof(gzip_file), "%s/%s.pending.gz", tlog.logdir, tlog.logname); + snprintf(pending_file, sizeof(pending_file), "%s/%s.pending", tlog.logdir, tlog.logname); + + if (_tlog_log_lock() != 0) { + return -1; + } + + /* if pending.zip exists */ + if (access(gzip_file, F_OK) == 0) { + /* rename it to standard name */ + if (_tlog_rename_logfile(gzip_file) != 0) { + goto errout; + } + } + + if (access(pending_file, F_OK) != 0) { + /* rename current log file to pending */ + snprintf(log_file, sizeof(log_file), "%s/%s", tlog.logdir, tlog.logname); + if (rename(log_file, pending_file) != 0) { + goto errout; + } + } + + /* start gzip process to compress log file */ + snprintf(gzip_cmd, sizeof(gzip_cmd), "gzip -1 %s", pending_file); + if (tlog.zip_pid <= 0) { + int pid = vfork(); + if (pid == 0) { + execl("/bin/sh", "sh", "-c", gzip_cmd, NULL); + _exit(1); + } else if (pid < 0) { + goto errout; + } + tlog.zip_pid = pid; + } + + return 0; + +errout: + _tlog_log_unlock(); + return -1; +} + +static int _tlog_write_log(char *buff, int bufflen) +{ + int len; + + /* if log file size exceeds threshold, start to compress */ + if (tlog.multi_log) { + tlog.filesize = lseek(tlog.fd, 0, SEEK_END); + } + + if (tlog.filesize > tlog.logsize && tlog.zip_pid <= 0) { + if (tlog.filesize < lseek(tlog.fd, 0, SEEK_END) && tlog.multi_log == 0) { + const char *msg = "[Auto enable multi-process write mode, log may be lost, please enable multi-process write mode manually]\n"; + tlog.multi_log = 1; + write(tlog.fd, msg, strlen(msg)); + } + close(tlog.fd); + tlog.fd = -1; + tlog.filesize = 0; + _tlog_archive_log(); + } + + if (tlog.fd <= 0) { + /* open a new log file to write */ + char logfile[PATH_MAX]; + if (_tlog_mkdir(tlog.logdir) != 0) { + fprintf(stderr, "create log dir %s failed.\n", tlog.logdir); + return -1; + } + snprintf(logfile, sizeof(logfile), "%s/%s", tlog.logdir, tlog.logname); + tlog.filesize = 0; + tlog.fd = open(logfile, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0640); + if (tlog.fd < 0) { + fprintf(stderr, "open log file %s failed, %s\n", logfile, strerror(errno)); + return -1; + } + + /* get log file size */ + tlog.filesize = lseek(tlog.fd, 0, SEEK_END); + } + + /* output log to screen */ + if (tlog.logscreen) { + write(STDOUT_FILENO, buff, bufflen); + } + + /* write log to file */ + len = write(tlog.fd, buff, bufflen); + if (len > 0) { + tlog.filesize += len; + } + + return len; +} + +static void *_tlog_work(void *arg) +{ + int ret = 0; + int log_len; + int log_extlen; + int log_end; + int log_extend; + int log_dropped; + struct timespec tm; + time_t now = time(0); + time_t last = now; + + while (tlog.run || tlog.end != tlog.start || tlog.ext_end > 0) { + log_len = 0; + log_end = 0; + log_extlen = 0; + log_extend = 0; + + /* if compressing */ + if (tlog.zip_pid > 0) { + now = time(0); + if (now != last) { + /* try to archive compressed file */ + _tlog_wait_pid(0); + last = now; + } + } + + pthread_mutex_lock(&tlog.lock); + if (tlog.end == tlog.start && tlog.ext_end == 0) { + /* if buffer is empty, wait */ + clock_gettime(CLOCK_REALTIME, &tm); + tm.tv_sec += 5; + tlog.is_wait = 1; + ret = pthread_cond_timedwait(&tlog.cond, &tlog.lock, &tm); + tlog.is_wait = 0; + if (ret < 0 || ret == ETIMEDOUT) { + pthread_mutex_unlock(&tlog.lock); + if (ret == ETIMEDOUT) { + continue; + } + sleep(1); + continue; + } + } + + if (tlog.ext_end > 0) { + log_len = tlog.ext_end - tlog.start; + log_extend = tlog.ext_end; + } + if (tlog.end < tlog.start) { + log_extlen = tlog.end; + } else if (tlog.end > tlog.start) { + log_len = tlog.end - tlog.start; + } + log_end = tlog.end; + log_dropped = tlog.dropped; + tlog.dropped = 0; + pthread_mutex_unlock(&tlog.lock); + + /* write log */ + _tlog_write_log(tlog.buff + tlog.start, log_len); + if (log_extlen > 0) { + /* write extend buffer log */ + _tlog_write_log(tlog.buff, log_extlen); + } + + if (log_dropped > 0) { + /* if there is dropped log, record dropped log number */ + char dropmsg[TLOG_TMP_LEN]; + snprintf(dropmsg, sizeof(dropmsg), "[Totoal Dropped %d Messages]\n", log_dropped); + _tlog_write_log(dropmsg, strnlen(dropmsg, sizeof(dropmsg))); + } + + pthread_mutex_lock(&tlog.lock); + /* release finished buffer */ + tlog.start = log_end; + if (log_extend > 0) { + tlog.ext_end = 0; + } + + if (tlog.waiters > 0) { + /* if there are waiters, wakeup */ + pthread_cond_broadcast(&tlog.client_cond); + } + pthread_mutex_unlock(&tlog.lock); + + /* sleep for a while to reduce cpu usage */ + usleep(20 * 1000); + } + return NULL; +} + +void tlog_setlogscreen(int enable) +{ + tlog.logscreen = (enable != 0) ? 1 : 0; +} + +int tlog_reg_format_func(tlog_format_func callback) +{ + tlog_format = callback; + return 0; +} + +int tlog_setlevel(tlog_level level) +{ + if (level >= TLOG_END) { + return -1; + } + + tlog_set_level = level; + return 0; +} + +int tlog_init(const char *logdir, const char *logname, int maxlogsize, int maxlogcount, int block, int buffsize, int multiwrite) +{ + pthread_attr_t attr; + int ret; + + if (tlog_format != NULL) { + fprintf(stderr, "tlog already initilized.\n"); + return -1; + } + + if (buffsize > 0 && buffsize < TLOG_MAX_LINE_LEN * 2) { + fprintf(stderr, "buffer size is invalid.\n"); + return -1; + } + + tlog_format = _tlog_format; + tlog.logscreen = 0; + tlog.buffsize = (buffsize > 0) ? buffsize : TLOG_BUFF_SIZE; + tlog.start = 0; + tlog.end = 0; + tlog.ext_end = 0; + tlog.block = (block != 0) ? 1 : 0; + tlog.waiters = 0; + tlog.dropped = 0; + tlog.logsize = (maxlogsize > 0) ? maxlogsize : TLOG_LOG_SIZE; + tlog.logcount = (maxlogcount > 0) ? maxlogcount : TLOG_LOG_COUNT; + tlog.fd = -1; + tlog.filesize = 0; + tlog.zip_pid = -1; + tlog.logscreen = 0; + tlog.multi_log = (multiwrite != 0) ? 1 : 0; + tlog.is_wait = 0; + + pthread_attr_init(&attr); + pthread_mutex_init(&tlog.lock, 0); + pthread_cond_init(&tlog.cond, 0); + pthread_cond_init(&tlog.client_cond, 0); + tlog.buff = malloc(tlog.buffsize); + if (tlog.buff == NULL) { + fprintf(stderr, "malloc tlog buffer failed, %s\n", strerror(errno)); + goto errout; + } + + strncpy(tlog.logdir, logdir, sizeof(tlog.logdir)); + strncpy(tlog.logname, logname, sizeof(tlog.logname)); + + tlog.run = 1; + ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL); + if (ret != 0) { + fprintf(stderr, "create tlog work thread failed, %s\n", strerror(errno)); + goto errout; + } + + return 0; +errout: + if (tlog.buff) { + free(tlog.buff); + tlog.buff = NULL; + } + if (tlog.tid > 0) { + void *retval = NULL; + tlog.run = 0; + pthread_join(tlog.tid, &retval); + } + + pthread_cond_destroy(&tlog.client_cond); + pthread_mutex_destroy(&tlog.lock); + pthread_cond_destroy(&tlog.cond); + tlog.run = 0; + + return -1; +} + +void tlog_exit(void) +{ + if (tlog.tid > 0) { + void *ret = NULL; + tlog.run = 0; + pthread_mutex_lock(&tlog.lock); + pthread_cond_signal(&tlog.cond); + pthread_mutex_unlock(&tlog.lock); + pthread_join(tlog.tid, &ret); + } + + if (tlog.zip_pid > 0) { + _tlog_wait_pid(1); + } + + if (tlog.buff) { + free(tlog.buff); + tlog.buff = NULL; + } + + if (tlog.fd > 0) { + close(tlog.fd); + tlog.fd = -1; + } + + _tlog_log_unlock(); + + pthread_cond_destroy(&tlog.client_cond); + pthread_mutex_destroy(&tlog.lock); + pthread_cond_destroy(&tlog.cond); +} \ No newline at end of file diff --git a/tlog.h b/tlog.h new file mode 100644 index 0000000..354aad1 --- /dev/null +++ b/tlog.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 Ruilin Peng (Nick) + */ + +#ifndef TLOG_H +#define TLOG_H +#include + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus */ + +typedef enum { + TLOG_DEBUG = 0, + TLOG_INFO = 1, + TLOG_NOTICE = 2, + TLOG_WARN = 3, + TLOG_ERROR = 4, + TLOG_FATAL = 5, + TLOG_END = 6 +} tlog_level; + +struct tlog_time { + int year; + int mon; + int mday; + int hour; + int min; + int sec; + int usec; +}; + +struct tlog_info { + const char *level; + const char *file; + const char *func; + int line; + struct tlog_time time; +}; + +/* +Function:Print log +level: Current log Levels +format: Log formats +*/ +#ifndef BASE_FILE_NAME +#define BASE_FILE_NAME __FILE__ +#endif +#define tlog(level, format, ...) tlog_ext(level, BASE_FILE_NAME, __LINE__, __func__, 0, format, ##__VA_ARGS__) + +extern int tlog_ext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, ...) __attribute__((format(printf, 6, 7))); +extern int tlog_vext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap); + +/* set log level */ +extern int tlog_setlevel(tlog_level level); + +/* enalbe log to screen */ +extern void tlog_setlogscreen(int enable); + +/* +Function:Initialize log module +logdir: Log Output path. +logname: Log name. +maxlogsize: The maximum size of a single log file. +maxlogcount: Number of archived logs. +block: Blocked if buffer is not sufficient. +buffsize: Buffer size, zero for default (128K) +multiwrite: enable multi process write mode. + NOTICE: maxlogsize in all prcesses must be same when enable this mode. + */ +extern int tlog_init(const char *logdir, const char *logname, int maxlogsize, int maxlogcount, int block, int buffsize, int multiwrite); + +extern void tlog_exit(void); + +/* +customize log output format +steps: +1. define format function, function must be defined as tlog_format_func, use snprintf or vsnprintf format log to buffer +2. call tlog_reg_format_func to register format function. + +read _tlog_format for example. +*/ +typedef int (*tlog_format_func)(char *buff, int maxlen, struct tlog_info *info, void *userptr, const char *format, va_list ap); +extern int tlog_reg_format_func(tlog_format_func func); + +#ifdef __cplusplus +} +#endif /*__cplusplus */ +#endif // !TLOG_H diff --git a/util.c b/util.c index dcfa9e0..b6feea7 100644 --- a/util.c +++ b/util.c @@ -1,5 +1,9 @@ #include "util.h" #include +#include +#include +#include +#include unsigned long get_tick_count() { @@ -8,4 +12,34 @@ unsigned long get_tick_count() clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); +} + +char *gethost_by_addr(char *host, struct sockaddr *addr, socklen_t addr_len) +{ + struct sockaddr_storage *addr_store = (struct sockaddr_storage *)addr; + host[0] = 0; + switch (addr_store->ss_family) { + case AF_INET: { + struct sockaddr_in *addr_in; + addr_in = (struct sockaddr_in *)addr; + inet_ntop(AF_INET, &addr_in->sin_addr, host, addr_len); + } break; + case AF_INET6: { + struct sockaddr_in6 *addr_in6; + addr_in6 = (struct sockaddr_in6 *)addr; + if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { + struct sockaddr_in addr_in4; + memset(&addr_in4, 0, sizeof(addr_in4)); + memcpy(&addr_in4.sin_addr.s_addr, addr_in6->sin6_addr.s6_addr + 12, sizeof(addr_in4.sin_addr.s_addr)); + } else { + inet_ntop(AF_INET6, &addr_in6->sin6_addr, host, addr_len); + } + } break; + default: + goto errout; + break; + } + return host; +errout: + return NULL; } \ No newline at end of file diff --git a/util.h b/util.h index 7700d4d..737be34 100644 --- a/util.h +++ b/util.h @@ -3,6 +3,10 @@ #ifndef SMART_DNS_UTIL_H #define SMART_DNS_UTIL_H +#include + unsigned long get_tick_count(); +char *gethost_by_addr(char *host, struct sockaddr *addr, socklen_t addr_len); + #endif \ No newline at end of file