/* (c) Copyright 2007 Daniel Hokka Zakrisson
   Released under the terms of the GNU GPL v2

   Build with gcc ... -osigrelay sigrelay
 */

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>

#define PREFIX "sigrelay: "

static pid_t child;
static int pipefd[2];

static void signal_handler(int signal)
{
	/* child exited, so should we */
	if (signal == SIGCHLD) {
		int ret;
		if (waitpid(child, &ret, 0) == -1) {
			perror(PREFIX "waitpid");
			exit(111);
		}
		exit(WEXITSTATUS(ret));
	}
	/* relay */
	else {
		if (!write(pipefd[1], &signal, sizeof(int))) {
			perror(PREFIX "write");
			exit(111);
		}
	}
}

#define SETSIG(sig)	if (signal(sig, signal_handler) == SIG_ERR) { \
				signal_handler(SIGTERM); \
				perror(PREFIX "signal(" #sig ")"); \
				exit(111); \
			}
int main(int argc, char *argv[])
{
	int opt, new_group = 0;
	int read_signal;

	while ((opt = getopt(argc, argv, "+P")) != -1) {
		switch (opt) {
		case 'P':
			new_group = 1;
			break;
		default:
			fprintf(stderr, "Usage: %s [-P] <program> <args>*\n", argv[0]);
			break;
		}
	}

	if (argc == optind)
		return 0;

	if (pipe2(pipefd, O_CLOEXEC) == -1) {
		perror(PREFIX "pipe");
		exit(111);
	}

	SETSIG(SIGCHLD)
	child = fork();
	if (child == 0) {
		close(pipefd[1]);
		if (new_group && setsid() == -1) {
			perror(PREFIX "setsid");
			exit(111);
		}
		child = fork();
		if (child == 0) {
			execvp(argv[optind], argv+optind);
			perror(PREFIX "execvp");
			exit(111);
		}
		else if (child == -1) {
			perror(PREFIX "fork");
			exit(111);
		}
		while (read(pipefd[0], &read_signal, sizeof(int)) == sizeof(int)) {
			if (kill(child, read_signal) == -1)
				perror(PREFIX "kill");
		}
		perror(PREFIX "read");
		exit(111);
	}
	else if (child == -1) {
		perror(PREFIX "fork");
		exit(111);
	}
	close(pipefd[0]);
	SETSIG(SIGHUP)
	SETSIG(SIGINT)
	SETSIG(SIGSEGV)
	SETSIG(SIGTERM)
	SETSIG(SIGUSR1)
	SETSIG(SIGUSR2)
	SETSIG(SIGWINCH)
	SETSIG(SIGALRM)
	SETSIG(SIGPWR)

	while (1) {
		sleep(600);
	}
	/* never get here, signal handler exits */
	return 1;
}