最近由於項目的需要,需要將IPC中的日誌同步到SD卡中,以便後續IPC出現問題了進行分析。由於我們程序的架構是多進程的,爲了將所有進程的日誌同步到SD卡中,進程間需要傳遞文件描述符,然後將該描述符重定向即可。
//client.c
#include <stdio.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#define STD_OUT (1)
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int recv_fd(int fd)
{
int newfd, nr, status;
char *ptr;
char buf[256];
struct iovec iov[1];
struct msghdr msg;
status = -1;
for(;;)
{
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if ((cmptr == NULL) && ((cmptr = malloc(CONTROLLEN)) == NULL))
{
return(-1);
}
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
if((nr = recvmsg(fd, &msg, 0)) < 0)
{
printf("recvmsg error");
return(-1);
}
else if(nr == 0)
{
printf("connection close by server");
return(-1);
}
/*
* See if this is the final data with null & status. Null
* is next to last byte to buffer; status byte is last byte.
* Zero status means there is a file descriptor to receive.
*/
for(ptr = buf; ptr < &buf[nr];)
{
if(*ptr++ == 0)
{
if(ptr != &buf[nr - 1])
{
printf("message format error");
}
status = *ptr & 0xFF; /* prevent sign extension */
if(status == 0)
{
if(msg.msg_controllen != CONTROLLEN)
{
printf("status = 0 but no fd");
}
newfd = *(int *)CMSG_DATA(cmptr);
}
else
{
newfd = -status;
}
nr -= 2;
}
}
/* final data has arrived */
if(status >= 0)
{
return(newfd); /* descriptor, or -status */
}
}
}
int main()
{
int sockfd = -1;
int recvfd = -1;
int ret = 0;
int i = 0;
struct sockaddr_un addr_client;
int length;
const char path[] = "/home/guo/work/me/code/f2e/jooan-2020";
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0)
{
printf("client creat socket error!errno is %d\n", errno);
return -1;
}
addr_client.sun_family = AF_UNIX;
strcpy(addr_client.sun_path, path);
length = sizeof(addr_client.sun_family) + sizeof(addr_client.sun_path);
printf("Start connecting to the server\n");
ret = connect(sockfd, (struct sockaddr*)&addr_client, length);
if(ret < 0)
{
printf("in client connect error, errorno is %d\n",errno);
close(sockfd);
}
printf("Connection to server successful, Wait recv server send fd...\n");
recvfd = recv_fd(sockfd);
if(recvfd < 0)
{
printf("in client func_recv_fd failed\n");
close(sockfd);
}
printf("recv fd is %d\n", recvfd);
//重定向文件描述符
dup2(recvfd, STD_OUT);
for (i=0; i<10; i++)
{
printf("I'm the clent sleep(%d)\n", i);
sleep(1);
}
return 0;
}
//server.c
#include <stdio.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <dirent.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#define STD_OUT (1)
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int send_fd(int fd, int fd_to_send)
{
struct iovec iov[1];
struct msghdr msg;
char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
iov[0].iov_base = buf;
iov[0].iov_len = 2;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if(fd_to_send < 0)
{
msg.msg_control = NULL;
msg.msg_controllen = 0;
buf[1] = -fd_to_send; /* nonzero status means error */
if(buf[1] == 0)
{
buf[1] = 1; /* -256, etc. would screw up protocol */
}
}
else
{
if(cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
{
return(-1);
}
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
cmptr->cmsg_len = CONTROLLEN;
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
*(int *)CMSG_DATA(cmptr) = fd_to_send; /* the fd to pass */
buf[1] = 0; /* zero status means ok */
}
buf[0] = 0; /* null byte flag to recv_fd() */
if(sendmsg(fd, &msg, 0) != 2)
return(-1);
return(0);
}
int main(int argc, char *argv)
{
int recvfd = 0;
int ret = 0;
int i = 0;
int on = 1;
int fdsock, fdaccept;
struct sockaddr_un addr_server;
int len;
const char path1[] = "/home/guo/work/me/code/f2e/jooan-2020";
const char path2[] = "/home/guo/work/me/code/f2e/log";
recvfd = open(path2, O_RDWR|O_CREAT|O_TRUNC);
if(recvfd < 0)
{
perror("open:");
printf("open return %d, err...", recvfd);
return -1;
}
chmod(path2, 0777);
printf("open %s fd is %d\n",path2, recvfd);
dup2(recvfd, STD_OUT);
fdsock = socket(AF_UNIX, SOCK_STREAM, 0);
if(-1 == fdsock)
{
printf("myopen creat socket error!errno is %d\n", errno);
return -1;
}
memset(&addr_server, 0, sizeof(addr_server));
addr_server.sun_family = AF_UNIX;
strcpy(addr_server.sun_path, path1);
len = sizeof(struct sockaddr_un);
unlink(addr_server.sun_path);
ret = setsockopt(fdsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
if(ret < 0)
{
printf("setsockopt error, errorno is %d\n",errno);
close(fdsock);
return -1;
}
printf("Start bind...\n");
ret = bind(fdsock, (struct sockaddr*)&addr_server, len);
if(ret < 0)
{
printf("in myopen bind error, errorno is %d\n",errno);
close(fdsock);
return -1;
}
printf("Start listen...\n");
ret = listen(fdsock,1);
if(ret < 0)
{
printf("in myopen listen error, errorno is %d\n",errno);
close(fdsock);
return -1;
}
printf("Start accept...\n");
fdaccept = accept(fdsock, (struct sockaddr*)&addr_server, &len);
if(ret < 0)
{
printf("in myopen accept error, errorno is %d\n",errno);
close(fdsock);
return -1;
}
/* 向已經連接的client傳遞文件描述符 */
printf("Start passing the fd to the client...\n");
send_fd(fdaccept, recvfd);
printf("send fd to client ...\n");
for (i=0; i<10; i++)
{
printf("I'm the server sleep(%d)\n", i);
sleep(1);
}
return 0;
}
編譯運行結果如下:
參考文檔:
https://www.cnblogs.com/nufangrensheng/p/3571370.html
https://blog.csdn.net/genzld/article/details/84639298
https://blog.csdn.net/IOT_SHUN/article/details/80922734
https://blog.csdn.net/KingOfMyHeart/article/details/90272050