rspan

Executes a given command after a randomized time span.
git clone git://git.noxz.tech/rspan
Log | Files | Refs | LICENSE

commit 10929d8c1438e91d3166aaccf5ae7153167cf7a6
Author: Chris Noxz <chris@noxz.tech>
Date:   Thu,  3 Jan 2019 19:12:32 +0100

Initial commit

Diffstat:
A.gitignore | 2++
AMakefile | 41+++++++++++++++++++++++++++++++++++++++++
Aconfig.mk | 8++++++++
Arspan.1 | 29+++++++++++++++++++++++++++++
Arspan.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 183 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +rspan +rspan.o diff --git a/Makefile b/Makefile @@ -0,0 +1,41 @@ +.POSIX: + +include config.mk + +SRC = rspan.c +OBJ = $(SRC:.c=.o) + +all: options rspan + +options: + @echo rspan build options: + @echo "VERSION = $(VERSION)" + @echo "PREFIX = $(PREFIX)" + @echo "CFLAGS = $(STCFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) $(STCFLAGS) -c $< + +$(OBJ): config.mk + +rspan: $(OBJ) + $(CC) -o $@ $(OBJ) $(STCFLAGS) + +clean: + rm -f rspan $(OBJ) + +install: rspan + @echo installing executable to ${PREFIX}/bin + mkdir -p $(PREFIX)/bin + cp -f rspan $(PREFIX)/bin + chmod 755 $(PREFIX)/bin/rspan + @echo installing manual page to ${MANPREFIX}/man1 + mkdir -p ${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < rspan.1 > ${MANPREFIX}/man1/rspan.1 + chmod 644 ${MANPREFIX}/man1/rspan.1 + +uninstall: + rm -f $(PREFIX)/bin/rspan + +.PHONY: all options clean install uninstall diff --git a/config.mk b/config.mk @@ -0,0 +1,8 @@ +VERSION = 0.0.1 + +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +CFLAGS = -Wall -pedantic -std=c99 +CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 +STCFLAGS = $(CPPFLAGS) $(CFLAGS) diff --git a/rspan.1 b/rspan.1 @@ -0,0 +1,29 @@ +.Dd $Mdocdate$ +.Dt RSPAN 1 +.Os +.Sh NAME +.Nm rspan +.Nd a random time span creator +.Sh SYNOPSIS +.Nm +.Ar interval +.Ar file +.Op Ar arguments ... +.Sh DESCRIPTION +.Nm +is a random time span creator, +a program that upon execution creates a random timeout based on given +.Ar interval "." +When the timeout ends the given +.Ar file +will get executed with potentially given +.Ar arguments "." +.Pp +.Sh OPTIONS +.Bl -tag -width Ds +.It Ar interval +Specifies the maximum timeout of the span. The interval must be a positive +integer. +.It Ar file +Specifies what file to execute upon timeout. +.El diff --git a/rspan.c b/rspan.c @@ -0,0 +1,103 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> + +static const char* usage = "usage: rspan INT FILE [ARG ...]\n" +"\n" +" INT : interval in seconds\n" +" FILE : file path to executable\n" +" ARG : list of arguments for executable\n"; + +static int command_argc; +static char **command; + +static void die(int, const char*); +static void handler(int); +static void init_timer(int, void (*)(int)); +static unsigned long mix(unsigned long, unsigned long, unsigned long); + +unsigned long mix(unsigned long a, unsigned long b, unsigned long c) +{ + a=a-b; a=a-c; a=a^(c >> 13); + b=b-c; b=b-a; b=b^(a << 8); + a=a-b; a=a-c; a=a^(c >> 12); + b=b-c; b=b-a; b=b^(a << 16); + a=a-b; a=a-c; a=a^(c >> 3); + b=b-c; b=b-a; b=b^(a << 10); + c=c-a; c=c-b; c=c^(b >> 15); + return c; +} + +void die(int code, const char *msg) +{ + int i; + + if (msg != NULL) + fprintf(stderr, "%s\n", msg); + + if (command != NULL) { + for (i = 0; i < command_argc; i++) + free(command[i]); + free(command); + } + + exit(code); +} + +void init_timer(int timeout, void (*func)(int)) +{ + struct itimerval it_val; + + it_val.it_value.tv_sec = timeout; + it_val.it_value.tv_usec = 0; + it_val.it_interval.tv_sec = 0; + it_val.it_interval.tv_usec = 0; + + if (signal(SIGALRM, func) == SIG_ERR) + die(1, "signal handler couldn't be created"); + if (setitimer(ITIMER_REAL, &it_val, NULL) == -1) + die(1, "timer couldn't initialize"); +} + +void handler(int signum) +{ + execvp(command[0], command); + die(0, NULL); +} + +int main (int argc, char *argv[]) +{ + unsigned long seed = mix(clock(), time(NULL), getpid()); + int c, i, s; + + if (argc == 1) + die(0, usage); + + if (sscanf(argv[1], "%i", &s) != 1 || s <= 0) + die(1, "interval must be a positive integer"); + + if ((command_argc = argc - 1) < 2) + die(1, "please add something to execute"); + + command = (char **)malloc((command_argc) * sizeof(char *)); + for (i = 2; i < argc; i++) { + *command = (char *)malloc(strlen(argv[i]) + 1); + strncpy(*command, argv[i], strlen(argv[i]) + 1); + command++; + } + *command = NULL; + command -= (command_argc - 1); + + srand(seed); + s = rand() % s + 1; + init_timer(s, &handler); + + while ((c = getchar())); + + /* unreachable */ + return 0; +}