genode/repos/libports/src/lib/libc/time.cc

183 lines
3.6 KiB
C++

/*
* \brief C-library back end
* \author Christian Prochaska
* \author Norman Feske
* \date 2010-05-19
*/
/*
* Copyright (C) 2010-2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Libc includes */
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "task.h"
#include "libc_errno.h"
/* Genode includes */
#include <base/log.h>
#include <vfs/vfs_handle.h>
namespace Libc {
extern char const *config_rtc();
}
struct Rtc : Vfs::Watch_response_handler
{
Vfs::Vfs_watch_handle *_watch_handle { nullptr };
char const *_file { nullptr };
bool _read_file { true };
time_t _rtc_value { 0 };
Rtc(char const *rtc_file)
: _file(rtc_file)
{
if (!Genode::strcmp(_file, "")) {
Genode::warning("rtc not configured, returning ", _rtc_value);
return;
}
_watch_handle = Libc::watch(_file);
if (_watch_handle) {
_watch_handle->handler(this);
}
}
/******************************************
** Vfs::Watch_reponse_handler interface **
******************************************/
void watch_response() override
{
_read_file = true;
}
time_t read()
{
if (!_file) { return 0; }
/* return old value */
if (!_read_file) { return _rtc_value; }
_read_file = false;
int fd = open(_file, O_RDONLY);
if (fd == -1) {
Genode::warning(Genode::Cstring(Libc::config_rtc()), " not readable, returning ", _rtc_value);
return _rtc_value;
}
char buf[32];
ssize_t n = ::read(fd, buf, sizeof(buf));
if (n > 0) {
buf[n - 1] = '\0';
struct tm tm;
Genode::memset(&tm, 0, sizeof(tm));
if (strptime(buf, "%Y-%m-%d %R", &tm)) {
_rtc_value = mktime(&tm);
if (_rtc_value == (time_t)-1)
_rtc_value = 0;
}
}
close(fd);
uint64_t const ts_value =
Libc::current_time().trunc_to_plain_ms().value;
_rtc_value += (time_t)ts_value / 1000;
return _rtc_value;
}
};
extern "C" __attribute__((weak))
int clock_gettime(clockid_t clk_id, struct timespec *ts)
{
if (!ts) return Libc::Errno(EFAULT);
/* initialize timespec just in case users do not check for errors */
ts->tv_sec = 0;
ts->tv_nsec = 0;
switch (clk_id) {
/* IRL wall-time */
case CLOCK_REALTIME:
case CLOCK_SECOND: /* FreeBSD specific */
{
static Rtc rtc(Libc::config_rtc());
time_t const rtc_value = rtc.read();
if (!rtc_value) return Libc::Errno(EINVAL);
Genode::uint64_t const time = Libc::current_time().trunc_to_plain_ms().value;
ts->tv_sec = rtc_value + time/1000;
ts->tv_nsec = (time % 1000) * (1000*1000);
break;
}
/* component uptime */
case CLOCK_MONOTONIC:
case CLOCK_UPTIME:
{
Genode::uint64_t us = Libc::current_time().trunc_to_plain_us().value;
ts->tv_sec = us / (1000*1000);
ts->tv_nsec = (us % (1000*1000)) * 1000;
break;
}
default:
return Libc::Errno(EINVAL);
}
return 0;
}
extern "C" __attribute__((weak, alias("clock_gettime")))
int __sys_clock_gettime(clockid_t clk_id, struct timespec *ts);
extern "C" __attribute__((weak))
int gettimeofday(struct timeval *tv, struct timezone *)
{
if (!tv) return 0;
struct timespec ts;
if (int ret = clock_gettime(CLOCK_REALTIME, &ts))
return ret;
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1000;
return 0;
}
extern "C" __attribute__((weak, alias("gettimeofday")))
int __sys_gettimeofday(struct timeval *tv, struct timezone *);
extern "C"
clock_t clock()
{
Genode::error(__func__, " not implemented, use 'clock_gettime' instead");
return -1;
}