X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fntp.c;h=0de4df759c7edb8e3fc7b2b0f48f6e25193ae0de;hb=a01a64f7444af4734621ba43af3f11216891e245;hp=abb2caa26260e27a775f20d25b85223b0b0a3d81;hpb=22633ced6225d294ce8483efbf2b39ea0c0c1b65;p=platform%2Fupstream%2Fconnman.git diff --git a/src/ntp.c b/src/ntp.c old mode 100644 new mode 100755 index abb2caa..0de4df7 --- a/src/ntp.c +++ b/src/ntp.c @@ -23,17 +23,18 @@ #include #endif -#define _GNU_SOURCE #include #include #include #include #include #include +#include #include #include #include #include +#include #include @@ -65,9 +66,13 @@ struct ntp_msg { #define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */ -#define STEPTIME_MIN_OFFSET 0.128 +#define STEPTIME_MIN_OFFSET 0.4 #define LOGTOD(a) ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a)) +#define NSEC_PER_SEC ((uint64_t)1000000000ULL) +#ifndef ADJ_SETOFFSET +#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ +#endif #define NTP_SEND_TIMEOUT 2 #define NTP_SEND_RETRIES 3 @@ -112,48 +117,62 @@ struct ntp_msg { #define NTP_PRECISION_US -19 #define NTP_PRECISION_NS -29 -static guint channel_watch = 0; -static struct timespec mtx_time; -static int transmit_fd = 0; - -static char *timeserver = NULL; -static struct sockaddr_in timeserver_addr; -static gint poll_id = 0; -static gint timeout_id = 0; -static guint retries = 0; - -static void send_packet(int fd, const char *server, uint32_t timeout); - -static void next_server(void) +struct ntp_data { + char *timeserver; + struct sockaddr_in6 timeserver_addr; + struct timespec mtx_time; + int transmit_fd; + gint timeout_id; + guint retries; + guint channel_watch; + gint poll_id; + uint32_t timeout; + __connman_ntp_cb_t cb; + void *user_data; +}; + +static struct ntp_data *ntp_data; + +static void free_ntp_data(struct ntp_data *nd) { - if (timeserver) { - g_free(timeserver); - timeserver = NULL; - } - - __connman_timeserver_sync_next(); + if (nd->poll_id) + g_source_remove(nd->poll_id); + if (nd->timeout_id) + g_source_remove(nd->timeout_id); + if (nd->channel_watch) + g_source_remove(nd->channel_watch); + if (nd->timeserver) + g_free(nd->timeserver); + g_free(nd); } +static void send_packet(struct ntp_data *nd, struct sockaddr *server, + uint32_t timeout); + static gboolean send_timeout(gpointer user_data) { - uint32_t timeout = GPOINTER_TO_UINT(user_data); + struct ntp_data *nd = user_data; - DBG("send timeout %u (retries %d)", timeout, retries); + DBG("send timeout %u (retries %d)", nd->timeout, nd->retries); - if (retries++ == NTP_SEND_RETRIES) - next_server(); + if (nd->retries++ == NTP_SEND_RETRIES) + nd->cb(false, nd->user_data); else - send_packet(transmit_fd, timeserver, timeout << 1); + send_packet(nd, (struct sockaddr *)&nd->timeserver_addr, + nd->timeout << 1); return FALSE; } -static void send_packet(int fd, const char *server, uint32_t timeout) +static void send_packet(struct ntp_data *nd, struct sockaddr *server, + uint32_t timeout) { struct ntp_msg msg; - struct sockaddr_in addr; 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: @@ -164,35 +183,33 @@ static void send_packet(int fd, const char *server, uint32_t timeout) memset(&msg, 0, sizeof(msg)); msg.flags = NTP_FLAGS_ENCODE(NTP_FLAG_LI_NOTINSYNC, NTP_FLAG_VN_VER4, NTP_FLAG_MD_CLIENT); - msg.poll = 4; // min - msg.poll = 10; // max + msg.poll = 10; msg.precision = NTP_PRECISION_S; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(123); - addr.sin_addr.s_addr = inet_addr(server); + if (server->sa_family == AF_INET) { + size = sizeof(struct sockaddr_in); + addr = (void *)&(((struct sockaddr_in *)&nd->timeserver_addr)->sin_addr); + } else if (server->sa_family == AF_INET6) { + size = sizeof(struct sockaddr_in6); + addr = (void *)&nd->timeserver_addr.sin6_addr; + } else { + DBG("Family is neither ipv4 nor ipv6"); + nd->cb(false, nd->user_data); + return; + } gettimeofday(&transmit_timeval, NULL); - clock_gettime(CLOCK_MONOTONIC, &mtx_time); + clock_gettime(CLOCK_MONOTONIC, &nd->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, - &addr, sizeof(addr)); - if (len < 0) { - connman_error("Time request for server %s failed (%d/%s)", - server, errno, strerror(errno)); - - if (errno == ENETUNREACH) - __connman_timeserver_sync_next(); - - return; - } - - if (len != sizeof(msg)) { - connman_error("Broken time request for server %s", server); + len = sendto(nd->transmit_fd, &msg, sizeof(msg), MSG_DONTWAIT, + server, size); + if (len < 0 || len != sizeof(msg)) { + DBG("Time request for server %s failed", + inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring))); + nd->cb(false, nd->user_data); return; } @@ -202,39 +219,43 @@ static void send_packet(int fd, const char *server, uint32_t timeout) * trying another server. */ - timeout_id = g_timeout_add_seconds(timeout, send_timeout, - GUINT_TO_POINTER(timeout)); + nd->timeout = timeout; + nd->timeout_id = g_timeout_add_seconds(timeout, send_timeout, nd); } static gboolean next_poll(gpointer user_data) { - poll_id = 0; + struct ntp_data *nd = user_data; + nd->poll_id = 0; - if (!timeserver || transmit_fd == 0) + if (!nd->timeserver || nd->transmit_fd == 0) return FALSE; - send_packet(transmit_fd, timeserver, NTP_SEND_TIMEOUT); + send_packet(nd, (struct sockaddr *)&nd->timeserver_addr, NTP_SEND_TIMEOUT); return FALSE; } -static void reset_timeout(void) +static void reset_timeout(struct ntp_data *nd) { - if (timeout_id > 0) { - g_source_remove(timeout_id); - timeout_id = 0; + if (nd->timeout_id > 0) { + g_source_remove(nd->timeout_id); + nd->timeout_id = 0; } - retries = 0; + nd->retries = 0; } -static void decode_msg(void *base, size_t len, struct timeval *tv, - struct timespec *mrx_time) +static void decode_msg(struct ntp_data *nd, 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 !defined TIZEN_EXT + struct timex tmx = {}; +#endif if (len < sizeof(*msg)) { connman_error("Invalid response from time server"); @@ -263,9 +284,9 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, uint32_t code = ntohl(msg->refid); connman_info("Skipping server %s KoD code %c%c%c%c", - timeserver, code >> 24, code >> 16 & 0xff, + nd->timeserver, code >> 24, code >> 16 & 0xff, code >> 8 & 0xff, code & 0xff); - next_server(); + nd->cb(false, nd->user_data); return; } @@ -273,6 +294,7 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, if (NTP_FLAGS_LI_DECODE(msg->flags) == NTP_FLAG_LI_NOTINSYNC) { DBG("ignoring unsynchronized peer"); + nd->cb(false, nd->user_data); return; } @@ -283,17 +305,19 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, NTP_FLAG_VN_VER4, NTP_FLAGS_VN_DECODE(msg->flags)); } else { DBG("unsupported version %d", NTP_FLAGS_VN_DECODE(msg->flags)); + nd->cb(false, nd->user_data); return; } } if (NTP_FLAGS_MD_DECODE(msg->flags) != NTP_FLAG_MD_SERVER) { DBG("unsupported mode %d", NTP_FLAGS_MD_DECODE(msg->flags)); + nd->cb(false, nd->user_data); return; } - m_delta = mrx_time->tv_sec - mtx_time.tv_sec + - 1.0e-9 * (mrx_time->tv_nsec - mtx_time.tv_nsec); + m_delta = mrx_time->tv_sec - nd->mtx_time.tv_sec + + 1.0e-9 * (mrx_time->tv_nsec - nd->mtx_time.tv_nsec); org = tv->tv_sec + (1.0e-6 * tv->tv_usec) - m_delta + OFFSET_1900_1970; rec = ntohl(msg->rectime.seconds) + @@ -311,60 +335,133 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, /* Remove the timeout, as timeserver has responded */ - reset_timeout(); + reset_timeout(nd); /* * Now poll the server every transmit_delay seconds * for time correction. */ - if (poll_id > 0) - g_source_remove(poll_id); + if (nd->poll_id > 0) + g_source_remove(nd->poll_id); - DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay); + DBG("Timeserver %s, next sync in %d seconds", nd->timeserver, + transmit_delay); - poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL); + nd->poll_id = g_timeout_add_seconds(transmit_delay, next_poll, nd); - connman_info("ntp: time slew %+.6f s", offset); +#if defined TIZEN_EXT + //send the dbus message to alram-manager + { +#define TIME_BUS_NAME "org.tizen.alarm.manager" +#define TIME_INTERFACE "org.tizen.alarm.manager" +#define TIME_PATH "/org/tizen/alarm/manager" +#define TIME_METHOD "alarm_set_time_with_propagation_delay" - if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) { - struct timeval adj; + struct timespec cur = {0}; + struct timespec req = {0}; + double dtime; - adj.tv_sec = (long) offset; - adj.tv_usec = (offset - adj.tv_sec) * 1000000; + DBusConnection *connection = NULL; + DBusMessage *msg = NULL, *reply = NULL; + DBusError error; - DBG("adjusting time"); + dbus_error_init(&error); - if (adjtime(&adj, &adj) < 0) { - connman_error("Failed to adjust time"); + connection = connman_dbus_get_connection(); + if(!connection){ + DBG("dbus connection does not exist"); return; } - DBG("%lu seconds, %lu msecs", adj.tv_sec, adj.tv_usec); - } else { - struct timeval cur; - double dtime; - - gettimeofday(&cur, NULL); - dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec; + clock_gettime(CLOCK_REALTIME, &cur); + dtime = offset + cur.tv_sec + 1.0e-9 * cur.tv_nsec; cur.tv_sec = (long) dtime; - cur.tv_usec = (dtime - cur.tv_sec) * 1000000; + cur.tv_nsec = (dtime - cur.tv_sec) * 1000000000; + + clock_gettime(CLOCK_REALTIME, &req); + msg = dbus_message_new_method_call(TIME_BUS_NAME, TIME_PATH, + TIME_INTERFACE, TIME_METHOD); + dbus_message_append_args(msg, DBUS_TYPE_INT64, &(cur.tv_sec), + DBUS_TYPE_INT64, &(cur.tv_nsec), + DBUS_TYPE_INT64, &(req.tv_sec), + DBUS_TYPE_INT64, &(req.tv_nsec), DBUS_TYPE_INVALID); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + DBUS_TIMEOUT_USE_DEFAULT, &error); + if(reply == NULL){ + if(dbus_error_is_set(&error)){ + DBG("%s", error.message); + dbus_error_free(&error); + } + else{ + DBG("Failed to request set time"); + } + dbus_connection_unref(connection); + dbus_message_unref(msg); + return; + } + dbus_message_unref(msg); + dbus_message_unref(reply); + dbus_connection_unref(connection); + + DBG("%lu cur seconds, %lu cur nsecs, %lu req seconds, %lu req nsecs", + cur.tv_sec, cur.tv_nsec, req.tv_sec, req.tv_nsec); DBG("setting time"); - if (settimeofday(&cur, NULL) < 0) { - connman_error("Failed to set time"); - return; + __connman_clock_set_time_updated(true); + } +#else + 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; } - DBG("%lu seconds, %lu msecs", cur.tv_sec, cur.tv_usec); + 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("Failed to adjust time: %s (%d)", strerror(errno), errno); + nd->cb(false, nd->user_data); + return; } + + DBG("interval/delta/delay/drift %fs/%+.3fs/%.3fs/%+ldppm", + LOGTOD(msg->poll), offset, delay, tmx.freq / 65536); + + nd->cb(true, nd->user_data); +#endif } static gboolean received_data(GIOChannel *channel, GIOCondition condition, gpointer user_data) { + struct ntp_data *nd = user_data; unsigned char buf[128]; - struct sockaddr_in sender_addr; + struct sockaddr_in6 sender_addr; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; @@ -373,10 +470,13 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, 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("Problem with timer server channel"); - channel_watch = 0; + nd->channel_watch = 0; return FALSE; } @@ -397,8 +497,20 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, if (len < 0) return TRUE; - if (timeserver_addr.sin_addr.s_addr != sender_addr.sin_addr.s_addr) - /* only accept messages from the timeserver */ + if (sender_addr.sin6_family == AF_INET) { + size = 4; + addr_ptr = &((struct sockaddr_in *)&nd->timeserver_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 *)&nd->timeserver_addr)->sin6_addr; + src_ptr = &((struct sockaddr_in6 *)&sender_addr)->sin6_addr; + } else { + connman_error("Not a valid family type"); + return TRUE; + } + + if(memcmp(addr_ptr, src_ptr, size) != 0) return TRUE; tv = NULL; @@ -415,111 +527,141 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, } } - decode_msg(iov.iov_base, iov.iov_len, tv, &mrx_time); + decode_msg(nd, iov.iov_base, iov.iov_len, tv, &mrx_time); return TRUE; } -static void start_ntp(char *server) +static void start_ntp(struct ntp_data *nd) { GIOChannel *channel; - struct sockaddr_in addr; + 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, timestamp = 1; + int ret; - if (!server) + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_UNSPEC; + hint.ai_socktype = SOCK_DGRAM; + hint.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + ret = getaddrinfo(nd->timeserver, NULL, &hint, &info); + + if (ret) { + connman_error("cannot get server info"); return; + } - DBG("server %s", server); + family = info->ai_family; + + memcpy(&ntp_data->timeserver_addr, info->ai_addr, info->ai_addrlen); + freeaddrinfo(info); + memset(&in6addr, 0, sizeof(in6addr)); + + if (family == AF_INET) { + ((struct sockaddr_in *)&ntp_data->timeserver_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) { + ntp_data->timeserver_addr.sin6_port = htons(123); + in6addr.sin6_family = family; + addr = (struct sockaddr *)&in6addr; + size = sizeof(in6addr); + } else { + connman_error("Family is neither ipv4 nor ipv6"); + return; + } + + DBG("server %s family %d", nd->timeserver, family); - if (channel_watch > 0) + if (nd->channel_watch > 0) goto send; - transmit_fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (transmit_fd < 0) { - connman_error("Failed to open time server socket"); - return; - } + nd->transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; + if (nd->transmit_fd <= 0) { + if (errno != EAFNOSUPPORT) + connman_error("Failed to open time server socket"); + } - if (bind(transmit_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (bind(nd->transmit_fd, (struct sockaddr *) addr, size) < 0) { connman_error("Failed to bind time server socket"); - close(transmit_fd); - return; + goto err; } - if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { - connman_error("Failed to set type of service option"); - close(transmit_fd); - return; + if (family == AF_INET) { + if (setsockopt(nd->transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { + connman_error("Failed to set type of service option"); + goto err; + } } - if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp, + if (setsockopt(nd->transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp, sizeof(timestamp)) < 0) { connman_error("Failed to enable timestamp support"); - close(transmit_fd); - return; + goto err; } - channel = g_io_channel_unix_new(transmit_fd); - if (!channel) { - close(transmit_fd); - return; - } + channel = g_io_channel_unix_new(nd->transmit_fd); + if (!channel) + goto err; 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, + nd->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); + received_data, nd, NULL); g_io_channel_unref(channel); send: - send_packet(transmit_fd, server, NTP_SEND_TIMEOUT); + send_packet(nd, (struct sockaddr*)&ntp_data->timeserver_addr, + NTP_SEND_TIMEOUT); + return; + +err: + if (nd->transmit_fd > 0) + close(nd->transmit_fd); + + nd->cb(false, nd->user_data); } -int __connman_ntp_start(char *server) +int __connman_ntp_start(char *server, __connman_ntp_cb_t callback, + void *user_data) { - DBG("%s", server); - if (!server) return -EINVAL; - if (timeserver) - g_free(timeserver); + if (ntp_data) { + connman_warn("ntp_data is not NULL (timerserver %s)", + ntp_data->timeserver); + free_ntp_data(ntp_data); + } + + ntp_data = g_new0(struct ntp_data, 1); - timeserver = g_strdup(server); - timeserver_addr.sin_addr.s_addr = inet_addr(server); + ntp_data->timeserver = g_strdup(server); + ntp_data->cb = callback; + ntp_data->user_data = user_data; - start_ntp(timeserver); + start_ntp(ntp_data); return 0; } void __connman_ntp_stop() { - DBG(""); - - if (poll_id > 0) { - g_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_data) { + free_ntp_data(ntp_data); + ntp_data = NULL; } }