diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..09b3dd3 --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +--- +AlignAfterOpenBracket: AlwaysBreak +AlignEscapedNewlines: DontAlign +AlignOperands: DontAlign +AlwaysBreakAfterReturnType: AllDefinitions +ContinuationIndentWidth: 8 +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +TabWidth: 4 +UseTab: ForContinuationAndIndentation +AlignTrailingComments: false +... diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0a02187 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = tab +tab_width = 4 +indent_size = 4 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..b79187e --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,27 @@ +name: Check +on: +- push +jobs: + build: + runs-on: ubuntu-latest + container: alpine:3.14 + steps: + - uses: actions/checkout@v2 + - name: install deps + run: | + apk add make gcc musl-dev + - name: make + run: make DEBUG=0 STATIC=1 STRIP=1 + - name: irc notice + uses: Gottox/irc-message-action@v2 + if: failure() + with: + channel: "Gottox" + nickname: gh-gottox + message: "https://github.com/${{ github.repository }} failed on push ${{ github.event.compare }}" + - name: release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/v') && success() + with: + files: | + autohup diff --git a/.gitignore b/.gitignore index c6127b3..c96a68e 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,8 @@ modules.order Module.symvers Mkfile.old dkms.conf + +compile_commands.json +.cache +autohup +.gdb_history diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..949d87d --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +###################################################################### +# @author : Enno Boland (mail@eboland.de) +# @file : Makefile +# @created : Tuesday Nov 09, 2021 18:51:02 CET +###################################################################### + +CFLAGS ?= -Wall -Wpedantic -Werror +VERSION ?= 0.1 + +ifeq ($(STATIC),1) + LDFLAGS += -static +endif + +ifeq ($(DEBUG),1) + LDFLAGS += -g +endif + +ifeq ($(STRIP),1) + LDFLAGS += -s +endif + +autohup: main.c + $(CC) -o $@ $< $(CFLAGS) $(LDFLAGS) -DVERSION='"$(VERSION)"' + +.PHONY: clean release +clean: + rm -f autohup + +release: clean + test -z "`git status -s`" + test "`git branch --show-current`" = main + test "`git rev-parse HEAD`" = "`git rev-parse origin/main`" + git tag v$(VERSION) + git push --tags diff --git a/README.md b/README.md index 474f9c4..7774e11 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,37 @@ # autohup automaticly sends SIGHUP to a process when a file changes. + +## usage + +``` +usage: autohup [-s signal] [-v] [path ...] -- command [argument ...] + autohup -l +``` + +## exit code + +`autohup` always returns 255 if an error occurs internally. If the program +defined with `command` exits, its error code is used. + +## example + +This example watches for changes in `/etc/daemon.d` and sends `SIGHUP` when inotify +registers a change in that directory. +``` +$ autohup /etc/daemon.d -- daemon +``` + +The second example watches multiple directories and sends `SIGTERM` instead +``` +$ autohup -s term /etc/daemon.d /var/lib/daemon -- daemon +``` + +To list all signals autohup can send use this command: +``` +$ autohup -l +``` + +Signals can also be defined as numbers +``` +$ autohup /etc/daemon.d -s 10 -- daemon # 10 = SIGUSR2 +``` diff --git a/main.c b/main.c new file mode 100644 index 0000000..df870fd --- /dev/null +++ b/main.c @@ -0,0 +1,183 @@ +/** + * @author : Enno Boland (mail@eboland.de) + * @file : main + * @created : Tuesday Nov 09, 2021 18:54:40 CET + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EXIT_ERROR_CODE 255 +#define MAX_EVENTS 1 +#define EVENT_SIZE (sizeof(struct inotify_event)) +#define BUF_LEN (MAX_EVENTS * (EVENT_SIZE + NAME_MAX)) + +#define SIG(x) \ + { .number = SIG##x, .name = #x } +const struct { + int number; + char name[7]; +} signals[] = {SIG(ALRM), SIG(HUP), SIG(INT), SIG(QUIT), SIG(TERM), + SIG(URG), SIG(USR1), SIG(USR2), SIG(WINCH), {0}}; +#undef SIG + +pid_t child = 0; +int trigger_signal = SIGHUP; +int verbose = 0; + +static const char * +get_sig_name(const int signal) { + int i; + + for (i = 0; signals[i].number != 0; i++) { + if (signal == signals[i].number) { + return signals[i].name; + } + } + + return NULL; +} + +static int +get_sig_number(const char *signal) { + int i; + if (strncasecmp("SIG", signal, 3) == 0) { + signal += 3; + } + + for (i = 0; signals[i].number != 0; i++) { + if (strcasecmp(signals[i].name, signal) == 0) { + return signals[i].number; + } + } + + return -1; +} + +void +sig_handler_forward(int sig) { + if (child == 0) { + return; + } + + kill(child, sig); +} + +static void +sig_handler_child(int sig) { + int status; + + while (waitpid(-1, &status, WNOHANG) > 0) { + } + + exit(status); +} + +int +list_signals() { + int i; + for (i = 0; signals[i].number != 0; i++) { + fputs("SIG", stdout); + puts(signals[i].name); + } + + return EXIT_SUCCESS; +} + +int +usage(char *arg0) { + fprintf(stderr, + "usage: %s [-s signal] [-v] [path ...] -- command [argument ...]\n", + arg0); + fprintf(stderr, " %s -l\n", arg0); + return EXIT_ERROR_CODE; +} + +int +main(int argc, char **argv) { + int rv; + int inotify_fd; + char buffer[BUF_LEN]; + const char *observed = argv[1]; + int opt; + + inotify_fd = inotify_init(); + + while ((opt = getopt(argc, argv, "-s:lvV")) != -1) { + switch (opt) { + case 'l': + return list_signals(); + case 's': + rv = atoi(optarg); + if (rv == 0) { + rv = get_sig_number(optarg); + } + if (rv < 0) { + fprintf(stderr, "%s: Signal cannot be found\n", optarg); + return usage(argv[0]); + } + trigger_signal = rv; + break; + case 'V': + fputs("autohup-" VERSION "\n", stderr); + return EXIT_ERROR_CODE; + case 'v': + verbose++; + break; + case 1: + rv = inotify_add_watch(inotify_fd, optarg, IN_ALL_EVENTS); + if (rv < 0) { + perror(observed); + return EXIT_ERROR_CODE; + } + break; + default: + return EXIT_ERROR_CODE; + } + } + if (optind == argc) { + return usage(argv[0]); + } + + signal(SIGCHLD, sig_handler_child); + + child = fork(); + if (child < 0) { + perror(argv[2]); + } else if (child == 0) { + argv += optind; + execvp(argv[0], argv); + perror(argv[0]); + + return EXIT_ERROR_CODE; + } else { + signal(SIGHUP, sig_handler_forward); + signal(SIGINT, sig_handler_forward); + signal(SIGQUIT, sig_handler_forward); + signal(SIGUSR1, sig_handler_forward); + signal(SIGUSR2, sig_handler_forward); + signal(SIGTERM, sig_handler_forward); + signal(SIGCONT, sig_handler_forward); + for (;;) { + rv = read(inotify_fd, buffer, BUF_LEN); + if (rv < 0) { + perror("inotify"); + return EXIT_ERROR_CODE; + } + + if (verbose) { + fprintf(stderr, "DEBUG: sending signal %s to child process\n", + get_sig_name(trigger_signal)); + } + sig_handler_forward(trigger_signal); + } + return EXIT_ERROR_CODE; + } +}