2

I have two processes on Linux, A & B. I want to share the file descriptor from process A with process B, now I just serialize it to a char* and pass it to the execl parameters, but that doesn't work.

A.c looks like this:

union descriptor{
    char c[sizeof(int)];
    int i;
} fd;
pid_t pid;

fd.i = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Perform other socket functions

pid = fork();
if(pid == 0){
    // Read data from socket
    if(execl("./B", fd.c, NULL) < 0){
        exit(EXIT_FAILURE);
    }else(
        exit(EXIT_SUCCESS);
    }
}else if(pid < 0){
    exit(EXIT_FAILURE);
}else{
    waitpid(pid, NULL, 0);
}

B.c looks like this:

union descriptor{
    char c[sizeof(int)];
    int i;
} fd;

memcpy(&fd.c[0], argv[0], sizeof(int));

write(fd.i, "TEST", 4);
close(fd.i);

But this doesn't work, and I don't really understand why not. How can I make this work? And if it works, is it the best solution to share a file descriptor between a parent and a child after a fork and a exec?

Update

The problem is unrelated to the question I asked, it is caused by a wrong way of passing an integer as pointed out by @OliCharlesworth. Please close this question.

tversteeg
  • 4,717
  • 10
  • 42
  • 77
  • 1
    Also, you're using `strcpy` on binary data? – Oliver Charlesworth Feb 02 '14 at 15:33
  • `argv[0]` is the file-descriptor passed by `execl`. – tversteeg Feb 02 '14 at 15:33
  • I might be wrong but argv[0] should be the name of the process, try argv[1] Follow @OliCharlesworth suggestion as well, and call a memcpy to copy sizeof(int) bytes instead of a strcpy (that could led you to a SEGFAULT) – sergico Feb 02 '14 at 15:38
  • No it actually is `argv[0]`, I tested that. And @OliCharlesworth I made an edit for the `strcpy`. – tversteeg Feb 02 '14 at 15:42
  • Ok, in future, when you're asking about problems with some code, please be sure to post the *actual code* verbatim, not something that kind-of-looks-like-the-code, to avoid needless distractions. – Oliver Charlesworth Feb 02 '14 at 15:43
  • Yes I am sorry, I forgot that detail; I didn't want to clutter the question with unnecessary code. – tversteeg Feb 02 '14 at 15:45
  • 2
    Also, `execl` expects arguments that are real strings, not arbitrary binary data. Basically, I think you should first confirm the mechanism of correctly getting the value of an integer from one place to the other, and only then worry about the process/socket business. – Oliver Charlesworth Feb 02 '14 at 15:53
  • You were correct about getting the integer across, I corrected it and now it works. I feel so stupid.. – tversteeg Feb 02 '14 at 16:10
  • 1
    Always a useful learning experience, right? I think here the lesson is to solve a problem in pieces, rather than all in one go. – Oliver Charlesworth Feb 02 '14 at 16:14

2 Answers2

6

File descriptors are always passed between a parent and child process

When you fork a process, the file descriptors that are open in the parent(at the time of fork()) are implicitly passed on to the child. There is no need to send them explicitly.

For example:

The pseudo-code looks as follows:

In process A:

fd = open_socket_or_file;
char str_fd[3];
str_fd[0]=fd;
str_fd[1]=fd;
str_fd[2]=0;
if(fork()==0)
{
     execl("./B",str_fd,NULL);
}

In the child process B you can do:

int fd = argv[1][0];
/* now do whatever you want with the fd...*/

EDIT:

In case the processes are different, you need to pass the file descriptor explicitly. This is generally done using UNIX-Domain Sockets(If you are using Linux Flavors). For code related to this, you can see this answer

Community
  • 1
  • 1
nitish712
  • 19,504
  • 5
  • 26
  • 34
5

Yes that is true that file descriptors remain open even after fork or exec or fork and exec.You only need to know the value of fd in the new process image that was replaced using exec else put that fd on the one which is already known to that process(ex:0,1,2). So you can do this in two ways:

  • Placing the fd on either one of standard file descriptors using dup2(note:as far as i know you will be unable to reset that standard file descriptor for which it was actually known for)

  • Passing the fd as string argument for one of 6 exec functions does the job

Therefore I suggest you to use second method in case if you want standard fds remain

These are the two methods of implementation:

P1.c(using argument passing)

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
    printf("Hello this is process 1\n");
    int fd=open("./foo",O_RDONLY);
    char buf[255];
    //int n=read(fd,buf,255);
    int h=fork();
    if(h==0)
    {
        char *fname="./p2";
        char *arg[3];
        char targ[10];
        sprintf(targ,"%d",fd);
        arg[0]=fname;
        arg[1]=targ;
        arg[2]=NULL;
        execvp(fname,arg);
    }
    else
    {
        printf("This is from p1 process\n");
        //write(1,buf,strlen(buf));
                    //do some process with p1
        printf("This is end of p1 process\n");
    }
}

P1.c(using dup2 with 0)

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
    printf("Hello this is process 1\n");
    int fd=open("./foo",O_RDONLY);
    int h=fork();
    if(h==0)
    {
        dup2(fd,0);//note we will be loosing standard input in p2
        execvp(fname,NULL);
    }
    else
    {
        printf("This is from p1 process\n");
        //write(1,buf,strlen(buf));
                    //do some process with p1
        printf("This is end of p1 process\n");
    }
}

P2.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc,char *argv[])
{
    int fd=atoi(argv[1]);      //here fd=0 in case dup2 in process ps1.c
    char buf[1024];
    int n=read(fd,buf,1024);
    buf[n]='\0';
    printf("This is from p2\n");
    write(1,buf,strlen(buf));
}
Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
IronMan007
  • 105
  • 4
  • 14
  • 1
    I would strongly recommend against the variant using `dup2()` -- it's unnecessary complexity and either implicitly closes a widely known and potentially useful open descriptor, or relies on hard-coded assumptions about some magic number and whether or not the target descriptor might already be open and in use for some other purpose. – Greg A. Woods Feb 11 '16 at 01:07
  • Yes!, agreed, even I suggest to use passing as argument instead of using dup2() :) – IronMan007 Feb 11 '16 at 04:11
  • Many years later, but I figured I'd chime in. For the cases where you *actually want to control* PS2's std fds, the dup2 choice is the only choice you have. For instance, if the application you are writing is a debugger, and you have no idea what PS2 does, then dup is perfectly acceptable. – Simon Farre Jul 08 '23 at 15:07