Some days ago, I asked here how to start a program using C without using fork(). This solution is working fine.....except that I can't kill this child process!
My program (in this case, 'rbfeeder') has some threads....and when I send kill signal, only threads are killed (I think). What am I doing wrong?
This is the code used to 'start' rbfeeder:
/*
* Start dump1090, if not running
*/
void startDump(void) {
if (p_dump != 0) {
log_level(8, "Looks like dump is already running.\n");
return;
}
pid_t ret = run_cmd("/home/jmaurin/dev/client/rbfeeder");
if (ret != 0) {
log_level(8, "Ok, started! Pid is: %i\n", ret);
p_dump = ret;
sendStats();
} else {
log_level(8, "Error starting dump1090\n");
p_dump = 0;
sendStats();
}
return;
}
And this is the code to 'stop':
void stopDump(void) {
if (checkDumpRunning()) {
log_level(3, "Dump is running, let's try to kill.\n");
if (kill(p_dump, SIGKILL) == 0) {
log_level(3,"Succesfull kill dump!\n");
sendStats();
return;
} else {
log_level(3,"Error killing dump.\n");
return;
}
} else {
log_level(3, "Dump is not running.\n");
}
return;
}
and run_cmd function:
pid_t run_cmd(char *cmd) {
pid_t pid, ret;
char *argv[] = {"sh", "-c", cmd, NULL};
int status, s;
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_t *file_actionsp;
s = posix_spawn_file_actions_init(&file_actions);
if (s != 0)
return 0;
//STDERR_FILENO
s = posix_spawn_file_actions_addclose(&file_actions,STDERR_FILENO);
if (s != 0)
return 0;
file_actionsp = &file_actions;
//printf("Run command: %s\n", cmd);
status = posix_spawn(&pid, "/bin/sh", file_actionsp, NULL, argv, environ);
if (status == 0) {
log_level(8, "Child pid: %i\n", pid);
ret = pid;
/*
if (waitpid(pid, &status, 0) != -1) {
printf("Child exited with status %i\n", status);
} else {
perror("waitpid");
}
*/
} else {
// printf("posix_spawn: %s\n", strerror(status));
ret = 0;
}
return ret;
//printf("End of run\n");
}
p_dump is a global variable to hold PID.
This image is when my 'client' receive the command (over ethernet) to start external program:
Then, an HTOP on the same machine....see that PID are the same, which means that my variable is correct:
Then, I've sent an 'stop' command and my client executed 'stopDump', but one process still running (the other threads from same program are 'killed'):
The external program doesn't 'spawn/fork' itself, but it does have threads.
Your kill is completing successfully, because the process is becoming a zombie, (The Z in the status column in HTOP). A zombie is a process that still has metadata in the kernel, but is not actually running. To get rid of the zombie, the parent has to wait on the process. As your process is the parent, adding a call to waitpid(p_dump) after the kill succeeds should handle this.
The answer by user1937198 worked fine, but I found another way that doesn't need to call any function, like 'waitpid'.
struct sigaction sigchld_action = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDWAIT
};
sigaction(SIGCHLD, &sigchld_action, NULL);
At least in my case, it's preventing zombie proccess after kill. It's works fine.
Related
I want to fully terminate a process on Linux C89.
The flow is: check if its already dead, if not, let it die peacefully using sigterm and wait 10 seconds till it dies. If its still alive - SIGKILL it.
int TerminateProcessIMP(pid_t process_to_kill)
{
assert(process_to_kill);
/*---------------------------------------------------------*/
/* check if process is already does not exist */
if (!IsProcessAliveIMP(process_to_kill))
{
return (SUCCESS);
}
/*---------------------------------------------------------*/
/* terminate process_to_kill */
if (kill(process_to_kill, SIGTERM))
{
fprintf(stderr, "%s\n", strerror(errno));
}
if (!IsProcessAliveIMP(process_to_kill))
{
return (SUCCESS);
}
/*---------------------------------------------------------*/
/* if its still alive, SIGKILL it */
if (kill(process_to_kill, SIGKILL))
{
fprintf(stderr, "%s\n", strerror(errno));
}
if (!IsProcessAliveIMP(process_to_kill))
{
return (SUCCESS);
}
/*---------------------------------------------------------*/
return (FAILURE);
}
/******************************************************************************/
int IsProcessAliveIMP(pid_t process_to_check)
{
time_t start_time = 0;
time_t end_time = 0;
time_t time_to_wait = 10; /* in seconds */
assert(process_to_check);
start_time = time(0);
end_time = start_time + time_to_wait;
/* give it time to be terminated because maybe it frees memory meanwhile */
while (0 != kill(process_to_check, 0) && time(0) < end_time)
{}
/* check if it still exists */
if (0 == kill(process_to_check, 0))
{
return (0);
}
/* the process is still alive */
return (1);
}
What do you think?
Right now, it does not work and does not terminate the process.
It tries to terminate the process but fails to do that. I can't figure out why.
Thanks.
You're doing unnecessary things.
To check if a process exists:
kill(pid, 0);
If the return value is 0, then the process exists, if -1 then you have to check errno. In case of ESRCH:
The target process or process group does not exist. Note that an existing process might be a zombie, a process that has terminated execution, but has not yet been wait(2)ed for.
To terminate a process:
kill(pid, SIGTERM); // signal can be blocked, handled or ignored
or
kill(pid, SIGKILL); // signal cannot be blocked, handled or ignored
After a kill, you have to wait for the process via:
waitpid(pid, &status, 0);
If status is not NULL, wait() and waitpid() store status information in the int to which it points. This integer can be inspected with the macros, described in the man page.
See
man kill
man waitpid
Example:
int TerminateProcessIMP(pid_t pid)
{
//check if process exists
int res = kill(pid, 0);
if ((res == -1) && (errno != ESRCH)) {
//error: either EINVAL or EPERM
//ESRCH: an existing process might be a zombie
return -1;
}
if (res == 0) { //process exists
//ask politely to terminate
if (kill(pid, SIGTERM) == -1) {
//error: unable to send a signal to the process
return -1;
}
//let us see if the child complied to our request
res = waitpid(pid, NULL, WNOHANG | WUNTRACED | WCONTINUED);
if (res == -1) {
//most likely not our child (errno == ECHILD)
//but the process could follow our request and still terminate
//if you want to be sure goto SIGKILL below
//or return
} else if (res == 0) {
//our child, but at this point the child has not terminated yet
//(maybe it will never)
//either continue to wait or goto SIGKILL below
} else {
//child complied to our request and terminated in time
//res contains the id of the child (res == pid)
return res;
}
//--- or ---
/* do {
res = waitpid(pid, NULL, WNOHANG | WUNTRACED | WCONTINUED);
//because of WNOHANG
sleep(1);
//your timeout method goes here
} while (!res);
if (res == -1) { //same as above }
if (res > 0) { return res; } */
}
//at this point, the process either does not exists (maybe zombie),
//is not our child or refused our request (SIGTERM)
//send a SIGKILL signal to the process
kill(pid, SIGKILL);
//wait for the process to terminate
res = waitpid(pid, NULL, 0);
/*if (res == -1) {
//not our child, or process does not exists
}*/
/*if (res > 0) {
//child successfully terminated
}*/
return res;
}
i have the following function:
int run_func(command history[MAX_INPUT_SIZE], char** args, int capacity) {
int need_to_wait = 1;
int i = 0;
char* arg = args[0];
int status;
while (arg != NULL) {
if (strcmp(arg, "&") == 0) {
need_to_wait = 0;
break;
}
arg = args[i++];
}
pid_t wait_pid;
pid_t pid = fork();
int res;
if (pid == 0) {
res = execvp(args[0], args);
if (res == -1) {
printf("exec failed\n");
fflush(stdout);
return 0;
}
} else if (pid < 0) {
printf("fork failed\n");
fflush(stdout);
return 0;
} else {
if (need_to_wait){
do {
wait_pid = waitpid(pid, &status, 0);
} while(!WIFEXITED(status) && !WIFSIGNALED(status));
}
history[capacity - 1].pid = pid;
}
return 1;
}
the issue I have is that the bottom while loop, hangs and doesn't stop whenever I get an invalid command such as 'hello' from the user from the terminal until I press enter again.
this function is being called from another function that receives input from the user.
Copying comment into an answer.
Side issues:
Error messages should be printed to stderr, not stdout.
There's no need to save or test the return value from execvp() — if it returns, it failed; if it succeeds, it does not return.
Main observation:
You should almost certainly have an exit() or _exit() instead of return 0; in the error handling code after execvp(). When the command fails (hello?), then you end up with two processes running — one from the failed execvp() and one is the parent process. This is apt to confuse everything as you have two processes trying to read the terminal at the same time.
I am trying to fork my C application, running on an embedded linux environment, and get its return value for failure/success analysis.
I looked at similar questions (e.g. this, this, this, this and some other Q/A..) but still can't get it to work.
code:
static int fork_test(const char *src, const char *dst)
{
int childExitStatus;
pid_t pid;
int status;
DEBUG_PRINT("forking");
pid = fork();
if (pid == 0) { // child
sleep(2);
DEBUG_PRINT("child - exiting");
exit(1);
}
else if (pid < 0) {
return -1;
}
else { // parent
int i;
for (i = 0 ; i < 10 ; i++)
{
pid_t ws = waitpid(pid, &childExitStatus, WNOHANG);
if (-1 == ws)
{
DEBUG_PRINT("parent - failed wait. errno = %d", errno);
return -1;
}
if (0 == ws)
{
DEBUG_PRINT("parent - child is still running");
sleep(1);
continue;
}
}
if (10 == i)
return -1;
DEBUG_PRINT("parent - done waiting");
if (WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
{
DEBUG_PRINT("parent - got status %d", childExitStatus);
status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
if (0 != status)
{
DEBUG_PRINT("parent - picked up bad exit status");
return status;
}
return 0;
}
else
{
DEBUG_PRINT("parent - bad exit route");
return -1;
}
}
}
This provided this output:
forking
parent - child is still running
parent - child is still running
parent - child is still running
child - exiting
parent - failed wait. errno = 10
note that errno=10 means ECHILD.
so i tried to add:
...
DEBUG_PRINT("forking");
signal(SIGCHLD,SIG_DFL);
pid = fork();
...
(or with SIG_IGN) with no difference.
I can successfully add a signal handler for SIGCHLD, and might be able to wait for signal, instead of the child process, with sigwait() or the likes, but it seems like a bad solution..
Any idea what I'm missing here?
$ uname -mrso
Linux 3.18.20 armv7l GNU/Linux
Your code nicely tests for "errors". Good.
But the code unfortunately misses to catch the case you are after, the one where waitpid() actually returns the child's PID.
You could achieve this like so:
for (i = 0 ; i < 10 ; i++)
{
pid_t ws = waitpid(pid, &childExitStatus, WNOHANG);
if (-1 == ws)
{
DEBUG_PRINT("parent - failed wait. errno = %d", errno);
return -1;
}
else if (0 == ws)
{
DEBUG_PRINT("parent - child is still running");
sleep(1);
continue;
}
DEBUG_PRINT("parent - successfully waited for child with PID %d", (int) ws);
break;
}
Here is my code for the evaluate function, which is called in the main.
void eval(char *cmdline)
{
char *argv[MAXARGS]; /* argv for execve() */
int bg; /* should the job run in bg or fg? */
pid_t pid; /* process id */
bg = parseline(cmdline, argv);
struct job_t tempJob;
if (builtin_cmd(argv) == 0)
{
if ((pid = fork()) == 0)
{ /* child runs user job */
if (execve(argv[0], argv, environ) < 0)
{
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
if (!bg)
{ /* parent waits for fg job to terminate */
int status;
if (waitpid(pid, &status, 0) < 0)
unix_error("waitfg: waitpid error");
}
else /* otherwise, don’t wait for bg job */
{
printf("%d %s", pid, cmdline);
}
}
return;
}
Now when I run a background job, I expect that the pid of the job get printed twice twice, once in parent and once for child.I just need to understand what is going on in the code. It works the way it is supposed to but why?
Both the parent and child return from fork, but the condition == 0 is true only for the child:
if ((pid = fork()) == 0)
Thus only the child runs this:
if (execve(argv[0], argv, environ) < 0)
execve replaces the current process, and thus does not return if there is no error, i.e., the child's execution of this program ends either here or at the exit(0); that follows from the error condition.
The rest of the function is run only by the parent in all cases.
Title may be a little confusing, so let me explain. I am trying to write a simple shell to practice my programming. I have got the get a command, fork, exec loop working. However, when I press CTRL-C while child process is still executing, my shell terminates, instead of child process (but child process would keep running). Here is the main function:
int main()
{
dynarray *args; /* pointer to a dynamic array */
int bytes_read;
size_t nbytes = 0;
char *command;
pid_t pid;
printf("Enter command: ");
while ((bytes_read = getline(&command, &nbytes, stdin)) != -1) {
if (bytes_read == -1) {
perror("getline");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) { /* child process */
args = newdynarray();
char *arg = strtok(command, " \n");
while (arg != NULL) {
addstring(args, arg);
arg = strtok(NULL, " \n");
}
if (args->nval == 0) {
freedynarray(args);
continue;
}
addstring(args, NULL);
char *fullpath = find_executable(args->strings[0]);
if (fullpath == NULL) {
fprintf(stderr, "Couldn't find executable: %s\n", command);
exit(EXIT_FAILURE);
}
if (execv(fullpath, args->strings) == -1) {
perror("execv");
exit(EXIT_FAILURE);
}
} else {
int status;
waitpid(pid, &status, 0);
}
printf("Enter command: ");
}
return 0;
}
I didn't include other parts, because I don't think they are relevant. How can I make my child process catch all the input from stdin until it terminates?
You can register a signal handler for SIGINT in your parent process, and therein use kill(2) to send a signal to the child process, whose PID you should store somewhere.
How can I make my child process catch all the input from stdin until it terminates? Signals generated from stdin keys (such as control C) will be sent to the last process to use stdin, so there's nothing you can do unless you can force your child to use the path.
Instead, you need to create a signal handler in your shell process to catch SIGINT (and others), and resend the signal (using the kill() function) to the process you want to receive it.