I'm trying to make a program that simulates the command nohup. The program gets as a first parameter, the name of a command that is gonna be executed. The program executed by my program must not be notified when the terminal is closed, it will have to ignore the SIGHUP. If I test my program with with the following command:
./mynohup sleep 120 &
And then I try to send a SIGHUP from another terminal, sleep terminates when it should be immune to it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include "utils.h"
#define NOHUP_OUT_FILE "nohup.out"
static void handle_signal(int signum)
{
if(signum == SIGHUP)
{
printf("This is ignored\n");
}
else
{
printf("Not ignored\n");
}
fflush(stdout);
}
/* configure handlers */
static void set_signals(void)
{
struct sigaction sa;
int rc;
/* TODO - ignore SIGHUP */
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_signal;
rc = sigaction(SIGHUP, &sa, NULL);
DIE(rc == -1, "sigaction");
}
/* execute a new program */
static void exec_func(int argc, char **argv)
{
int rc;
int i;
char **exec_args;
int fd;
set_signals(); /* ignore SIGHUP */
if(isatty(STDOUT_FILENO))
{
fd = open(NOHUP_OUT_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
DIE(fd < 0, "open");
dup2(fd, STDOUT_FILENO);
close(fd);
}
/* exec a new process */
exec_args = malloc(argc * sizeof(*exec_args));
DIE(exec_args == NULL, "malloc");
for (i = 0; i < argc-1; i++)
exec_args[i] = argv[i+1];
exec_args[argc-1] = NULL;
execvp(exec_args[0], exec_args);
DIE(1, "execvp");
}
int main(int argc, char **argv)
{
if (argc <= 1) {
fprintf(stderr, "Usage: %s command_and_arguments\n", argv[0]);
exit(EXIT_FAILURE);
}
exec_func(argc, argv);
return 0;
}
I tried to skip creating a new process and the signal handler works great. If the signal handler is in the following form the program works
static void set_signals(void)
{
struct sigaction sa;
int rc;
/* ignore SIGHUP */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
rc = sigaction(SIGHUP, &sa, NULL);
DIE(rc == -1, "sigaction");
}
I don't understand why when I create the first version of the signal handler the program doesn't works and with the second one it works.
Thanks in advance!
All exec functions reset the dispositions of caught signals to their default dispositions.
When you exec, your process image is destroyed and replaced by the process image of the new program. In it, the pointer to the handle_function
you passed to sigaction no longer has meaning, or the old meaning at least. The only sensible thing the OS can do with handled signals upon execve
is to reset them.
The meaning of SIG_IGN
is universal and independent of the current program and that's why SIG_IGN
can be, and is, inherited.
execvp()
is a front end for the execve()
syscall.
From its linux manpage :
All process attributes are preserved during an execve(), except the following:
* The dispositions of any signals that are being caught are reset to the default (signal(7)).
So the signal handler you installed is reset.
The nohup(1)
program just shifts the progran name ( nohup
) and the options to it, from the argc
/ argv
parameters to main
, redirects stdout
/ stderr
to a file ( nohup.out
) in case one or both are directed to a tty device, and then just ignores SIGHUP
and execvp(*argv, argv);
for the original program to execute. It even does no fork(2)
at all.
The source code of FreeBSD nohup is available here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.