Initial commit
This commit is contained in:
201
die-with-parent.c
Normal file
201
die-with-parent.c
Normal file
@ -0,0 +1,201 @@
|
||||
#define _XOPEN_SOURCE
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#include "signal-names.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PID_MAX ((((size_t) 1) << (sizeof(pid_t) * 8)) + 1)
|
||||
|
||||
static const char *EXEC_NAME;
|
||||
|
||||
int eprintf(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int c = 0;
|
||||
c += fprintf(stderr, "%s: ", EXEC_NAME);
|
||||
c += vfprintf(stderr, fmt, args);
|
||||
c += fputc('\n', stderr);
|
||||
va_end(args);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arrange for the current process to be sent SIGNAL when its parent dies.
|
||||
* This should return false on error and print a diagnostic message.
|
||||
*/
|
||||
bool die_with_parent(int signal);
|
||||
|
||||
#ifdef __linux__
|
||||
# include <linux/prctl.h>
|
||||
# include <sys/prctl.h>
|
||||
|
||||
bool die_with_parent(int signal) {
|
||||
if (prctl(PR_SET_PDEATHSIG, signal) < 0) {
|
||||
eprintf("prctl PR_SET_PDEATHSIG: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
# error "Unsuported operating system"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Parse OPT into an integer signal number, or return -1 on error.
|
||||
*/
|
||||
int parse_signal(const char *opt) {
|
||||
if (*opt >= '0' && *opt <= '9') {
|
||||
char *endptr;
|
||||
long signum = strtol(opt, &endptr, 10);
|
||||
if (*endptr) {
|
||||
return -1;
|
||||
}
|
||||
return signum;
|
||||
} else if (strncasecmp(opt, "SIG", 3) == 0) {
|
||||
opt += 3;
|
||||
}
|
||||
if (strncasecmp(opt, "RT", 2) == 0) {
|
||||
int reference = 0;
|
||||
if (strncasecmp(opt + 2, "MIN", 3) == 0) {
|
||||
reference = SIGRTMIN;
|
||||
} else if (strncasecmp(opt + 2, "MAX", 3) == 0) {
|
||||
reference = SIGRTMAX;
|
||||
}
|
||||
if (reference > 0) {
|
||||
if (!opt[5]) {
|
||||
return reference;
|
||||
}
|
||||
char *endptr;
|
||||
long signum = strtol(opt + 5, &endptr, 10);
|
||||
if ((signum > 0 && reference != SIGRTMIN)
|
||||
|| (signum < 0 && reference != SIGRTMAX)) {
|
||||
return -1;
|
||||
}
|
||||
long abs_signum = signum < 0 ? -signum : signum;
|
||||
return !*endptr && abs_signum <= SIGRTMAX - SIGRTMIN
|
||||
? reference + signum
|
||||
: -1;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < MAX_SIGNUM; ++i) {
|
||||
if (SIGNAL_NAMES[i] && strcasecmp(opt, SIGNAL_NAMES[i] + 3) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void print_help(FILE *stream) {
|
||||
fprintf(stream,
|
||||
"usage: %s [-h] [-s SIGNAL] [-n] [-p PID] COMMAND ARGS...\n",
|
||||
EXEC_NAME);
|
||||
fprintf(stream, "Arrange for COMMAND to be executed with ARGS, arranging "
|
||||
"for it to have a signal\nsent when its parent dies.\n\n");
|
||||
fprintf(stream, "Options:\n");
|
||||
fprintf(stream, " -h print this message, then exit\n");
|
||||
fprintf(
|
||||
stream,
|
||||
" -s arrage for SIGNAL to be sent instead of the default SIGHUP\n");
|
||||
fprintf(stream, " -n don't search $PATH for COMMAND\n");
|
||||
fprintf(stream, " -p specify the parent processes process-id as PID\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *const *argv) {
|
||||
EXEC_NAME = argv[0];
|
||||
opterr = false;
|
||||
int signal_num = SIGHUP;
|
||||
bool no_search = false;
|
||||
bool had_error = false;
|
||||
pid_t init_ppid = -1;
|
||||
int c;
|
||||
const char *old_posixly_correct = getenv("POSIXLY_CORRECT");
|
||||
putenv("POSIXLY_CORRECT=1");
|
||||
while ((c = getopt(argc, argv, ":hs:np:")) >= 0) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help(stdout);
|
||||
return 0;
|
||||
case 's':
|
||||
signal_num = parse_signal(optarg);
|
||||
if (signal_num < 0) {
|
||||
eprintf("unknown signal: %s", optarg);
|
||||
had_error = true;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
no_search = true;
|
||||
break;
|
||||
case 'p': {
|
||||
char *endptr;
|
||||
long lpid = strtol(optarg, &endptr, 10);
|
||||
if (!endptr) {
|
||||
eprintf("invalid pid: %s", optarg);
|
||||
had_error = true;
|
||||
} else if (lpid < 1 || lpid > PID_MAX) {
|
||||
eprintf("out of range: %s", optarg);
|
||||
had_error = true;
|
||||
}
|
||||
init_ppid = lpid;
|
||||
} break;
|
||||
case ':':
|
||||
if (isprint(optopt)) {
|
||||
eprintf("option requires argument: -%c", optopt);
|
||||
} else {
|
||||
eprintf("option requires argument: 0x%x", optopt);
|
||||
}
|
||||
had_error = true;
|
||||
break;
|
||||
case '?':
|
||||
if (isprint(optopt)) {
|
||||
eprintf("unknown option: -%c", optopt);
|
||||
} else {
|
||||
eprintf("unknown option: 0x%x", optopt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!argv[optind]) {
|
||||
eprintf("expected command as positional arguments");
|
||||
return 1;
|
||||
}
|
||||
if (!old_posixly_correct) {
|
||||
unsetenv("POSIXLY_CORRECT");
|
||||
} else {
|
||||
setenv("POSIXLY_CORRECT", old_posixly_correct, true);
|
||||
}
|
||||
if (had_error) {
|
||||
return 1;
|
||||
}
|
||||
if (!die_with_parent(signal_num)) {
|
||||
return 1;
|
||||
}
|
||||
// we now check if the parent exited before this point
|
||||
pid_t new_ppid = getppid();
|
||||
if (new_ppid != init_ppid) {
|
||||
raise(signal_num);
|
||||
// if we got here that means the signal in question was ignored which is
|
||||
// probably what the user intended, so just go on with execing the new
|
||||
// process
|
||||
}
|
||||
const char *func;
|
||||
if (no_search) {
|
||||
execv(argv[optind], &argv[optind]);
|
||||
func = "execv";
|
||||
} else {
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
func = "execvp";
|
||||
}
|
||||
// if we got here it means an error occurred
|
||||
eprintf("%s: %s", func, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user