Reputation: 106
There is scenario where one process open serial port and configure it's terminal parameters. How to share this file descriptor to another two processes which will also pass data over serial port. is it possible to share opened file descriptor? if yes then, how is it possible?
EDIT: note one thing these three processes are not related processes (not parent/child processes)
Upvotes: 1
Views: 770
Reputation: 20392
The easiest thing is to open and configure the file descriptor before forking off the other processes. This will save you the most headaches.
Assuming that is not possible, there is a way.
Create a unix domain socket. Make the main process listen on that socket. As soon as someone connects to it, send a cmsg with SCM_RIGHTS. On my linux system this is documented on the man page "cmsg(3)". On my MacOS (where I wrote the example code) I couldn't find where it's documented.
Now, in the processes that need a copy of the special file descriptor, open the unix domain socket read the cmsg and you now have the file descriptor.
Here's some example code. This is long because the whole process is quite verbose. What it will do is without sharing any file descriptors it will send the descriptor to the file '/tmp/magic' to the client and the client will write a message to it. The file descriptor passed is in the local variable magic
in both the client and server.
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#include <err.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#define SOCKET_NAME "/tmp/socket"
#define SPECIAL_FILE "/tmp/magic"
void
server(void)
{
struct sockaddr_un sun = { 0 };
socklen_t sunlen;
int l, fd, magic;
if ((magic = open(SPECIAL_FILE, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1)
err(1, "open");
if ((l = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "server socket");
unlink(SOCKET_NAME);
sun.sun_family = AF_UNIX;
snprintf(sun.sun_path, sizeof(sun.sun_path), SOCKET_NAME);
if (bind(l, (struct sockaddr *)&sun, sizeof(sun)) == -1)
err(1, "bind");
if (listen(l, 5) == -1)
err(1, "listen");
sunlen = sizeof(sun);
while ((fd = accept(l, (struct sockaddr *)&sun, &sunlen)) != -1) {
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
struct iovec iov;
char buf[CMSG_SPACE(sizeof(magic))];
iov.iov_base = (void *)"ok";
iov.iov_len = 2;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(magic));
*((int *)(void *)CMSG_DATA(cmsg)) = magic;
if (sendmsg(fd, &msg, 0) == -1)
err(1, "sendmsg");
close(fd);
}
close(l);
unlink(SOCKET_NAME);
}
void
client(void)
{
struct sockaddr_un sun = { 0 };
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
struct iovec iov;
int fd, magic;
char cbuf[CMSG_SPACE(sizeof(int))];
char buf[16];
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
err(1, "socket");
}
sun.sun_family = AF_UNIX;
snprintf(sun.sun_path, sizeof(sun.sun_path), SOCKET_NAME);
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
err(1, "connect");
}
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
if (recvmsg(fd, &msg, 0) == -1)
err(1, "recvmsg");
cmsg = CMSG_FIRSTHDR(&msg);
assert(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS);
magic = *(int *)(void *)CMSG_DATA(cmsg);
if (write(magic, "hello", strlen("hello")) != strlen("hello"))
err(1, "write(magic)");
close(magic);
close(fd);
}
int
main(int argc, char **argv)
{
switch (fork()) {
case 0:
sleep(2); /* poor mans synchronization */
printf("starting client\n");
client();
_exit(0);
break;
case -1:
err(1, "fork");
default:
printf("starting server\n");
server();
exit(1);
}
return 1;
}
Some notes about the example:
malloc
or even posix_memalign
.Upvotes: 3