From 1d9e9112298ac898ceb39feb919a72484adc22e7 Mon Sep 17 00:00:00 2001 From: Fred Dushin Date: Sat, 8 Jul 2023 17:03:14 -0400 Subject: [PATCH] Add support for `atomvm:posix_clock_settime/2` Signed-off-by: Fred Dushin --- CHANGELOG.md | 1 + doc/src/programmers-guide.md | 10 ++++++- libs/eavmlib/src/atomvm.erl | 25 ++++++++++++++++- src/libAtomVM/CMakeLists.txt | 1 + src/libAtomVM/nifs.c | 53 ++++++++++++++++++++++++++++++++++++ src/libAtomVM/nifs.gperf | 1 + 6 files changed, 89 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca8158bb4..5d39c727a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added heap growth strategies as a fine-tuning option to `spawn_opt/2,4` - Added `crypto:crypto_one_time/4,5` on ESP32 - Improved nif and port support on STM32 +- Added support for `atomvm:posix_clock_settime/2` ### Fixed diff --git a/doc/src/programmers-guide.md b/doc/src/programmers-guide.md index b9238f528..07836e637 100644 --- a/doc/src/programmers-guide.md +++ b/doc/src/programmers-guide.md @@ -581,7 +581,15 @@ Use `erlang:universaltime/0` to get the current time at second resolution, to ob %% erlang {{Year, Month, Day}, {Hour, Minute, Second}} = erlang:universaltime(). -> Note. Setting the system time is done in a platform-specific manner. For information about how to set system time on the ESP32, see the [Network Programming Guide](./network-programming-guide.md). +On some platforms, you can use the `atomvm:posix_clock_settime/2` to set the system time. Supply a clock id (currently, the only supported clock id is the atom `realtime`) and a time value as a tuple, containing seconds and nanoseconds since the UNIX epoch (midnight, January 1, 1970). For example, + + %% erlang + SecondsSinceUnixEpoch = ... %% acquire the time + atomvm:posix_clock_settime(realtime, {SecondsSinceUnixEpoch, 0}) + +> Note. This operation is not supported yet on the `stm32` platform. On most UNIX platforms, you typically need `root` permission to set the system time. + +On the ESP32 platform, you can use the Wifi network to set the system time automatically. For information about how to set system time on the ESP32 using SNTP, see the [Network Programming Guide](./network-programming-guide.md). To convert a time (in seconds, milliseconds, or microseconds from the UNIX epoch) to a date-time, use the `calendar:system_time_to_universal_time/2` function. For example, diff --git a/libs/eavmlib/src/atomvm.erl b/libs/eavmlib/src/atomvm.erl index 3d14681f1..0ccaf6271 100644 --- a/libs/eavmlib/src/atomvm.erl +++ b/libs/eavmlib/src/atomvm.erl @@ -39,7 +39,8 @@ posix_open/3, posix_close/1, posix_read/2, - posix_write/2 + posix_write/2, + posix_clock_settime/2 ]). -export_type([ @@ -267,3 +268,25 @@ posix_read(_File, _Count) -> {ok, non_neg_integer()} | {error, posix_error()}. posix_write(_File, _Data) -> erlang:nif_error(undefined). + +%% +%% @param ClockId The clock id +%% @param ValueSinceUnixEpoch The value, in specified seconds and nanoseconds, +%% since the UNIX epoch (Jan 1, 1970) +%% @return `ok' or an error tuple +%% @doc Set the system time. +%% +%% This function sets the system time to the specified value, expressed as a +%% tuple containing seconds and nanoseconds since the UNIX epoch (Jan 1, 1970). +%% Coordinates are all in UTC. +%% +%% Note. Some systems may require special permissions to call this function. +%% @end +%% +-spec posix_clock_settime( + ClockId :: realtime, + ValueSinceUnixEpoch :: {Seconds :: integer(), Nanoseconds :: integer()} +) -> + ok | {error, Reason :: posix_error()}. +posix_clock_settime(_ClockId, _Time) -> + erlang:nif_error(undefined). diff --git a/src/libAtomVM/CMakeLists.txt b/src/libAtomVM/CMakeLists.txt index a5e99d20c..f003ee3c4 100644 --- a/src/libAtomVM/CMakeLists.txt +++ b/src/libAtomVM/CMakeLists.txt @@ -166,6 +166,7 @@ define_if_symbol_exists(libAtomVM O_NOFOLLOW "fcntl.h" PRIVATE HAVE_O_NOFOLLOW) define_if_symbol_exists(libAtomVM O_RSYNC "fcntl.h" PRIVATE HAVE_O_RSYNC) define_if_symbol_exists(libAtomVM O_SEARCH "fcntl.h" PRIVATE HAVE_O_SEARCH) define_if_symbol_exists(libAtomVM O_TTY_INIT "fcntl.h" PRIVATE HAVE_O_TTY_INIT) +define_if_symbol_exists(libAtomVM clock_settime "sys/time.h" PRIVATE HAVE_CLOCK_SETTIME) if (AVM_USE_32BIT_FLOAT) target_compile_definitions(libAtomVM PUBLIC AVM_USE_SINGLE_PRECISION) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index e8fc9a5f1..f377c1e4f 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -24,6 +24,7 @@ #include "nifs.h" +#include #include #include #include @@ -158,6 +159,7 @@ static term nif_atomvm_add_avm_pack_file(Context *ctx, int argc, term argv[]); static term nif_atomvm_close_avm_pack(Context *ctx, int argc, term argv[]); static term nif_atomvm_get_start_beam(Context *ctx, int argc, term argv[]); static term nif_atomvm_read_priv(Context *ctx, int argc, term argv[]); +static term nif_atomvm_posix_clock_settime(Context *ctx, int argc, term argv[]); static term nif_console_print(Context *ctx, int argc, term argv[]); static term nif_base64_encode(Context *ctx, int argc, term argv[]); static term nif_base64_decode(Context *ctx, int argc, term argv[]); @@ -658,6 +660,11 @@ static const struct Nif atomvm_read_priv_nif = .base.type = NIFFunctionType, .nif_ptr = nif_atomvm_read_priv }; +static const struct Nif atomvm_posix_clock_settime_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_atomvm_posix_clock_settime +}; static const struct Nif console_print_nif = { .base.type = NIFFunctionType, @@ -3803,6 +3810,52 @@ static term nif_atomvm_read_priv(Context *ctx, int argc, term argv[]) return result; } +static term nif_atomvm_posix_clock_settime(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_atom); + if (globalcontext_is_term_equal_to_atom_string(ctx->global, argv[0], ATOM_STR("\x8", "realtime"))) { + RAISE_ERROR(BADARG_ATOM); + } + + VALIDATE_VALUE(argv[1], term_is_tuple); + if (term_get_tuple_arity(argv[1]) != 2) { + RAISE_ERROR(BADARG_ATOM); + } + +#ifdef HAVE_CLOCK_SETTIME + + term secs = term_get_tuple_element(argv[1], 0); + VALIDATE_VALUE(secs, term_is_any_integer); + avm_int64_t s = term_maybe_unbox_int64(secs); + + term nsecs = term_get_tuple_element(argv[1], 1); + VALIDATE_VALUE(nsecs, term_is_any_integer); + avm_int64_t ns = term_maybe_unbox_int64(nsecs); + + struct timespec tp = { + .tv_sec = s, + .tv_nsec = ns + }; + + int res = clock_settime(CLOCK_REALTIME, &tp); + if (res != 0) { + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term error = term_alloc_tuple(2, &ctx->heap); + term_put_tuple_element(error, 0, ERROR_ATOM); + term_put_tuple_element(error, 1, posix_errno_to_term(errno, ctx->global)); + return error; + } else { + return OK_ATOM; + } +#else + RAISE_ERROR(UNDEF_ATOM); +#endif +} + static term nif_console_print(Context *ctx, int argc, term argv[]) { UNUSED(argc); diff --git a/src/libAtomVM/nifs.gperf b/src/libAtomVM/nifs.gperf index 27b4d92bd..92f322fee 100644 --- a/src/libAtomVM/nifs.gperf +++ b/src/libAtomVM/nifs.gperf @@ -133,6 +133,7 @@ atomvm:posix_select_write/3, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_write_nif) atomvm:posix_select_stop/1, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_stop_nif) atomvm:posix_mkfifo/2, IF_HAVE_MKFIFO(&atomvm_posix_mkfifo_nif) atomvm:posix_unlink/1, IF_HAVE_UNLINK(&atomvm_posix_unlink_nif) +atomvm:posix_clock_settime/2, &atomvm_posix_clock_settime_nif code:load_abs/1, &code_load_abs_nif code:load_binary/3, &code_load_binary_nif console:print/1, &console_print_nif