| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2014 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #define _GNU_SOURCE |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/time.h> |
| #include <sys/timex.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netinet/ip.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| |
| #include <glib.h> |
| |
| #include <gdhcp/timer.h> |
| |
| #include "connman.h" |
| #undef DBG /* temp debug for SAPPHIRE-13875 */ |
| #define DBG(msg, ...) connman_info("ntp: " msg, ##__VA_ARGS__) |
| |
| // fail-safe for non-linux builds |
| #ifndef CLOCK_BOOTTIME |
| #define CLOCK_BOOTTIME CLOCK_REALTIME |
| #endif |
| |
| struct ntp_short { |
| uint16_t seconds; |
| uint16_t fraction; |
| } __attribute__ ((packed)); |
| |
| struct ntp_time { |
| uint32_t seconds; |
| uint32_t fraction; |
| } __attribute__ ((packed)); |
| |
| struct ntp_msg { |
| uint8_t flags; /* Mode, version and leap indicator */ |
| uint8_t stratum; /* Stratum details */ |
| int8_t poll; /* Maximum interval in log2 seconds */ |
| int8_t precision; /* Clock precision in log2 seconds */ |
| struct ntp_short rootdelay; /* Root delay */ |
| struct ntp_short rootdisp; /* Root dispersion */ |
| uint32_t refid; /* Reference ID */ |
| struct ntp_time reftime; /* Reference timestamp */ |
| struct ntp_time orgtime; /* Origin timestamp */ |
| struct ntp_time rectime; /* Receive timestamp */ |
| struct ntp_time xmttime; /* Transmit timestamp */ |
| } __attribute__ ((packed)); |
| |
| #define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */ |
| |
| #define LOGTOD(a) ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a)) |
| |
| #define NTP_SEND_TIMEOUT 2 |
| #define NTP_SEND_RETRIES 3 |
| /* maximum time to stay awake for ntp response. Time for all retries plus |
| * arbitrary amount of extra to allow for jitter on calling send_timeout |
| */ |
| #define NTP_SEND_WAKETIMEOUT (((NTP_SEND_TIMEOUT << NTP_SEND_RETRIES)*2) + 10) |
| |
| #define NTP_FLAG_LI_SHIFT 6 |
| #define NTP_FLAG_LI_MASK 0x3 |
| #define NTP_FLAG_LI_NOWARNING 0x0 |
| #define NTP_FLAG_LI_ADDSECOND 0x1 |
| #define NTP_FLAG_LI_DELSECOND 0x2 |
| #define NTP_FLAG_LI_NOTINSYNC 0x3 |
| |
| #define NTP_FLAG_VN_SHIFT 3 |
| #define NTP_FLAG_VN_MASK 0x7 |
| |
| #define NTP_FLAG_MD_SHIFT 0 |
| #define NTP_FLAG_MD_MASK 0x7 |
| #define NTP_FLAG_MD_UNSPEC 0 |
| #define NTP_FLAG_MD_ACTIVE 1 |
| #define NTP_FLAG_MD_PASSIVE 2 |
| #define NTP_FLAG_MD_CLIENT 3 |
| #define NTP_FLAG_MD_SERVER 4 |
| #define NTP_FLAG_MD_BROADCAST 5 |
| #define NTP_FLAG_MD_CONTROL 6 |
| #define NTP_FLAG_MD_PRIVATE 7 |
| |
| #define NTP_FLAG_VN_VER3 3 |
| #define NTP_FLAG_VN_VER4 4 |
| |
| #define NTP_FLAGS_ENCODE(li, vn, md) ((uint8_t)( \ |
| (((li) & NTP_FLAG_LI_MASK) << NTP_FLAG_LI_SHIFT) | \ |
| (((vn) & NTP_FLAG_VN_MASK) << NTP_FLAG_VN_SHIFT) | \ |
| (((md) & NTP_FLAG_MD_MASK) << NTP_FLAG_MD_SHIFT))) |
| |
| #define NTP_FLAGS_LI_DECODE(flags) ((uint8_t)(((flags) >> NTP_FLAG_LI_SHIFT) & NTP_FLAG_LI_MASK)) |
| #define NTP_FLAGS_VN_DECODE(flags) ((uint8_t)(((flags) >> NTP_FLAG_VN_SHIFT) & NTP_FLAG_VN_MASK)) |
| #define NTP_FLAGS_MD_DECODE(flags) ((uint8_t)(((flags) >> NTP_FLAG_MD_SHIFT) & NTP_FLAG_MD_MASK)) |
| |
| #define NTP_PRECISION_S 0 |
| #define NTP_PRECISION_DS -3 |
| #define NTP_PRECISION_CS -6 |
| #define NTP_PRECISION_MS -9 |
| #define NTP_PRECISION_US -19 |
| #define NTP_PRECISION_NS -29 |
| |
| #define NTP_MINPOLL 4 /* 2^4 = 16 seconds */ |
| #define NTP_MAXPOLL 17 /* 2^17 = 36.4 hours */ |
| #define NTP_MINPOLL_DEFAULT 6 /* 2^6 = 64 seconds */ |
| #define NTP_MAXPOLL_DEFAULT 12 /* 2^12 = 4096 seconds */ |
| |
| static guint channel_watch = 0; |
| static struct timespec mtx_time; |
| static int transmit_fd = 0; |
| |
| static char *timeserver = NULL; |
| static struct sockaddr_in6 timeserver_addr; |
| static gint poll_id = 0; |
| static gint timeout_id = 0; |
| static guint retries = 0; |
| static bool ntp_can_sleep = true; |
| |
| static void send_packet(int fd, struct sockaddr *server, uint32_t timeout); |
| |
| static void next_server(void) |
| { |
| if (timeserver) { |
| g_free(timeserver); |
| timeserver = NULL; |
| } |
| |
| __connman_timeserver_sync_next(); |
| } |
| |
| static gboolean send_timeout(gpointer user_data) |
| { |
| uint32_t timeout = GPOINTER_TO_UINT(user_data); |
| |
| DBG("send timeout %u (retries %d)", timeout, retries); |
| |
| if (retries++ == NTP_SEND_RETRIES) |
| next_server(); |
| else |
| send_packet(transmit_fd, (struct sockaddr *)×erver_addr, timeout << 1); |
| |
| return FALSE; |
| } |
| |
| static void send_packet(int fd, struct sockaddr *server, uint32_t timeout) |
| { |
| struct ntp_msg msg; |
| struct timeval transmit_timeval; |
| ssize_t len; |
| void * addr; |
| int size; |
| char ipaddrstring[INET6_ADDRSTRLEN + 1]; |
| |
| /* |
| * At some point, we could specify the actual system precision with: |
| * |
| * clock_getres(CLOCK_REALTIME, &ts); |
| * msg.precision = (int)log2(ts.tv_sec + (ts.tv_nsec * 1.0e-9)); |
| */ |
| memset(&msg, 0, sizeof(msg)); |
| msg.flags = NTP_FLAGS_ENCODE(NTP_FLAG_LI_NOTINSYNC, NTP_FLAG_VN_VER4, |
| NTP_FLAG_MD_CLIENT); |
| msg.poll = NTP_MAXPOLL_DEFAULT; |
| msg.precision = NTP_PRECISION_S; |
| |
| if (server->sa_family == AF_INET) { |
| size = sizeof(struct sockaddr_in); |
| addr = (void *)&(((struct sockaddr_in *)×erver_addr)->sin_addr); |
| } else if (server->sa_family == AF_INET6) { |
| size = sizeof(struct sockaddr_in6); |
| addr = (void *)×erver_addr.sin6_addr; |
| } else { |
| connman_error("ntp: Family is neither ipv4 nor ipv6"); |
| return; |
| } |
| |
| if (ntp_can_sleep) { |
| ntp_can_sleep = false; |
| __connman_notifier_sleep_event(&ntp_can_sleep, ntp_can_sleep, time(0) + NTP_SEND_WAKETIMEOUT); |
| } |
| |
| gettimeofday(&transmit_timeval, NULL); |
| clock_gettime(CLOCK_MONOTONIC, &mtx_time); |
| |
| msg.xmttime.seconds = htonl(transmit_timeval.tv_sec + OFFSET_1900_1970); |
| msg.xmttime.fraction = htonl(transmit_timeval.tv_usec * 1000); |
| |
| len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT, |
| server, size); |
| |
| if (len < 0) { |
| connman_error("ntp: Time request for server %s failed (%d/%s)", |
| inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring)), |
| errno, strerror(errno)); |
| |
| if (errno == ENETUNREACH) |
| __connman_timeserver_sync_next(); |
| |
| return; |
| } |
| |
| if (len != sizeof(msg)) { |
| connman_error("ntp: Broken time request for server %s", |
| inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring))); |
| return; |
| } |
| |
| /* |
| * Add an exponential retry timeout to retry the existing |
| * request. After a set number of retries, we'll fallback to |
| * trying another server. |
| */ |
| |
| timeout_id = g_timeout_add_seconds(timeout, send_timeout, |
| GUINT_TO_POINTER(timeout)); |
| } |
| |
| static gboolean next_poll(gpointer user_data) |
| { |
| poll_id = 0; |
| |
| if (!timeserver || transmit_fd == 0) |
| return FALSE; |
| |
| send_packet(transmit_fd, (struct sockaddr *)×erver_addr, NTP_SEND_TIMEOUT); |
| |
| return FALSE; |
| } |
| |
| static void reset_timeout(void) |
| { |
| if (timeout_id > 0) { |
| g_source_remove(timeout_id); |
| timeout_id = 0; |
| } |
| |
| retries = 0; |
| } |
| |
| /* |
| * function to support both light and heavy sleeping devices |
| * slew adjustment for light sleep device |
| * set time of day for heavy sleep device |
| */ |
| static void adjust_time(struct ntp_msg *msg, double delay, double offset) |
| { |
| #ifdef ANDROID_COMPILE |
| #define STEPTIME_MIN_OFFSET 0.4 |
| #define NSEC_PER_SEC ((uint64_t)1000000000ULL) |
| #ifndef ADJ_SETOFFSET |
| #define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ |
| #endif /* ADJ_SETOFFSET */ |
| struct timex tmx = {}; |
| |
| if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) { |
| tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; |
| tmx.status = STA_PLL; |
| tmx.offset = offset * NSEC_PER_SEC; |
| tmx.constant = msg->poll - 4; |
| tmx.maxerror = 0; |
| tmx.esterror = 0; |
| |
| connman_info("ntp: adjust (slew): %+.6f sec", offset); |
| } else { |
| tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR; |
| |
| /* ADJ_NANO uses nanoseconds in the microseconds field */ |
| tmx.time.tv_sec = (long)offset; |
| tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC; |
| tmx.maxerror = 0; |
| tmx.esterror = 0; |
| |
| /* the kernel expects -0.3s as {-1, 7000.000.000} */ |
| if (tmx.time.tv_usec < 0) { |
| tmx.time.tv_sec -= 1; |
| tmx.time.tv_usec += NSEC_PER_SEC; |
| } |
| |
| connman_info("ntp: adjust (jump): %+.6f sec", offset); |
| } |
| |
| |
| if (NTP_FLAGS_LI_DECODE(msg->flags) & NTP_FLAG_LI_ADDSECOND) |
| tmx.status |= STA_INS; |
| else if (NTP_FLAGS_LI_DECODE(msg->flags) & NTP_FLAG_LI_DELSECOND) |
| tmx.status |= STA_DEL; |
| |
| if (adjtimex(&tmx) < 0) { |
| connman_error("ntp: Failed to adjust time"); |
| } else { |
| DBG("interval/delta/delay/drift %fs/%+.3fs/%.3fs/%+ldppm", |
| LOGTOD(msg->poll), offset, delay, tmx.freq / 65536); |
| } |
| #else |
| struct timeval cur; |
| double dtime; |
| |
| gettimeofday(&cur, NULL); |
| dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec; |
| cur.tv_sec = (long) dtime; |
| cur.tv_usec = (dtime - cur.tv_sec) * 1000000; |
| |
| if (settimeofday(&cur, NULL) < 0) { |
| connman_error("ntp: Failed to set time"); |
| } else { |
| connman_info("ntp: adjust (jump): %+.6f sec", offset); |
| } |
| #endif |
| } |
| |
| static void decode_msg(void *base, size_t len, struct timeval *tv, |
| struct timespec *mrx_time) |
| { |
| struct ntp_msg *msg = base; |
| double m_delta, org, rec, xmt, dst; |
| double delay, offset; |
| static guint transmit_delay; |
| |
| if (len < sizeof(*msg)) { |
| connman_error("ntp: Invalid response from time server"); |
| return; |
| } |
| |
| if (!tv) { |
| connman_error("ntp: Invalid packet timestamp from time server"); |
| return; |
| } |
| |
| DBG("flags : 0x%02x", msg->flags); |
| DBG("stratum : %u", msg->stratum); |
| DBG("poll : %f seconds (%d)", |
| LOGTOD(msg->poll), msg->poll); |
| DBG("precision : %f seconds (%d)", |
| LOGTOD(msg->precision), msg->precision); |
| DBG("root delay : %u seconds (fraction %u)", |
| msg->rootdelay.seconds, msg->rootdelay.fraction); |
| DBG("root disp. : %u seconds (fraction %u)", |
| msg->rootdisp.seconds, msg->rootdisp.fraction); |
| DBG("reference : 0x%04x", msg->refid); |
| |
| if (!msg->stratum) { |
| /* RFC 4330 ch 8 Kiss-of-Death packet */ |
| uint32_t code = ntohl(msg->refid); |
| |
| connman_info("ntp: Skipping server %s KoD code %c%c%c%c", |
| timeserver, code >> 24, code >> 16 & 0xff, |
| code >> 8 & 0xff, code & 0xff); |
| next_server(); |
| return; |
| } |
| |
| if (msg->poll < NTP_MINPOLL || msg->poll > NTP_MAXPOLL) { |
| DBG("fix poll : %f seconds (%d)", |
| LOGTOD(NTP_MAXPOLL_DEFAULT), NTP_MAXPOLL_DEFAULT); |
| transmit_delay = LOGTOD(NTP_MAXPOLL_DEFAULT); |
| msg->poll = NTP_MAXPOLL_DEFAULT; |
| } else { |
| transmit_delay = LOGTOD(msg->poll); |
| } |
| |
| if (NTP_FLAGS_LI_DECODE(msg->flags) == NTP_FLAG_LI_NOTINSYNC) { |
| DBG("ignoring unsynchronized peer"); |
| return; |
| } |
| |
| |
| if (NTP_FLAGS_VN_DECODE(msg->flags) != NTP_FLAG_VN_VER4) { |
| if (NTP_FLAGS_VN_DECODE(msg->flags) == NTP_FLAG_VN_VER3) { |
| DBG("requested version %d, accepting version %d", |
| NTP_FLAG_VN_VER4, NTP_FLAGS_VN_DECODE(msg->flags)); |
| } else { |
| DBG("unsupported version %d", NTP_FLAGS_VN_DECODE(msg->flags)); |
| return; |
| } |
| } |
| |
| if (NTP_FLAGS_MD_DECODE(msg->flags) != NTP_FLAG_MD_SERVER) { |
| DBG("unsupported mode %d", NTP_FLAGS_MD_DECODE(msg->flags)); |
| return; |
| } |
| |
| m_delta = mrx_time->tv_sec - mtx_time.tv_sec + |
| 1.0e-9 * (mrx_time->tv_nsec - mtx_time.tv_nsec); |
| |
| org = tv->tv_sec + (1.0e-6 * tv->tv_usec) - m_delta + OFFSET_1900_1970; |
| rec = ntohl(msg->rectime.seconds) + |
| ((double) ntohl(msg->rectime.fraction) / UINT_MAX); |
| xmt = ntohl(msg->xmttime.seconds) + |
| ((double) ntohl(msg->xmttime.fraction) / UINT_MAX); |
| dst = tv->tv_sec + (1.0e-6 * tv->tv_usec) + OFFSET_1900_1970; |
| |
| DBG("org=%f rec=%f xmt=%f dst=%f", org, rec, xmt, dst); |
| |
| offset = ((rec - org) + (xmt - dst)) / 2; |
| delay = (dst - org) - (xmt - rec); |
| |
| DBG("offset=%f delay=%f", offset, delay); |
| |
| /* Remove the timeout, as timeserver has responded */ |
| |
| reset_timeout(); |
| |
| /* |
| * Now poll the server every transmit_delay seconds |
| * for time correction. |
| */ |
| if (poll_id > 0) |
| g_rttimeout_source_remove(poll_id); |
| |
| DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay); |
| |
| poll_id = g_rttimeout_add_seconds_full(CLOCK_BOOTTIME, G_PRIORITY_DEFAULT, transmit_delay, |
| next_poll, NULL, NULL); |
| |
| adjust_time(msg, delay, offset); |
| |
| if (!ntp_can_sleep) { |
| ntp_can_sleep = true; |
| __connman_notifier_sleep_event(&ntp_can_sleep, ntp_can_sleep, time(0) + 2 * NTP_SEND_TIMEOUT); |
| } |
| } |
| |
| static gboolean received_data(GIOChannel *channel, GIOCondition condition, |
| gpointer user_data) |
| { |
| unsigned char buf[128]; |
| struct sockaddr_in6 sender_addr; |
| struct msghdr msg; |
| struct iovec iov; |
| struct cmsghdr *cmsg; |
| struct timeval *tv; |
| struct timespec mrx_time; |
| char aux[128]; |
| ssize_t len; |
| int fd; |
| int size; |
| void * addr_ptr; |
| void * src_ptr; |
| |
| if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { |
| connman_error("ntp: Problem with timer server channel"); |
| channel_watch = 0; |
| return FALSE; |
| } |
| |
| fd = g_io_channel_unix_get_fd(channel); |
| |
| iov.iov_base = buf; |
| iov.iov_len = sizeof(buf); |
| |
| memset(&msg, 0, sizeof(msg)); |
| msg.msg_iov = &iov; |
| msg.msg_iovlen = 1; |
| msg.msg_control = aux; |
| msg.msg_controllen = sizeof(aux); |
| msg.msg_name = &sender_addr; |
| msg.msg_namelen = sizeof(sender_addr); |
| |
| len = recvmsg(fd, &msg, MSG_DONTWAIT); |
| if (len < 0) |
| return TRUE; |
| |
| if (sender_addr.sin6_family == AF_INET) { |
| size = 4; |
| addr_ptr = &((struct sockaddr_in *)×erver_addr)->sin_addr; |
| src_ptr = &((struct sockaddr_in *)&sender_addr)->sin_addr; |
| } else if (sender_addr.sin6_family == AF_INET6) { |
| size = 16; |
| addr_ptr = &((struct sockaddr_in6 *)×erver_addr)->sin6_addr; |
| src_ptr = &((struct sockaddr_in6 *)&sender_addr)->sin6_addr; |
| } else { |
| connman_error("ntp: Not a valid family type"); |
| return TRUE; |
| } |
| |
| if(memcmp(addr_ptr, src_ptr, size) != 0) |
| return TRUE; |
| |
| tv = NULL; |
| clock_gettime(CLOCK_MONOTONIC, &mrx_time); |
| |
| for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
| if (cmsg->cmsg_level != SOL_SOCKET) |
| continue; |
| |
| switch (cmsg->cmsg_type) { |
| case SCM_TIMESTAMP: |
| tv = (struct timeval *) CMSG_DATA(cmsg); |
| break; |
| } |
| } |
| |
| decode_msg(iov.iov_base, iov.iov_len, tv, &mrx_time); |
| |
| return TRUE; |
| } |
| |
| static void start_ntp(char *server) |
| { |
| GIOChannel *channel; |
| struct addrinfo hint; |
| struct addrinfo *info; |
| struct sockaddr * addr; |
| struct sockaddr_in * in4addr; |
| struct sockaddr_in6 in6addr; |
| int size; |
| int family; |
| // int tos = IPTOS_LOWDELAY; |
| int timestamp = 1; |
| int ret; |
| int opt = 1; /* boolean */ |
| |
| if (!server) |
| return; |
| |
| DBG("server %s family %d", server, family); |
| memset(&hint, 0, sizeof(hint)); |
| hint.ai_family = AF_UNSPEC; |
| hint.ai_socktype = SOCK_DGRAM; |
| hint.ai_flags = AI_NUMERICHOST | AI_PASSIVE; |
| ret = getaddrinfo(server, NULL, &hint, &info); |
| |
| if (ret) { |
| connman_error("ntp: cannot get server info (ret %d)", ret); |
| return; |
| } |
| |
| family = info->ai_family; |
| |
| memcpy(×erver_addr, info->ai_addr, info->ai_addrlen); |
| freeaddrinfo(info); |
| memset(&in6addr, 0, sizeof(in6addr)); |
| |
| if (family == AF_INET) { |
| ((struct sockaddr_in *)×erver_addr)->sin_port = htons(123); |
| in4addr = (struct sockaddr_in *)&in6addr; |
| in4addr->sin_family = family; |
| addr = (struct sockaddr *)in4addr; |
| size = sizeof(struct sockaddr_in); |
| } else if (family == AF_INET6) { |
| timeserver_addr.sin6_port = htons(123); |
| in6addr.sin6_family = family; |
| addr = (struct sockaddr *)&in6addr; |
| size = sizeof(in6addr); |
| } else { |
| connman_error("ntp: Family not correct"); |
| return; |
| } |
| |
| DBG("server %s family %d", server, family); |
| |
| if (channel_watch > 0) |
| goto send; |
| |
| transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); |
| |
| if (transmit_fd <= 0) { |
| connman_error("ntp: Failed to open time server socket"); |
| return; |
| } |
| |
| if (setsockopt(transmit_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { |
| connman_error("ntp: Failed to enable reuse option"); |
| close(transmit_fd); |
| return; |
| } |
| |
| if (bind(transmit_fd, (struct sockaddr *) addr, size) < 0) { |
| connman_error("ntp: Failed to bind time server socket"); |
| close(transmit_fd); |
| return; |
| } |
| |
| /* SAPPHIRE-2965 */ |
| /* if (family == AF_INET) { */ |
| /* if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { */ |
| /* connman_error("ntp: Failed to set type of service option"); */ |
| /* close(transmit_fd); */ |
| /* return; */ |
| /* } */ |
| /* } */ |
| |
| if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp, |
| sizeof(timestamp)) < 0) { |
| connman_error("ntp: Failed to enable timestamp support"); |
| close(transmit_fd); |
| return; |
| } |
| |
| channel = g_io_channel_unix_new(transmit_fd); |
| if (!channel) { |
| close(transmit_fd); |
| return; |
| } |
| |
| g_io_channel_set_encoding(channel, NULL, NULL); |
| g_io_channel_set_buffered(channel, FALSE); |
| |
| g_io_channel_set_close_on_unref(channel, TRUE); |
| |
| channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, |
| G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, |
| received_data, NULL, NULL); |
| |
| g_io_channel_unref(channel); |
| |
| send: |
| send_packet(transmit_fd, (struct sockaddr*)×erver_addr, NTP_SEND_TIMEOUT); |
| } |
| |
| int __connman_ntp_start(char *server) |
| { |
| DBG("%s", server); |
| |
| if (!server) |
| return -EINVAL; |
| |
| if (timeserver) |
| g_free(timeserver); |
| |
| timeserver = g_strdup(server); |
| |
| start_ntp(timeserver); |
| |
| return 0; |
| } |
| |
| void __connman_ntp_stop() |
| { |
| DBG("stop"); |
| |
| if (poll_id > 0) { |
| g_rttimeout_source_remove(poll_id); |
| poll_id = 0; |
| } |
| |
| reset_timeout(); |
| |
| if (channel_watch > 0) { |
| g_source_remove(channel_watch); |
| channel_watch = 0; |
| transmit_fd = 0; |
| } |
| |
| if (timeserver) { |
| g_free(timeserver); |
| timeserver = NULL; |
| } |
| |
| if (!ntp_can_sleep) { |
| ntp_can_sleep = true; |
| __connman_notifier_sleep_event(&ntp_can_sleep, ntp_can_sleep, time(0) + 6 * NTP_SEND_TIMEOUT); |
| } |
| } |
| |
| int __connman_ntp_check_started(void) |
| { |
| return timeserver != NULL && transmit_fd > 0 && channel_watch > 0; |
| } |