Forking in Linux and the use of setsid() - c

I have a sample code and I'm at loss in understanding how to figure out what's happening.
I'm only showing relevant parts. The problem is make_daemon().
From what I understand about forking is that code from close(0) onwards is executed by the child which should have a pid == 0.
What happens when the code hits return -1? Does the code return to the parent or does it exit? Does the child p process code execute if(share) in Monitor()?
This code is an extract from Monitor.c in mdadm.
Thanks in advance for any help.
int Monitor( struct mddev_dev *devlist,
char *mailaddr, char *alert_cmd,
struct context *c,
int daemonise, int oneshot,
int dosyslog, char *pidfile, int increments,
int share )
{
if (daemonise) {
int rv = make_daemon(pidfile);
if (rv >= 0)
return rv;
}
if (share)
if (check_one_sharer(c->scan))
return 1;
/* etc .... */
}
static int make_daemon(char *pidfile)
{
int pid = fork();
if (pid > 0) {
if (!pidfile)
printf("%d\n", pid);
else {
FILE *pid_file;
pid_file=fopen(pidfile, "w");
if (!pid_file)
perror("cannot create pid file");
else {
fprintf(pid_file,"%d\n", pid);
fclose(pid_file);
}
}
return 0;
}
if (pid < 0) {
perror("daemonise");
return 1;
}
close(0);
open("/dev/null", O_RDWR);
dup2(0,1);
dup2(0,2);
setsid();
return -1;
}

fork can return three types of return values:
a positive number: this only happens in the parent: it is the pid of a successfully created child.
zero: this is only returned in the child and indicates that this code now executes in the child. This is not the pid of the child, use getpid to obtain the pid of the child.
a negative value (commonly -1). This is also only ever returned in the parent and indicates that fork failed for whatever reason and no child was created.
As to what your code does: yes the child will continue at close(0); provided that a child was indeed created.
When your cild hits return -1 it will return to whatever function called make_daemon back in the parent and will continue execution at that point. Normally forked children would do whatever they are supposed to do and then call exit in order not to mess up what the parent was doing.

Related

Amount of process

Do I understand correctly that 2 processes are created in the code below? Since one fork () call creates one thread. There are two such calls in the code, therefore, the process is created 2?
#include <stdio.h>
#include <stdlib.h>
int main() {
int x,y;
pid_t pidl, pid2;
x = 2;
y = 3;
printf ("Single process, x=$d\n",x);
pidl = fork();
if(pidl == 0) {
printf ("New, x=%d\n",x);
exit (0);
}
if(pid1 < 0){
printf("Cannot create");
exit (1);
}
pid2=fork();
if(pid2 == 0) {
printf ("New, y=%d\n",y);
exit (0);
}
if(pid2 < 0){
printf("Cannot create");
exit (1);
}
return 0;
}
Yes. This code create two child process.
However you do not make the relation between parent and child processes by using wait function.
Therefore result of code change variously.
Child process 1 can print result before / after parent process terminated.
Child process 2 can print result before / after parent process terminated.
Maybe this variable result confuse you.

How to properly fork() a process

I'm trying to understand how to properly used fork() and execvp() to execute a command. So far I have the following code:
When I run ./test vim myFile.c it correctly opens myFile.c but I get strange behavior. It seems as though there are two processes running because whenever I enter anything it seems to happen twice. Why is this?
int main (int argc, char* argv[]) {
int fdin, pid, w, status;
fdin = 0;
if ((pid = fork()) < 0)
errorExit (EXIT_FAILURE);
execvp(argv[0],argv);
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status))
}
When you call fork(), you create two almost completely identical processes, the parent process and the child process. The only difference between these two processes is the return value of fork(), which returns 0 to the child process and the pid of the child to the parent.
Hence, assuming fork succeeded, fork will return a nonnegative integer to both the parent and child process in line 4. Then, both the parent and child process will execute line 6, the execvp, and hence, you end up with two different processes running your vim myFile.c, causing all the problems you described.
The standard idiom is something like:
if ((pid = fork()) < 0) {
// Handle fork error
}
else if (pid == 0) {
// Child process
execvp(...);
}
else {
// Parent process
w = waitpid(pid, ...);
}
Since the return value for fork is 0 for the child, after fork succeeds, the test (pid == 0) will be true for the child, so execvp will be called.
For the parent, fork returns the pid of the child, so the check (pid == 0), which still get executed, is false, so the else condition is executed, causing the parent to wait for the child.
Both parent and child in your program get execvp():
if ((pid = fork()) < 0)
errorExit (EXIT_FAILURE);
execvp(argv[0],argv);
You should check, if you are in parent with pid != 0, and if you are in child otherwise.
You should look at the return value of fork, after a successful fork you will have two running processes at the same position in your program. The child process will get a return value of 0, the parent will get a return value which is the pid of the child. Most likely you want to do different things in the child process and the parent process.
You might also want to think again about how execvp is called. Do you really want to give "./test" as the first argument to execvp?

whether a function returned in the child process can be captured in the parent process

I'm currently implementing the && function in a shell using C. For example, if we input cmd1 && cmd2, then cmd2 executes only when cmd1 exits successfully. I'm thinking about:
int main() {
int i;
char **args;
while(1) {
printf("yongfeng's shell:~$ ");
args = get_line();
if (strcmp(args[0], "exit") == 0) exit(0); /* if it's built-in command exit, exit the shell */
if('&&') parse_out_two_commands: cmd1, cmd2;
if (execute(cmd1) != -1) /* if cmd1 successfully executed */
execute(cmd2); /* then execute the second cmd */
}
}
int execute(char **args){
int pid;
int status; /* location to store the termination status of the terminated process */
char **cmd; /* pure command without special charactors */
if(pid=fork() < 0){ //fork a child process, if pid<0, fork fails
perror("Error: forking failed");
return -1;
}
/* child */
else if(pid==0){ /* child process, in which command is going to be executed */
cmd = parse_out(args);
/* codes handleing I/O redirection */
if(execvp(*cmd, cmd) < 0){ /* execute command */
perror("execution error");
return -1;
}
return 0;
}
/* parent */
else{ /* parent process is going to wait for child or not, depends on whether there's '&' at the end of the command */
if(strcmp(args[sizeof(args)],'&') == 0){
/* handle signals */
}
else if (pid = waitpid(pid, &status, 0) == -1) perror("wait error");
}
}
So I'm using another function int execute(char ** args) to do the actual work. Its return type is int because I wan to know whether the command exits successfully. But I'm not sure here whether the parent process can get the return value from the child since they're two different processes.
Or should I decide whether to execute the second command in the child process, by forking another process to run it? Thanks a lot.
Change:
if(pid=fork() < 0){ //fork a child process, if pid<0, fork fails
to:
if((pid=fork()) < 0){ //fork a child process, if pid<0, fork fails
You're setting pid to the result of fork() < 0, not setting it to the PID of the child. So unless there's an error in fork(), this sets pid to 0 in both the parent and child, so they both think they're the child.
Regarding the return value of the execute() function: It will return in both the parent and child. In each process, it will return whatever was specified in the return statement in the corresponding branch of the if in execute(). Note that it execve() is successful, the child never returns, because it's no longer running this program, it's running the program that was exec'ed.
If the child wants to send success or failure information to the parent, it does this using its exit status, by calling exit(0) to indicate success, and exit(some-nonzero-value) to indicate failure. The parent can get the exit status using waitpid, and then return a success or failure indication from execute().

More Information on Child Process Termination?

I googled for answer but all the threads I found seemed to suggest using an alternative way to terminate a child process: the _Exit() function.
I wonder if using "return 0;" truly terminate the child process? I tested that in my program (I have waitpid() in the parent process to catch the termination of the child process), and it seemed to work just fine.
So can someone please confirm on this question? Does a return statement truly terminate a process like the exit function or it simply sends a signal indicating the calling process is "done" while the process is actually still running?
Thanks in advance,
Dan
Sample Code:
pid = fork()
if (pid == 0) // child process
{
// do some operation
return 0; // Does this terminate the child process?
}
else if (pid > 0) // parent process
{
waitpid(pid, &status, 0);
// do some operation
}
Using the return statement inside the main function will immediately terminate the process and return the value specified. The process is terminated completely.
int main (int argc, char **argv) {
return 2;
return 1;
}
This program never reaches the second return statement, and the value 2 is returned to the caller.
EDIT - Example from when the fork happens inside another function
However if the return statement is not inside the main function, the child process will not terminate until it has reached down into main() again. The code below will output:
Child process will now return 2
Child process returns to parent process: 2
Parent process will now return 1
Code (tested on Linux):
pid_t pid;
int fork_function() {
pid = fork();
if (pid == 0) {
return 2;
}
else {
int status;
waitpid (pid, &status, 0);
printf ("Child process returns to parent process: %i\n", WEXITSTATUS(status));
}
return 1;
}
int main (int argc, char **argv) {
int result = fork_function();
if (pid == 0) {
printf ("Child process will now return %i\n", result);
}
else {
printf ("Parent process will now return %i\n", result);
}
return result;
}

Recursive Fibonacci using Fork in C (Pt 2)

I'm attempting to write a function that recursively computes the resulting fibonacci number from a given int n using forks in C.
Here is the function specification: If doPrint is true, print it. Otherwise, provide it to the parent process. The solution should be recursive and it must fork a new child for each call. Each process should call doFib() exactly once. The method signature cannot be changed. Helper functions cannot be used.
This is a continuation of this question: Recursive Fibonacci using Fork (in C)
Unfortunately, I never figured out a solution to the problem in the last post, however this is my modified code. I thought I had it figured out (psuedo code wise) but came to find out that I still am unsure about a few pieces.
At this point, this is solely for my amusement. This is not homework and won't be covered in my class again (after the most recent test, which has passed).
static pid_t root_pid;
// Function to return exit code for PID
static int exitcode(pid_t pid)
{
pid_t retpid;
int status;
retpid = waitpid(pid, &status, 0);
if (pid != retpid)
{
printf("waitpid error\n");
}
return WEXITSTATUS(status);
}
static void doFib(int n, int doPrint)
{
root_pid = getpid();
pid_t pid1;
int status1;
pid_t pid2;
int status2;
if(n < 2) // Base case, exit back to parent?
{
exit(n);
}
else // if not base case, fork child processes
{
pid1 = fork();
if (pid1 == 0) // Child Process 1 (for fib(n-1))
{
doFib(n-1, doPrint);
exit(n-1);
}
else if (pid1 > 0) // Parent Process
{
pid2 = fork();
if (pid2 == 0) // Child Process 2 (for fib(n-2))
{
doFib(n-2, doPrint);
exit(n-2);
}
// Get value from child process 1
status1 = exitcode(pid1);
// Get value from child process 2
status2 = exitcode(pid2);
// When to print?
if (getpid() == root_pid)
{
int result = status1 + status2;
if (doPrint)
{
printf("%d\n", result);
}
else
{
exit(result);
}
}
}
}
}
A few questions...
Do I need to call both of these functions for each child process?
doFib(n-1, doPrint); exit(n-1);
Is my base case at the beginning correct? (n < 2)
Is my base case at the end correct? (when to print)
Thank you for any help.
The answer for "when to print" really comes down to what you want to print ... if you only want to print the final answer, then you'll most likely need a flag that indicates when you're in the root parent process, and use the if statement to test if you are indeed the root parent so that you only print a single number. If on the other-hand you want to print the entire sequence up to the final number, then an if statement is not needed.
For instance, a good flag value would be the PID of the root process. You could save this in a global variable called root_pid in the first couple lines of main() before you start your forking off of separate child processes. That way all the child processes will have the same value set for root_pid, and the if statement can simply be if (getpid() == root_pid).
So do something like this:
//fib.c
#include <unistd.h>
pid_t root_pid
int main()
{
root_pid = getpid();
//... rest of your program
}
And as mentioned above, make your if statement inside of doFib the following:
if (getpid() == root_pid)
{
//...print results
}
else
{
exit(result)
}

Resources