Support adequate wake up timer (linux)
Systemd v254 (next release) will set cap_wake_alarm
capability for local user sessions by default (merge request link), enabling CLOCK_*_ALARM
option in timerfd_create(2)
syscalls for applications.
Which causes kernel to automatically set a wake up timer before suspend or shutdown to catch expiring userspace timers.
Thus providing a robust, concurrent and painless interface for wake up scheduling, compared to blind altering of a hardware RTC via rtcwake
.
Example
Required capability can be set with capsh(1)
.
$ capsh --current
Current: =
Current IAB:
$ sudo capsh --user=$USER --{caps,inh,addamb}='cap_wake_alarm+epi' --current -c "<bin>"
Current: cap_wake_alarm=eip
Current IAB: ^cap_wake_alarm
$ sudo capsh --user=$USER --{caps,inh,addamb}='cap_wake_alarm+epi' -- -c "time ./a.out"
Code in C
/*
* System wake up example using POSIX-like timer (linux only)
*
* Program sets a timer and then waits for an alarm.
* Note that `CAP_WAKE_ALARM` capability must be set.
*/
#include <assert.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/timerfd.h>
int main(int argc, char* argv[]) {
time_t seconds = 0;
if (argc > 1) {
seconds = atol(argv[1]);
}
// `timerfd_create(2)`
int timer_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0);
if (timer_fd < 0) {
perror("timerfd_create");
// `EPERM` - `CAP_WAKE_ALARM` capability is not set
// `EINVAL` - Alarm clock is not supported
return errno;
}
// Relative expiration time
struct itimerspec time = {
.it_value.tv_sec = seconds,
};
if (seconds == 0) {
printf("Alarm will never ring\n");
}
// `timerfd_settime(2)`
if (timerfd_settime(timer_fd, 0, &time, 0) != 0) {
perror("timerfd_settime");
return errno;
}
// Wait for the alarm
uint64_t expirations = 0;
assert(read(timer_fd, (char*)&expirations, sizeof(expirations)) == sizeof(expirations));
close(timer_fd);
return 0;
}
Edited by one-d-wide