execvp appears to change pid of the process - linux

I created a program that does:
1. fork
2. from the son, create execvp with csh. The execvp csh runs a script a.sh that prints in infinite loop.
My problem is that I cant stop or kill the process from the father (using kill(SIGKILL,pid) from the father process didn't work).
I think that the problem is in execvp.
when I print the pid from the script(echo $BASHPID ) I get a different pid from the one that I get before the execvp. i know the pid after execvp is supposed to remain the same, but it seems like it doesn't.
here is the problematic code:
int ExeExternal(char* args[MAX_ARG], char* cmdString, int* fg_pid, char** L_Fg_Cmd){
//int child_status;
int pID;
pID = fork();
switch(pID) {
case -1: //error
perror("fork");
return 1;
case 0 : // Child Process
setpgrp();
printf("son getpid() : %d",getpid());
fflush(stdout);
char* argument_for_cshs[5];
char* cmd="csh";
argument_for_cshs[0]="csh";
argument_for_cshs[1]="-f";
argument_for_cshs[2]="-c";
argument_for_cshs[3]=cmdString;
argument_for_cshs[4]=NULL;
execvp(argument_for_cshs[0],argument_for_cshs);
//if return => there is a problem
any solution?

Related

Is it possible to start a linux process, getting the PID, but start running later on certain condition?

I'm thinking about some tool that can pause the program at start.
For example, my_bin starts running at once.
$ ./my_bin
With this tool
$ magic_tool ./my_bin
my_bin will start. I can get the PID. Then I can start the actual running later.
I've just tested my suggestion in the comments and it worked! This is the code in my magic_tool.c:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main (int argc, char *argv[])
{
pid_t pid;
printf("Executing %s to wrap %s.\n", argv[0], argv[1]);
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
raise(SIGSTOP);
execl(argv[1], "", NULL);
} else {
printf("PID == %d\n", pid);
}
return 0;
}
I wrote another test program target.c:
#include <stdio.h>
int main ()
{
puts("It works!\n");
return 0;
}
Running ./magic_tool ./target printed a PID and returned to shell. Only after running kill -SIGCONT <printed_pid> was It works! printed. You'll probably want to have PID saved somewhere else and also perform some checks in the magic_tool, but I think this is nonetheless a good proof of concept.
EDIT:
I was playing around with this a bit more and for some reason it didn't always work (see why below). The solution is simple - just follow a proper fork off and die pattern a bit more closely in magic_tool.c:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main (int argc, char *argv[])
{
pid_t pid;
printf("Executing %s to wrap %s.\n", argv[0], argv[1]);
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
setsid();
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
raise(SIGSTOP);
if (execl(argv[1], "", NULL))
return -1;
}
printf("PID == %d\n", pid);
}
return 0;
}
I found an explanation in this answer:
When you start the root process from your shell, it is a process group leader, and its descendants are members of that group. When that leader terminates, the process group is orphaned. When the system detects a newly-orphaned process group in which any member is stopped, then every member of the process group is sent a SIGHUP followed by a SIGCONT.
So, some of your descendant processes are still stopped when the leader terminates, and thus everyone receives a SIGHUP followed by a SIGCONT, which for practical purposes mean they die of SIGHUP.
Exactly which descendants are still stopped (or even just merrily advancing toward exit()) is a timing race.
The answer also links to IEEE Std 1003.1-2017 _Exit entry which contains more details on the matter.
This is mostly a very similar idea as #gst, but done entirely in the shell, you can spawn a subshell (this forks and create a new pid) and have the subshell send itself SIGSTOP signal, when the subshell receives a SIGCONT signal and resumes, the subshell exec the intended program (this replaces the subshell with the intended program without creating a new pid). So that the main shell can continue doing stuff, the subshell should run on background with &.
In a nutshell:
(kill -STOP $BASHPID && exec ./my_bin) &
subpid=$! # get the pid of above subshell
... do something else ...
kill -CONT $subpid # resume
Another idea that wouldn't suffer from race condition between the main process sending SIGCONT and the subshell SIGSTOP-ing itself is to use a file descriptor to implement the wait instead:
exec {PIPEFD}<> <(:) # set PIPEFD to the file descriptor of an anonymous pipe
(read -u $PIPEFD && exec ./my_bin) &
subpid=$! # get the pid of above subshell
... do something else ...
echo >&$PIPEFD # resume

Who runs first in fork, with contradicting results

I have this simple test:
int main() {
int res = fork();
if (res == 0) { // child
printf("Son running now, pid = %d\n", getpid());
}
else { // parent
printf("Parent running now, pid = %d\n", getpid());
wait(NULL);
}
return 0;
}
When I run it a hundred times, i.e. run this command,
for ((i=0;i<100;i++)); do echo ${i}:; ./test; done
I get:
0:
Parent running now, pid = 1775
Son running now, pid = 1776
1:
Parent running now, pid = 1777
Son running now, pid = 1778
2:
Parent running now, pid = 1779
Son running now, pid = 1780
and so on; whereas when I first write to a file and then read the file, i.e. run this command,
for ((i=0;i<100;i++)); do echo ${i}:; ./test; done > forout
cat forout
I get it flipped! That is,
0:
Son running now, pid = 1776
Parent running now, pid = 1775
1:
Son running now, pid = 1778
Parent running now, pid = 1777
2:
Son running now, pid = 1780
Parent running now, pid = 1779
I know about the scheduler. What does this result not mean, in terms of who runs first after forking?
The forking function, do_fork() (at kernel/fork.c) ends with setting the need_resched flag to 1, with the comment by kernel developers saying, "let the child process run first."
I guessed that this has something to do with the buffers that the printf writes to.
Also, is it true to say that the input redirection (>) writes everything to a buffer first and only then copies to the file? And even so, why would this change the order of the prints?
Note: I am running the test on a single-core virtual machine with a Linux kernel v2.4.14.
Thank you for your time.
When you redirect, glibc detects that stdout is not tty turns on output buffering for efficiency. The buffer is therefore not written until the process exits. You can see this with e.g.:
int main() {
printf("hello world\n");
sleep(60);
}
When you run it interactively, it prints "hello world" and waits. When you redirect to a file, you will see that nothing is written for 60 seconds:
$ ./foo > file & tail -f file
(no output for 60 seconds)
Since your parent process waits for the child, it will necessarily always exit last, and therefore flush its output last.

Will ctrl+c send SIGINT signals to both parent and child processes in Linux?

In the terminal, I executed a main parent process which will fork a child process. In both the parent and child processes I implemented a SIGINT signal handler.
So when I press "ctrl+c", will both the handlers be called at the same time? Or do I need to call the child process's signal handler explicitly in the parent process's handler?
I looked up this post:
How does Ctrl-C terminate a child process?
which says that "The SIGINT signal is generated by the terminal line discipline, and broadcast to all processes in the terminal's foreground process group". I just didn't quite understand what does "foreground process group" means.
Thanks,
In both the parent and child processes I implemented a SIGINT signal
handler. So when I press "ctrl+c", will both the handlers be called at
the same time?
Yes, they both will receive SIGINT.
Or do I need to call the child process's signal handler explicitly in
the parent process's handler?
"Calling" another process' signal handler doesn't make sense. If the both the process have a handler installed then they will be called once they receive the signal SIGINT.
I just didn't quite understand what does "foreground process group"
means.
Typically, a process associated with a controlling terminal is foreground process and its process group is called foreground process group. When you start a process from the command line, it's a foreground process:
E.g.
$ ./script.sh # foreground process
$ ./script & # background process
I suggest you read about tty and The TTY demystified for a detailed explanation.
setpgid POSIX C process group minimal example
This illustrates how the signal does get sent to the child, if the child didn't change its process group with setpgid.
main.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t is_child = 0;
void signal_handler(int sig) {
char parent_str[] = "sigint parent\n";
char child_str[] = "sigint child\n";
signal(sig, signal_handler);
if (sig == SIGINT) {
if (is_child) {
write(STDOUT_FILENO, child_str, sizeof(child_str) - 1);
} else {
write(STDOUT_FILENO, parent_str, sizeof(parent_str) - 1);
}
}
}
int main(int argc, char **argv) {
pid_t pid, pgid;
(void)argv;
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
pid = fork();
assert(pid != -1);
if (pid == 0) {
/* Change the pgid.
* The new one is guaranteed to be different than the previous, which was equal to the parent's,
* because `man setpgid` says:
* > the child has its own unique process ID, and this PID does not match
* > the ID of any existing process group (setpgid(2)) or session.
*/
is_child = 1;
if (argc > 1) {
setpgid(0, 0);
}
printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0));
assert(kill(getppid(), SIGUSR1) == 0);
while (1);
exit(EXIT_SUCCESS);
}
/* Wait until the child sends a SIGUSR1. */
pause();
pgid = getpgid(0);
printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid);
/* man kill explains that negative first argument means to send a signal to a process group. */
kill(-pgid, SIGINT);
while (1);
}
GitHub upstream.
Compile with:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
Run without setpgid
Without any CLI arguments, setpgid is not done:
./setpgid
Possible outcome:
child pid, pgid = 28250, 28249
parent pid, pgid = 28249, 28249
sigint parent
sigint child
and the program hangs.
As we can see, the pgid of both processes is the same, as it gets inherited across fork.
Then whenever you hit:
Ctrl + C
It outputs again:
sigint parent
sigint child
This shows how:
to send a signal to an entire process group with kill(-pgid, SIGINT)
Ctrl + C on the terminal sends a kill to the entire process group by default
Quit the program by sending a different signal to both processes, e.g. SIGQUIT with Ctrl + \.
Run with setpgid
If you run with an argument, e.g.:
./setpgid 1
then the child changes its pgid, and now only a single sigint gets printed every time from the parent only:
child pid, pgid = 16470, 16470
parent pid, pgid = 16469, 16469
sigint parent
And now, whenever you hit:
Ctrl + C
only the parent receives the signal as well:
sigint parent
You can still kill the parent as before with a SIGQUIT:
Ctrl + \
however the child now has a different PGID, and does not receive that signal! This can seen from:
ps aux | grep setpgid
You will have to kill it explicitly with:
kill -9 16470
This makes it clear why signal groups exist: otherwise we would get a bunch of processes left over to be cleaned manually all the time.
Tested on Ubuntu 18.04.

Not able to kill a fork'ed process

I am forking a child, and trying to kill it.
pid_t *child_pid;
int main(){
child_pid = mmap(NULL, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);$
int a = fork();
if (a != 0) {
printf("child # %d\n", a);
*child_pid = a;
system("sleep 100");
} else {
sleep(1);
printf("Trying to kill %d\n", *child_pid);
int ret = kill(*child_pid,SIGKILL);
printf("killled with %d\n", ret);
}
}
However, the kill command gets stuck at:
child # 4752
Trying to kill 4752
In the meantime, calling ps shows this:
4752 pts/4 00:00:00 simple <defunct>
You are killing yourself. fork() returns 0 if you are in the forked process, or the child process id (PID) in the 'master' process.
So, the upper branch of your if() clause is performed in the master process, where you copy the child's process ID (stored in a) to child_pid.
In the lower branch you are in the child process, where take the child_pid, which is your own and then happily kill() yourself... That's why you never get the line 'Killed with...'
As paxdiablo pointed out, since this is a child process it will remain a zombie until you fetch the exit status with wait() or the master process exits.
BTW, I'm not sure what you want to do with this code:
If you want to exit the child process gracefully you can just do an exit().
If you want to kill your child process, keep track of the child PID (returned by fork()) and then kill() it from your master process.
If you want to kill the master process from your child (odd as that may sound) be careful, as that may take the child process(es) with it. You have to detach the child process from the master process (see the manual page for daemon()).
The child process is dead, it's just that the process entry will hang around until someone collects the exit code.
If the parent doesn't do it, it will eventually be inherited by init, which will reap it at some point.

Why Linux always output "^C" upon pressing of Ctrl+C?

I have been studying signals in Linux. And I've done a test program to capture SIGINT.
#include <unistd.h>
#include <signal.h>
#include <iostream>
void signal_handler(int signal_no);
int main() {
signal(SIGINT, signal_handler);
for (int i = 0; i < 10; ++i) {
std::cout << "I'm sleeping..." << std::endl;
unsigned int one_ms = 1000;
usleep(200* one_ms);
}
return 0;
}
void signal_handler(int signal_no) {
if (signal_no == SIGINT)
std::cout << "Oops, you pressed Ctrl+C!\n";
return;
}
While the output looks like this:
I'm sleeping...
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
I'm sleeping...
I'm sleeping...
I understand that when pressing Ctrl+C, processes in foreground process group all receives a SIGINT(if no process chooses to ignore it).
So is it that the shell(bash) AND the instance of the above program both received the signal? Where does the "^C" before each "Oops" come from?
The OS is CentOS, and the shell is bash.
It is the terminal (driver) that intercepts the ^C and translates it to a signal sent to the attached process (which is the shell) stty intr ^B would instruct the terminal driver to intercept a ^B instead. It is also the terminal driver that echoes the ^C back to the terminal.
The shell is just a process that sits at the other end of the line, and receives it's stdin from your terminal via the terminal driver (such as /dev/ttyX), and it's stdout (and stderr) are also attached to the same tty.
Note that (if echoing is enabled) the terminal sends the keystrokes to both the process (group) and back to the terminal. The stty command is just wrapper around the ioctl()s for the tty driver for the processes "controlling" tty.
UPDATE: to demonstrate that the shell is not involved, I created the following small program. It should be executed by its parent shell via exec ./a.out (it appears an interactive shell will fork a daughter shell, anyway) The program sets the key that generates the SIGINTR to ^B, switches echo off, and than waits for input from stdin.
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
int thesignum = 0;
void handler(int signum);
void handler(int signum)
{ thesignum = signum;}
#define THE_KEY 2 /* ^B */
int main(void)
{
int rc;
struct termios mytermios;
rc = tcgetattr(0 , &mytermios);
printf("tcgetattr=%d\n", rc );
mytermios.c_cc[VINTR] = THE_KEY; /* set intr to ^B */
mytermios.c_lflag &= ~ECHO ; /* Dont echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );
printf("Setting handler()\n" );
signal(SIGINT, handler);
printf("entering pause()\n... type something followed by ^%c\n", '#'+THE_KEY );
rc = pause();
printf("Rc=%d: %d(%s), signum=%d\n", rc, errno , strerror(errno), thesignum );
// mytermios.c_cc[VINTR] = 3; /* reset intr to ^C */
mytermios.c_lflag |= ECHO ; /* Do echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );
return 0;
}
intr.sh:
#!/bin/sh
echo $$
exec ./a.out
echo I am back.
The shell echoes everything you type, so when you type ^C, that too gets echoed (and in your case intercepted by your signal handler). The command stty -echo may or may not be useful to you depending on your needs/constraints, see the man page for stty for more information.
Of course much more goes on at a lower level, anytime you communicate with a system via peripherals device drivers (such as the keyboard driver that you use to generate the ^C signal, and the terminal driver that displays everything) are involved. You can dig even deeper at the level of assembly/machine language, registers, lookup tables etc. If you want a more detailed, in-depth level of understanding the books below are a good place to start:
The Design of the Unix OS is a good reference for these sort of things. Two more classic references: Unix Programming Environment
and Advanced Programming in the UNIX Environment
Nice summary here in this SO question How does Ctrl-C terminate a child process?
"when youre run a program, for example find, the shell:
the shell fork itself
and for the child set the default signal handling
replace the child with the given command (e.g. with find)
when you press CTRL-C, parent shell handle this signal but the child will receive it - with the default action - terminate. (the child can implement signal handling too)"

Resources