logo

cmd-timer

run command at a specific interval git clone https://anongit.hacktivis.me/git/cmd-timer.git
commit: 2e7604927c930e333d46a4b4c061db4601b8b672
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Sun, 15 Jun 2025 23:51:14 +0200

init

Diffstat:

A.clang-format23+++++++++++++++++++++++
A.gitignore4++++
ALICENSES/MIT.txt18++++++++++++++++++
AMakefile20++++++++++++++++++++
Atimer.c110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 175 insertions(+), 0 deletions(-)

diff --git a/.clang-format b/.clang-format @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2025 Haelwenn (lanodan) Monnier <contact+cmd-timer@hacktivis.me> +# SPDX-License-Identifier: MIT +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: false +AlignOperands: true +AlignTrailingComments: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AlwaysBreakAfterReturnType: AllDefinitions +BinPackArguments: false +BinPackParameters: false +BreakBeforeBraces: Allman +SpaceBeforeParens: Never +IncludeBlocks: Regroup +ReflowComments: false +SortIncludes: true +UseTab: ForIndentation +IndentWidth: 2 +TabWidth: 2 +ColumnLimit: 100 + +NamespaceIndentation: All diff --git a/.gitignore b/.gitignore @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2025 Haelwenn (lanodan) Monnier <contact+cmd-timer@hacktivis.me> +# SPDX-License-Identifier: MIT + +/timer diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) <year> <copyright holders> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2025 Haelwenn (lanodan) Monnier <contact+cmd-timer@hacktivis.me> +# SPDX-License-Identifier: MIT + +PREFIX ?= /usr/local +BINDIR ?= ${PREFIX}/bin + +timer: timer.c + ${CC} -std=c99 ${CFLAGS} -o $@ $< ${LDFLAGS} $[LDSTATIC} + +clean: + rm -f timer + +.PHONY: format +format: + clang-format -style=file -assume-filename=.clang-format -i timer.c + +.PHONY: install +install: + mkdir -p ${DESTDIR}${BINDIR}/ + cp -p ./timer ${DESTDIR}${BINDIR}/timer diff --git a/timer.c b/timer.c @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: 2025 Haelwenn (lanodan) Monnier <contact+cmd-timer@hacktivis.me> +// SPDX-License-Identifier: MIT + +#define _POSIX_C_SOURCE 200809L +#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 <time.h> // timer_create +#include <unistd.h> // sleep + +extern char **environ; +static char **args; + +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 +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(); + } + + char *durend = NULL; + errno = 0; + unsigned long dur = strtoul(*argv, &durend, 0); + if(errno != 0) + { + fprintf(stderr, + "timer: error: Failed parsing argument '%s' as integer: %s\n", + *argv, + strerror(errno)); + return 1; + } + + if(dur == 0) + { + fputs("timer: error: Got a duration of 0\n", stderr); + return 1; + } + + argc--; + argv++; + + struct sigaction sa; + sa.sa_handler = &sig_timer; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if(sigaction(SIGALRM, &sa, 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; + + while(sigsuspend(&sa.sa_mask)) + ; + + return 0; +}