diff --git a/repos/gems/run/depot_autopilot.run b/repos/gems/run/depot_autopilot.run
index cd043e3e61..f97c01e993 100644
--- a/repos/gems/run/depot_autopilot.run
+++ b/repos/gems/run/depot_autopilot.run
@@ -681,6 +681,7 @@ set default_test_pkgs {
test-libc_fifo_pipe
test-libc_fork
test-libc_getenv
+ test-libc_alarm
test-libc_pipe
test-libc_vfs
test-libc_vfs_audit
diff --git a/repos/libports/lib/mk/libc.mk b/repos/libports/lib/mk/libc.mk
index e491954a97..687b903433 100644
--- a/repos/libports/lib/mk/libc.mk
+++ b/repos/libports/lib/mk/libc.mk
@@ -12,7 +12,7 @@ LIBS += base vfs
# Back end
#
SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc \
- issetugid.cc errno.cc gai_strerror.cc time.cc \
+ issetugid.cc errno.cc gai_strerror.cc time.cc alarm.cc \
malloc.cc progname.cc fd_alloc.cc file_operations.cc \
plugin.cc plugin_registry.cc select.cc exit.cc environ.cc sleep.cc \
pread_pwrite.cc readv_writev.cc poll.cc \
diff --git a/repos/libports/recipes/pkg/test-libc_alarm/README b/repos/libports/recipes/pkg/test-libc_alarm/README
new file mode 100644
index 0000000000..77680bcf90
--- /dev/null
+++ b/repos/libports/recipes/pkg/test-libc_alarm/README
@@ -0,0 +1 @@
+Libc alarm() test.
diff --git a/repos/libports/recipes/pkg/test-libc_alarm/archives b/repos/libports/recipes/pkg/test-libc_alarm/archives
new file mode 100644
index 0000000000..170cda551e
--- /dev/null
+++ b/repos/libports/recipes/pkg/test-libc_alarm/archives
@@ -0,0 +1,5 @@
+_/src/init
+_/src/test-libc_alarm
+_/src/libc
+_/src/posix
+_/src/vfs
diff --git a/repos/libports/recipes/pkg/test-libc_alarm/hash b/repos/libports/recipes/pkg/test-libc_alarm/hash
new file mode 100644
index 0000000000..a6e2dbe4ac
--- /dev/null
+++ b/repos/libports/recipes/pkg/test-libc_alarm/hash
@@ -0,0 +1 @@
+2024-08-30 ca6e62da4a88ade9338dea365858d83409150c78
diff --git a/repos/libports/recipes/pkg/test-libc_alarm/runtime b/repos/libports/recipes/pkg/test-libc_alarm/runtime
new file mode 100644
index 0000000000..24bf4b4bce
--- /dev/null
+++ b/repos/libports/recipes/pkg/test-libc_alarm/runtime
@@ -0,0 +1,22 @@
+
+
+
+
+
+ triggered_alarms=3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/libports/recipes/src/test-libc_alarm/content.mk b/repos/libports/recipes/src/test-libc_alarm/content.mk
new file mode 100644
index 0000000000..7a6c4ee906
--- /dev/null
+++ b/repos/libports/recipes/src/test-libc_alarm/content.mk
@@ -0,0 +1,2 @@
+SRC_DIR := src/test/libc_alarm
+include $(GENODE_DIR)/repos/base/recipes/src/content.inc
diff --git a/repos/libports/recipes/src/test-libc_alarm/hash b/repos/libports/recipes/src/test-libc_alarm/hash
new file mode 100644
index 0000000000..0a11eb8f7c
--- /dev/null
+++ b/repos/libports/recipes/src/test-libc_alarm/hash
@@ -0,0 +1 @@
+2024-08-30 49e1a2b9ef6049be7c0fcee964324fbda54cbed7
diff --git a/repos/libports/recipes/src/test-libc_alarm/used_apis b/repos/libports/recipes/src/test-libc_alarm/used_apis
new file mode 100644
index 0000000000..0c483273a8
--- /dev/null
+++ b/repos/libports/recipes/src/test-libc_alarm/used_apis
@@ -0,0 +1,2 @@
+libc
+posix
diff --git a/repos/libports/src/lib/libc/alarm.cc b/repos/libports/src/lib/libc/alarm.cc
new file mode 100644
index 0000000000..250de9a3d6
--- /dev/null
+++ b/repos/libports/src/lib/libc/alarm.cc
@@ -0,0 +1,118 @@
+/*
+ * \brief Libc interval timer
+ * \author Norman Feske
+ * \date 2024-08-00
+ */
+
+/*
+ * Copyright (C) 2024 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
+
+/* libc-internal includes */
+#include
+#include
+#include
+#include
+
+
+static Libc::Timer_accessor *_timer_accessor_ptr;
+static Libc::Signal *_signal_ptr;
+
+void Libc::init_alarm(Timer_accessor &timer_accessor, Signal &signal)
+{
+ _timer_accessor_ptr = &timer_accessor;
+ _signal_ptr = &signal;
+}
+
+
+namespace Libc { struct Itimer_real; }
+
+
+struct Libc::Itimer_real : Noncopyable
+{
+ struct Handler : Timeout_handler
+ {
+ Signal &_signal;
+
+ virtual void handle_timeout() override { _signal.charge(SIGALRM); }
+
+ Handler(Signal &signal) : _signal(signal) { }
+
+ } _handler;
+
+ Timer_accessor &_timer_accessor;
+
+ Constructible _timeout { };
+
+ void arm_or_disarm(timeval tv)
+ {
+ Libc::uint64_t const ms = tv.tv_sec*1000 + tv.tv_usec/1000;
+
+ if (ms) {
+ _timeout.construct(_timer_accessor, _handler);
+ _timeout->start(ms);
+ } else {
+ _timeout.destruct();
+ }
+ }
+
+ timeval current()
+ {
+ if (!_timeout.constructed())
+ return { };
+
+ Libc::uint64_t const ms = _timeout->duration_left();
+
+ return { .tv_sec = long(ms/1000),
+ .tv_usec = long((ms % 1000)*1000) };
+ }
+
+ Itimer_real(Timer_accessor &timer_accessor, Signal &signal)
+ :
+ _handler(signal), _timer_accessor(timer_accessor)
+ { }
+};
+
+
+using namespace Libc;
+
+
+static Itimer_real &itimer_real()
+{
+ struct Missing_call_of_init_alarm : Exception { };
+ if (!_timer_accessor_ptr || !_signal_ptr)
+ throw Missing_call_of_init_alarm();
+
+ static Itimer_real itimer { *_timer_accessor_ptr, *_signal_ptr };
+ return itimer;
+}
+
+
+extern "C" int setitimer(int which, const itimerval *new_value, itimerval *old_value)
+{
+ if (which != ITIMER_REAL) {
+ warning("setitimer: timer %d unsupported");
+ return Errno(EINVAL);
+ }
+
+ if (!new_value)
+ return Errno(EFAULT);
+
+ if (new_value->it_interval.tv_sec || new_value->it_interval.tv_usec)
+ warning("setitimer: argument 'new_value->it_interval' not handled");
+
+ if (old_value) {
+ old_value->it_interval = { };
+ old_value->it_value = itimer_real().current();
+ }
+
+ itimer_real().arm_or_disarm(new_value->it_value);
+
+ return 0;
+}
diff --git a/repos/libports/src/lib/libc/dummies.cc b/repos/libports/src/lib/libc/dummies.cc
index 8d3606d5ac..1f55acbd58 100644
--- a/repos/libports/src/lib/libc/dummies.cc
+++ b/repos/libports/src/lib/libc/dummies.cc
@@ -141,7 +141,6 @@ DUMMY(int , -1, seteuid, (uid_t))
DUMMY(int , -1, setgid, (gid_t))
DUMMY(int , -1, setuid, (uid_t))
DUMMY(int , -1, setgroups, (int, const gid_t *))
-DUMMY(int , -1, setitimer, (int, const itimerval *, itimerval *))
DUMMY(int , -1, setpgid, (pid_t, pid_t))
DUMMY(int , -1, setpriority, (int, int, int))
DUMMY(int , -1, setregid, (gid_t, gid_t))
diff --git a/repos/libports/src/lib/libc/internal/init.h b/repos/libports/src/lib/libc/internal/init.h
index 433ff7136c..bd3d880bb9 100644
--- a/repos/libports/src/lib/libc/internal/init.h
+++ b/repos/libports/src/lib/libc/internal/init.h
@@ -108,6 +108,7 @@ namespace Libc {
*/
void init_sleep(Monitor &);
void init_time(Current_time &, Current_real_time &);
+ void init_alarm(Timer_accessor &, Signal &);
/**
* Socket fs
diff --git a/repos/libports/src/lib/libc/internal/signal.h b/repos/libports/src/lib/libc/internal/signal.h
index e3675f9259..823c352f83 100644
--- a/repos/libports/src/lib/libc/internal/signal.h
+++ b/repos/libports/src/lib/libc/internal/signal.h
@@ -16,10 +16,13 @@
/* Genode includes */
#include
+#include
#include
+#include
/* libc includes */
#include
+#include
/* libc-internal includes */
#include
diff --git a/repos/libports/src/lib/libc/kernel.cc b/repos/libports/src/lib/libc/kernel.cc
index e37fee0a93..be38ebe987 100644
--- a/repos/libports/src/lib/libc/kernel.cc
+++ b/repos/libports/src/lib/libc/kernel.cc
@@ -508,6 +508,7 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap)
init_vfs_plugin(*this, _env.rm());
init_file_operations(*this, _libc_env);
init_time(*this, *this);
+ init_alarm(_timer_accessor, _signal);
init_poll(_signal, *this);
init_select(*this);
init_socket_fs(*this, *this);
diff --git a/repos/libports/src/test/libc_alarm/main.c b/repos/libports/src/test/libc_alarm/main.c
new file mode 100644
index 0000000000..e5a61d9c56
--- /dev/null
+++ b/repos/libports/src/test/libc_alarm/main.c
@@ -0,0 +1,52 @@
+/*
+ * \brief Libc alarm test
+ * \author Norman Feske
+ * \date 2024-08-30
+ */
+
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#include
+#include
+#include
+
+static unsigned triggered_alarms;
+
+static void sigalarm_handler(int)
+{
+ triggered_alarms++;
+}
+
+int main(int, char **)
+{
+ static struct sigaction sa;
+ sa.sa_handler = sigalarm_handler;
+
+ int ret = sigaction(SIGALRM, &sa, NULL);
+ if (ret < 0) {
+ printf("sigaction unexpectedly returned %d\n", ret);
+ return 1;
+ }
+
+ signal(SIGALRM, sigalarm_handler);
+
+ unsigned observed_alarms = 0;
+
+ alarm(2);
+
+ while (observed_alarms != 3) {
+ sleep(1);
+ printf("triggered_alarms=%u\n", triggered_alarms);
+
+ if (triggered_alarms != observed_alarms) {
+ observed_alarms = triggered_alarms;
+ alarm(2);
+ }
+ }
+ return 0;
+}
diff --git a/repos/libports/src/test/libc_alarm/target.mk b/repos/libports/src/test/libc_alarm/target.mk
new file mode 100644
index 0000000000..77a61b5d6e
--- /dev/null
+++ b/repos/libports/src/test/libc_alarm/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-libc_alarm
+SRC_C = main.c
+LIBS = posix