#define _XOPEN_SOURCE #define _POSIX_C_SOURCE 200112L #include "signal-names.h" #include #include #include #include #include #include #include #include #include #include #include #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 # include 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; }