noux: add time modification

... and set initial time by using RTC session.

Up to now Noux used a monotic clock whose initial start value always
was '0' (which correlates to the start of the UNIX epoch) to provide
a notion of time. In addition it is now possible to use the RTC
session to set the initial value for use cases where having a proper
real-world time matters.

To use the RTC session the 'rtc' attribute of the '<config>' node
must be set to 'yes'. Thereby the session becomes a mandatory
dependency as Noux will not start without it.

Issue #1784
This commit is contained in:
Josef Söntgen 2019-04-09 17:52:44 +02:00 committed by Christian Helmuth
parent d0bf6d2b52
commit 94b63924ed
8 changed files with 204 additions and 15 deletions

View File

@ -96,6 +96,7 @@ struct Noux::Sysio
unsigned gid; unsigned gid;
unsigned long inode; unsigned long inode;
unsigned long device; unsigned long device;
long long mtime;
Stat & operator= (Vfs::Directory_service::Stat const &stat) Stat & operator= (Vfs::Directory_service::Stat const &stat)
{ {
@ -105,6 +106,7 @@ struct Noux::Sysio
gid = stat.gid; gid = stat.gid;
inode = stat.inode; inode = stat.inode;
device = stat.device; device = stat.device;
mtime = stat.modification_time.value;
return *this; return *this;
} }

View File

@ -7,3 +7,4 @@ noux_session
terminal_session terminal_session
timer_session timer_session
posix posix
rtc_session

View File

@ -397,6 +397,9 @@ static void _sysio_to_stat_struct(Noux::Sysio const *sysio, struct stat *buf)
buf->st_blocks = (buf->st_size + FS_BLOCK_SIZE - 1) / FS_BLOCK_SIZE; buf->st_blocks = (buf->st_size + FS_BLOCK_SIZE - 1) / FS_BLOCK_SIZE;
buf->st_ino = sysio->stat_out.st.inode; buf->st_ino = sysio->stat_out.st.inode;
buf->st_dev = sysio->stat_out.st.device; buf->st_dev = sysio->stat_out.st.device;
if (sysio->stat_out.st.mtime >= 0)
buf->st_mtime = sysio->stat_out.st.mtime;
} }
@ -934,6 +937,13 @@ extern "C" int gettimeofday(struct timeval *tv, struct timezone *tz)
extern "C" int utimes(const char* path, const struct timeval *times) extern "C" int utimes(const char* path, const struct timeval *times)
{ {
char * const dst = sysio()->utimes_in.path;
size_t const max_len = sizeof(sysio()->utimes_in.path);
Genode::strncpy(dst, path, max_len);
sysio()->utimes_in.sec = times ? times->tv_sec : 0;
sysio()->utimes_in.usec = times ? times->tv_usec : 0;
if (!noux_syscall(Noux::Session::SYSCALL_UTIMES)) { if (!noux_syscall(Noux::Session::SYSCALL_UTIMES)) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;

View File

@ -37,6 +37,7 @@
#include <verbose.h> #include <verbose.h>
#include <user_info.h> #include <user_info.h>
#include <armed_timeout.h> #include <armed_timeout.h>
#include <time_info.h>
namespace Noux { namespace Noux {
@ -124,6 +125,8 @@ class Noux::Child : public Rpc_object<Session>,
User_info const &_user_info; User_info const &_user_info;
Time_info const &_time_info;
Parent_exit *_parent_exit; Parent_exit *_parent_exit;
Kill_broadcaster &_kill_broadcaster; Kill_broadcaster &_kill_broadcaster;
Timer::Connection &_timer_connection; Timer::Connection &_timer_connection;
@ -319,6 +322,7 @@ class Noux::Child : public Rpc_object<Session>,
Child(Child_policy::Name const &name, Child(Child_policy::Name const &name,
Verbose const &verbose, Verbose const &verbose,
User_info const &user_info, User_info const &user_info,
Time_info const &time_info,
Parent_exit *parent_exit, Parent_exit *parent_exit,
Kill_broadcaster &kill_broadcaster, Kill_broadcaster &kill_broadcaster,
Timer::Connection &timer_connection, Timer::Connection &timer_connection,
@ -342,6 +346,7 @@ class Noux::Child : public Rpc_object<Session>,
_name(name), _name(name),
_verbose(verbose), _verbose(verbose),
_user_info(user_info), _user_info(user_info),
_time_info(time_info),
_parent_exit(parent_exit), _parent_exit(parent_exit),
_kill_broadcaster(kill_broadcaster), _kill_broadcaster(kill_broadcaster),
_timer_connection(timer_connection), _timer_connection(timer_connection),
@ -521,6 +526,7 @@ class Noux::Child : public Rpc_object<Session>,
Child *child = new (_heap) Child(filename, Child *child = new (_heap) Child(filename,
_verbose, _verbose,
_user_info, _user_info,
_time_info,
_parent_exit, _parent_exit,
_kill_broadcaster, _kill_broadcaster,
_timer_connection, _timer_connection,

View File

@ -27,6 +27,7 @@
#include <kill_broadcaster.h> #include <kill_broadcaster.h>
#include <vfs/dir_file_system.h> #include <vfs/dir_file_system.h>
#include <vfs/simple_env.h> #include <vfs/simple_env.h>
#include <time_info.h>
namespace Noux { namespace Noux {
@ -98,7 +99,9 @@ connect_stdio(Genode::Env &env,
Vfs::File_system &root, Vfs::File_system &root,
Noux::Vfs_io_waiter_registry &vfs_io_waiter_registry, Noux::Vfs_io_waiter_registry &vfs_io_waiter_registry,
Noux::Terminal_io_channel::Type type, Noux::Terminal_io_channel::Type type,
Genode::Allocator &alloc) Genode::Allocator &alloc,
Noux::Time_info &time_info,
Timer::Connection &timer)
{ {
using namespace Vfs; using namespace Vfs;
using namespace Noux; using namespace Noux;
@ -142,7 +145,8 @@ connect_stdio(Genode::Env &env,
return *new (alloc) return *new (alloc)
Vfs_io_channel(path.string(), root.leaf_path(path.string()), Vfs_io_channel(path.string(), root.leaf_path(path.string()),
vfs_handle, vfs_io_waiter_registry, env.ep()); vfs_handle, vfs_io_waiter_registry, env.ep(),
time_info, timer);
} }
@ -241,6 +245,8 @@ struct Noux::Main
User_info _user_info { _config.xml() }; User_info _user_info { _config.xml() };
Time_info _time_info { _env, _config.xml() };
Signal_handler<Main> _destruct_handler { Signal_handler<Main> _destruct_handler {
_env.ep(), *this, &Main::_handle_destruct }; _env.ep(), *this, &Main::_handle_destruct };
@ -273,6 +279,7 @@ struct Noux::Main
Noux::Child _init_child { _name_of_init_process(), Noux::Child _init_child { _name_of_init_process(),
_verbose, _verbose,
_user_info, _user_info,
_time_info,
0, 0,
_kill_broadcaster, _kill_broadcaster,
_timer_connection, _timer_connection,
@ -302,13 +309,13 @@ struct Noux::Main
Shared_pointer<Io_channel> Shared_pointer<Io_channel>
_channel_0 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, _channel_0 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir,
_io_response_handler.io_waiter_registry, _io_response_handler.io_waiter_registry,
Tio::STDIN, _heap), _heap }, Tio::STDIN, _heap, _time_info, _timer_connection), _heap },
_channel_1 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, _channel_1 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir,
_io_response_handler.io_waiter_registry, _io_response_handler.io_waiter_registry,
Tio::STDOUT, _heap), _heap }, Tio::STDOUT, _heap, _time_info, _timer_connection), _heap },
_channel_2 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, _channel_2 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir,
_io_response_handler.io_waiter_registry, _io_response_handler.io_waiter_registry,
Tio::STDERR, _heap), _heap }; Tio::STDERR, _heap, _time_info, _timer_connection), _heap };
Main(Env &env) : _env(env) Main(Env &env) : _env(env)
{ {

View File

@ -238,7 +238,9 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
leaf_path, leaf_path,
vfs_handle, vfs_handle,
_vfs_io_waiter_registry, _vfs_io_waiter_registry,
_env.ep()), _env.ep(),
_time_info,
_timer_connection),
_heap); _heap);
_sysio.open_out.fd = add_io_channel(channel); _sysio.open_out.fd = add_io_channel(channel);
@ -511,6 +513,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
child = new (_heap) Child(_child_policy.name(), child = new (_heap) Child(_child_policy.name(),
_verbose, _verbose,
_user_info, _user_info,
_time_info,
this, this,
_kill_broadcaster, _kill_broadcaster,
_timer_connection, _timer_connection,
@ -844,7 +847,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Milliseconds const ms = Milliseconds const ms =
_timer_connection.curr_time().trunc_to_plain_ms(); _timer_connection.curr_time().trunc_to_plain_ms();
_sysio.gettimeofday_out.sec = (ms.value / 1000); _sysio.gettimeofday_out.sec = _time_info.initial_time();
_sysio.gettimeofday_out.sec += (ms.value / 1000);
_sysio.gettimeofday_out.usec = (ms.value % 1000) * 1000; _sysio.gettimeofday_out.usec = (ms.value % 1000) * 1000;
result = true; result = true;
@ -864,7 +868,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
/* CLOCK_SECOND is used by time(3) in the libc. */ /* CLOCK_SECOND is used by time(3) in the libc. */
case Sysio::CLOCK_ID_SECOND: case Sysio::CLOCK_ID_SECOND:
{ {
_sysio.clock_gettime_out.sec = (ms.value / 1000); _sysio.clock_gettime_out.sec = _time_info.initial_time();
_sysio.clock_gettime_out.sec += (ms.value / 1000);
_sysio.clock_gettime_out.nsec = 0; _sysio.clock_gettime_out.nsec = 0;
result = true; result = true;
@ -889,13 +894,42 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
case SYSCALL_UTIMES: case SYSCALL_UTIMES:
{ {
/** /**
* This systemcall is currently not implemented because we lack * Always return true, even if 'update_modification_timestamp'
* the needed mechanisms in most file-systems. * failed to keep programs, e.g. make, happy.
*
* But we return true anyway to keep certain programs, e.g. make
* happy.
*/ */
result = true; result = true;
char const *path = (char const *)_sysio.utimes_in.path;
unsigned long sec = _sysio.utimes_in.sec;
unsigned long usec = _sysio.utimes_in.usec;
(void)usec;
Vfs::Vfs_handle *vfs_handle = 0;
_root_dir.open(path, 0, &vfs_handle, _heap);
if (!vfs_handle) { break; }
if (!sec) {
Milliseconds const ms =
_timer_connection.curr_time().trunc_to_plain_ms();
sec = _time_info.initial_time();
sec += (ms.value / 1000);
usec = (ms.value % 1000) * 1000;
}
Registered_no_delete<Vfs_io_waiter>
vfs_io_waiter(_vfs_io_waiter_registry);
Vfs::Timestamp ts { .value = (long long)sec };
for (;;) {
if (vfs_handle->fs().update_modification_timestamp(vfs_handle, ts)) {
break;
} else {
vfs_io_waiter.wait_for_io();
}
}
_root_dir.close(vfs_handle);
break; break;
} }

View File

@ -0,0 +1,104 @@
/*
* \brief Time information
* \author Josef Soentgen
* \date 2019-04-09
*/
/*
* Copyright (C) 2019 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.
*/
#ifndef _NOUX__TIME_INFO_H_
#define _NOUX__TIME_INFO_H_
/* Genode includes */
#include <util/string.h>
#include <util/xml_node.h>
#include <rtc_session/connection.h>
/* Noux includes */
#include <noux_session/sysio.h>
namespace Noux {
class Time_info;
using namespace Genode;
}
class Noux::Time_info : Noncopyable
{
private:
Constructible<Rtc::Connection> _rtc { };
Genode::int64_t _initial_time { 0 };
static bool _leap(unsigned year)
{
return ((year % 4) == 0
&& ((year % 100) != 0 || (year % 400) == 0));
}
/**
* Convert RTC timestamp to UNIX epoch (UTC)
*/
static Genode::int64_t _convert(Rtc::Timestamp const &ts)
{
if (ts.year < 1970) { return 0; }
/*
* Seconds per year lookup table
*/
static constexpr unsigned _secs_per_year[2] = {
365 * 86400, 366 * 86400,
};
/*
* Seconds per month lookup table
*/
static constexpr unsigned _sec_per_month[13] = {
0 * 86400,
31 * 86400, 28 * 86400, 31 * 86400, 30 * 86400,
31 * 86400, 30 * 86400, 31 * 86400, 31 * 86400,
30 * 86400, 31 * 86400, 30 * 86400, 31 * 86400
};
Genode::int64_t time = 0;
for (unsigned i = 1970; i < ts.year; i++) {
/* abuse bool conversion for seconds look up */
time += _secs_per_year[(int)_leap(i)];
}
for (unsigned i = 1; i < ts.month; i++) {
time += _sec_per_month[i];
}
time += _leap(ts.year) * 86400LL;
time += 86400LL * (ts.day-1);
time += 3600LL * ts.hour;
time += 60LL * ts.minute;
time += ts.second;
return time;
}
public:
Time_info(Env &env, Xml_node config)
{
/* only try to establish the connection on demand */
bool const rtc = config.attribute_value("rtc", false);
if (!rtc) { return; }
_rtc.construct(env);
_initial_time = _convert(_rtc->current_time());
}
Genode::int64_t initial_time() const { return _initial_time; }
};
#endif /* _NOUX__TIME_INFO_H_ */

View File

@ -14,9 +14,13 @@
#ifndef _NOUX__VFS_IO_CHANNEL_H_ #ifndef _NOUX__VFS_IO_CHANNEL_H_
#define _NOUX__VFS_IO_CHANNEL_H_ #define _NOUX__VFS_IO_CHANNEL_H_
/* Genode includes */
#include <timer_session/connection.h>
/* Noux includes */ /* Noux includes */
#include <io_channel.h> #include <io_channel.h>
#include <vfs/dir_file_system.h> #include <vfs/dir_file_system.h>
#include <time_info.h>
namespace Noux { namespace Noux {
class Vfs_io_waiter; class Vfs_io_waiter;
@ -69,11 +73,30 @@ struct Noux::Vfs_io_channel : Io_channel
bool const _dir = _fh.ds().directory(_leaf_path.base()); bool const _dir = _fh.ds().directory(_leaf_path.base());
Time_info const &_time_info;
Timer::Connection &_timer;
void _sync() void _sync()
{ {
Milliseconds const ms =
_timer.curr_time().trunc_to_plain_ms();
uint64_t sec = _time_info.initial_time();
sec += (ms.value / 1000);
Vfs::Timestamp ts { .value = (long long)sec };
Registered_no_delete<Vfs_io_waiter> Registered_no_delete<Vfs_io_waiter>
vfs_io_waiter(_vfs_io_waiter_registry); vfs_io_waiter(_vfs_io_waiter_registry);
for (;;) {
if (_fh.fs().update_modification_timestamp(&_fh, ts)) {
break;
} else {
Genode::error("_sync: update_modification_timestamp failed");
vfs_io_waiter.wait_for_io();
}
}
while (!_fh.fs().queue_sync(&_fh)) while (!_fh.fs().queue_sync(&_fh))
vfs_io_waiter.wait_for_io(); vfs_io_waiter.wait_for_io();
@ -89,11 +112,13 @@ struct Noux::Vfs_io_channel : Io_channel
Vfs_io_channel(char const *path, char const *leaf_path, Vfs_io_channel(char const *path, char const *leaf_path,
Vfs::Vfs_handle *vfs_handle, Vfs::Vfs_handle *vfs_handle,
Vfs_io_waiter_registry &vfs_io_waiter_registry, Vfs_io_waiter_registry &vfs_io_waiter_registry,
Entrypoint &ep) Entrypoint &ep,
Time_info const &time_info,
Timer::Connection &timer)
: :
_read_avail_handler(ep, *this, &Vfs_io_channel::_handle_read_avail), _read_avail_handler(ep, *this, &Vfs_io_channel::_handle_read_avail),
_fh(*vfs_handle), _vfs_io_waiter_registry(vfs_io_waiter_registry), _fh(*vfs_handle), _vfs_io_waiter_registry(vfs_io_waiter_registry),
_path(path), _leaf_path(leaf_path) _path(path), _leaf_path(leaf_path), _time_info(time_info), _timer(timer)
{ {
_fh.fs().register_read_ready_sigh(&_fh, _read_avail_handler); _fh.fs().register_read_ready_sigh(&_fh, _read_avail_handler);
} }