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

View File

@ -7,3 +7,4 @@ noux_session
terminal_session
timer_session
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_ino = sysio->stat_out.st.inode;
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)
{
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)) {
errno = EINVAL;
return -1;

View File

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

View File

@ -27,6 +27,7 @@
#include <kill_broadcaster.h>
#include <vfs/dir_file_system.h>
#include <vfs/simple_env.h>
#include <time_info.h>
namespace Noux {
@ -98,7 +99,9 @@ connect_stdio(Genode::Env &env,
Vfs::File_system &root,
Noux::Vfs_io_waiter_registry &vfs_io_waiter_registry,
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 Noux;
@ -142,7 +145,8 @@ connect_stdio(Genode::Env &env,
return *new (alloc)
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() };
Time_info _time_info { _env, _config.xml() };
Signal_handler<Main> _destruct_handler {
_env.ep(), *this, &Main::_handle_destruct };
@ -273,6 +279,7 @@ struct Noux::Main
Noux::Child _init_child { _name_of_init_process(),
_verbose,
_user_info,
_time_info,
0,
_kill_broadcaster,
_timer_connection,
@ -302,13 +309,13 @@ struct Noux::Main
Shared_pointer<Io_channel>
_channel_0 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir,
_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,
_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,
_io_response_handler.io_waiter_registry,
Tio::STDERR, _heap), _heap };
Tio::STDERR, _heap, _time_info, _timer_connection), _heap };
Main(Env &env) : _env(env)
{

View File

@ -238,7 +238,9 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
leaf_path,
vfs_handle,
_vfs_io_waiter_registry,
_env.ep()),
_env.ep(),
_time_info,
_timer_connection),
_heap);
_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(),
_verbose,
_user_info,
_time_info,
this,
_kill_broadcaster,
_timer_connection,
@ -844,7 +847,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Milliseconds const 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;
result = true;
@ -864,7 +868,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
/* CLOCK_SECOND is used by time(3) in the libc. */
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;
result = true;
@ -889,13 +894,42 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
case SYSCALL_UTIMES:
{
/**
* This systemcall is currently not implemented because we lack
* the needed mechanisms in most file-systems.
*
* But we return true anyway to keep certain programs, e.g. make
* happy.
* Always return true, even if 'update_modification_timestamp'
* failed to keep programs, e.g. make, happy.
*/
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;
}

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_
#define _NOUX__VFS_IO_CHANNEL_H_
/* Genode includes */
#include <timer_session/connection.h>
/* Noux includes */
#include <io_channel.h>
#include <vfs/dir_file_system.h>
#include <time_info.h>
namespace Noux {
class Vfs_io_waiter;
@ -69,11 +73,30 @@ struct Noux::Vfs_io_channel : Io_channel
bool const _dir = _fh.ds().directory(_leaf_path.base());
Time_info const &_time_info;
Timer::Connection &_timer;
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>
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))
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::Vfs_handle *vfs_handle,
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),
_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);
}