Skip to content

Instantly share code, notes, and snippets.

@nazgee
Created April 16, 2012 07:52
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save nazgee/2396992 to your computer and use it in GitHub Desktop.
Save nazgee/2396992 to your computer and use it in GitHub Desktop.
linux file descriptors passing between processes
#include <errno.h>
#include <string.h>
#include <iostream>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#define LOGD(...) do { printf(__VA_ARGS__); printf("\n"); } while(0)
#define LOGE(...) do { printf(__VA_ARGS__); printf("\n"); } while(0)
#define LOGW(...) do { printf(__VA_ARGS__); printf("\n"); } while(0)
using namespace std;
/**
* Sends given file descriptior via given socket
*
* @param socket to be used for fd sending
* @param fd to be sent
* @return sendmsg result
*
* @note socket should be (PF_UNIX, SOCK_DGRAM)
*/
int sendfd(int socket, int fd) {
char dummy = '$';
struct msghdr msg;
struct iovec iov;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
iov.iov_base = &dummy;
iov.iov_len = sizeof(dummy);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = cmsgbuf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*(int*) CMSG_DATA(cmsg) = fd;
int ret = sendmsg(socket, &msg, 0);
if (ret == -1) {
LOGE("sendmsg failed with %s", strerror(errno));
}
return ret;
}
/**
* Receives file descriptor using given socket
*
* @param socket to be used for fd recepion
* @return received file descriptor; -1 if failed
*
* @note socket should be (PF_UNIX, SOCK_DGRAM)
*/
int recvfd(int socket) {
int len;
int fd;
char buf[1];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
char cms[CMSG_SPACE(sizeof(int))];
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = (caddr_t) cms;
msg.msg_controllen = sizeof cms;
len = recvmsg(socket, &msg, 0);
if (len < 0) {
LOGE("recvmsg failed with %s", strerror(errno));
return -1;
}
if (len == 0) {
LOGE("recvmsg failed no data");
return -1;
}
cmsg = CMSG_FIRSTHDR(&msg);
memmove(&fd, CMSG_DATA(cmsg), sizeof(int));
return fd;
}
#define TXTSRV "server\n"
#define TXTCLI "client\n"
void main_server(int socket) {
int fd;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
char filename[] = "/tmp/file";
fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, mode);
write(fd, TXTSRV, strlen(TXTSRV));
sendfd(socket, fd);
close(fd);
}
void main_client(int socket) {
int fd = recvfd(socket);
write(fd, TXTCLI, strlen(TXTCLI));
close(fd);
}
int main() {
int pid;
int pair[2];
cout << "started..." << endl;
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, pair) < 0) {
cout << "socketpair failed" << endl;
return 1;
}
if ((pid = fork()) < 0) {
cout << "fork failed" << endl;
return 1;
}
if (pid != 0) {
cout << "i am a parent" << endl;
close(pair[0]);
main_server(pair[1]);
} else {
cout << "i am a child" << endl;
close(pair[1]);
main_client(pair[0]);
}
return 0;
}
@pbrit
Copy link

pbrit commented Dec 12, 2017

include <unistd.h> is missing.

@jonas-schievink
Copy link

According to the example in cmsg(3), msg.msg_controllen must be set to the raw buffer size, so the use of CMSG_LEN needs to be replaced with CMSG_SPACE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment