From 94b63924edf2e4a0dec9d99c5f446895cb385e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= Date: Tue, 9 Apr 2019 17:52:44 +0200 Subject: [PATCH] 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 '' node must be set to 'yes'. Thereby the session becomes a mandatory dependency as Noux will not start without it. Issue #1784 --- repos/ports/include/noux_session/sysio.h | 2 + repos/ports/recipes/src/noux/used_apis | 1 + repos/ports/src/lib/libc_noux/plugin.cc | 10 +++ repos/ports/src/noux/child.h | 6 ++ repos/ports/src/noux/main.cc | 17 ++-- repos/ports/src/noux/syscall.cc | 50 +++++++++-- repos/ports/src/noux/time_info.h | 104 +++++++++++++++++++++++ repos/ports/src/noux/vfs_io_channel.h | 29 ++++++- 8 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 repos/ports/src/noux/time_info.h diff --git a/repos/ports/include/noux_session/sysio.h b/repos/ports/include/noux_session/sysio.h index ae2edc605..f9d9fe846 100644 --- a/repos/ports/include/noux_session/sysio.h +++ b/repos/ports/include/noux_session/sysio.h @@ -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; } diff --git a/repos/ports/recipes/src/noux/used_apis b/repos/ports/recipes/src/noux/used_apis index 0d8514f83..ddb9ebeff 100644 --- a/repos/ports/recipes/src/noux/used_apis +++ b/repos/ports/recipes/src/noux/used_apis @@ -7,3 +7,4 @@ noux_session terminal_session timer_session posix +rtc_session diff --git a/repos/ports/src/lib/libc_noux/plugin.cc b/repos/ports/src/lib/libc_noux/plugin.cc index 9a5dbd32f..41e489c7b 100644 --- a/repos/ports/src/lib/libc_noux/plugin.cc +++ b/repos/ports/src/lib/libc_noux/plugin.cc @@ -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; diff --git a/repos/ports/src/noux/child.h b/repos/ports/src/noux/child.h index 3c13d3e8c..06acf98b0 100644 --- a/repos/ports/src/noux/child.h +++ b/repos/ports/src/noux/child.h @@ -37,6 +37,7 @@ #include #include #include +#include namespace Noux { @@ -124,6 +125,8 @@ class Noux::Child : public Rpc_object, 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, 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, _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, Child *child = new (_heap) Child(filename, _verbose, _user_info, + _time_info, _parent_exit, _kill_broadcaster, _timer_connection, diff --git a/repos/ports/src/noux/main.cc b/repos/ports/src/noux/main.cc index ca4f77c57..3fc8b2359 100644 --- a/repos/ports/src/noux/main.cc +++ b/repos/ports/src/noux/main.cc @@ -27,6 +27,7 @@ #include #include #include +#include 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
_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 _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) { diff --git a/repos/ports/src/noux/syscall.cc b/repos/ports/src/noux/syscall.cc index 2cabfedaf..093674a2d 100644 --- a/repos/ports/src/noux/syscall.cc +++ b/repos/ports/src/noux/syscall.cc @@ -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_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; } diff --git a/repos/ports/src/noux/time_info.h b/repos/ports/src/noux/time_info.h new file mode 100644 index 000000000..df666fa2e --- /dev/null +++ b/repos/ports/src/noux/time_info.h @@ -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 +#include +#include + +/* Noux includes */ +#include + +namespace Noux { + class Time_info; + using namespace Genode; +} + + +class Noux::Time_info : Noncopyable +{ + private: + + Constructible _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_ */ diff --git a/repos/ports/src/noux/vfs_io_channel.h b/repos/ports/src/noux/vfs_io_channel.h index 8290088a7..e8fb0f2d9 100644 --- a/repos/ports/src/noux/vfs_io_channel.h +++ b/repos/ports/src/noux/vfs_io_channel.h @@ -14,9 +14,13 @@ #ifndef _NOUX__VFS_IO_CHANNEL_H_ #define _NOUX__VFS_IO_CHANNEL_H_ +/* Genode includes */ +#include + /* Noux includes */ #include #include +#include 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_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); }