timer.c (3053B)
- // SPDX-FileCopyrightText: 2025 Haelwenn (lanodan) Monnier <contact+cmd-timer@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include "strtodur.h"
- #include <errno.h>
- #include <limits.h> // UINT_MAX
- #include <signal.h> // sigaction
- #include <spawn.h> // posix_spawnp
- #include <stdio.h> // fprintf, fputs
- #include <stdlib.h> // exit
- #include <string.h> // strerror
- #include <sys/wait.h> // waitpid
- #include <time.h> // timer_create
- #include <unistd.h> // sleep
- extern char **environ;
- static char **args;
- const char *argv0 = "timer";
- static void
- sig_timer(int sig)
- {
- pid_t child = -1;
- errno = 0;
- int ret = posix_spawnp(&child, args[0], NULL, NULL, args, environ);
- if(ret != 0)
- {
- fprintf(stderr, "timer: error: Failed spawning command '%s': %s\n", args[0], strerror(ret));
- exit(1);
- }
- }
- static void
- sig_chld(int sig)
- {
- int chld_stat = 0;
- waitpid((pid_t)-1, &chld_stat, WNOHANG);
- if(!WIFEXITED(chld_stat)) return;
- if(WEXITSTATUS(chld_stat) != 0) exit(WEXITSTATUS(chld_stat));
- }
- static void
- bad_usage()
- {
- fputs("Usage: timer <interval> <command> [arguments...]\n", stderr);
- exit(1);
- }
- int
- main(int argc, char *argv[])
- {
- argc--;
- argv++;
- if(argc < 2)
- {
- fprintf(stderr, "timer: error: Got '%d' arguments instead of at least 2\n", argc);
- bad_usage();
- }
- struct timespec dur = {
- .tv_sec = 0,
- .tv_nsec = 0,
- };
- if(strtodur(*argv, &dur) != 0) return 1;
- if(dur.tv_sec == 0 && dur.tv_nsec == 0)
- {
- fputs("timer: error: Got a duration of 0\n", stderr);
- return 1;
- }
- argc--;
- argv++;
- struct sigaction sa_alrm;
- sa_alrm.sa_handler = &sig_timer;
- sigemptyset(&sa_alrm.sa_mask);
- sa_alrm.sa_flags = SA_RESTART;
- if(sigaction(SIGALRM, &sa_alrm, NULL) != 0)
- {
- fprintf(stderr, "timer: error: Failed registering signal handler: %s\n", strerror(errno));
- return 1;
- }
- struct sigaction sa_chld;
- sa_chld.sa_handler = &sig_chld;
- sigemptyset(&sa_chld.sa_mask);
- sa_chld.sa_flags = SA_RESTART;
- if(sigaction(SIGCHLD, &sa_chld, NULL) != 0)
- {
- fprintf(stderr, "timer: error: Failed registering signal handler: %s\n", strerror(errno));
- return 1;
- }
- struct sigevent timer_se;
- timer_se.sigev_signo = SIGALRM;
- timer_se.sigev_notify = SIGEV_SIGNAL;
- timer_t timer;
- if(timer_create(0, &timer_se, &timer) != 0)
- {
- fprintf(stderr, "timer: error: Failed creating timer: %s\n", strerror(errno));
- return 1;
- }
- struct itimerspec timerspec = {
- .it_value = dur,
- .it_interval = dur,
- };
- if(timer_settime(timer, 0, &timerspec, NULL) != 0)
- {
- fprintf(stderr, "timer: error: Failed setting timer: %s\n", strerror(errno));
- return 1;
- }
- args = argv;
- sigset_t sigmask;
- sigfillset(&sigmask);
- if(sigdelset(&sigmask, SIGALRM) != 0)
- {
- fprintf(stderr, "timer: error: Failed adding SIGALARM to new sigmask: %s\n", strerror(errno));
- return 1;
- }
- if(sigdelset(&sigmask, SIGCHLD) != 0)
- {
- fprintf(stderr, "timer: error: Failed adding SIGCHLD to new sigmask: %s\n", strerror(errno));
- return 1;
- }
- while(sigsuspend(&sigmask))
- ;
- return 0;
- }