Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5d7997f

Browse files
author
Anton Yarkov
committed
Socketpair... fd passing.
1 parent b6e1d66 commit 5d7997f

File tree

1 file changed

+286
-0
lines changed

1 file changed

+286
-0
lines changed
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <unistd.h>
4+
#include <netinet/in.h>
5+
#include <sys/types.h>
6+
#include <sys/wait.h>
7+
#include <sys/socket.h>
8+
9+
#define EXIT_FAILURE 1
10+
11+
// Compilation:
12+
// gcc "05 - Socketpair_fd_passing.c" -o socketpairfdpassing
13+
14+
// Task:
15+
// Use of FD-passing technology to let 2 processes/programs communicate with each other and send sockets.
16+
// This technology should be helpful in programming multiprocess server programming to handle socket connections
17+
// in child processes instead of single parent process.
18+
19+
void kill_child_handler(int sig)
20+
{
21+
int status;
22+
pid_t done = waitpid(
23+
-1, // Any child
24+
&status,
25+
0); // Blocked mode.
26+
if (done == -1)
27+
{
28+
printf("No more child processes.\n", done);
29+
}
30+
else
31+
{
32+
short isNormalTermination = WIFEXITED(status);
33+
if (!isNormalTermination ||
34+
// WEXITSTATUS should be used only if normal termination = true.
35+
(isNormalTermination && WEXITSTATUS(status) != 0))
36+
{
37+
printf("Zombie for PID -- %d failed.\n", done);
38+
exit(EXIT_FAILURE);
39+
}
40+
else
41+
{
42+
printf("Zombie for PID -- %d successfully removed.\n", done);
43+
}
44+
}
45+
}
46+
47+
// Send information in a buffer via socket and attached additional information with that buffer i.e. our file descriptor.
48+
// We may send several file descriptors, but this is a simple example.
49+
ssize_t sock_fd_write(int sock, void *buf, ssize_t buflen, int fd)
50+
{
51+
// We should send at least 1 byte of any information except file descriptor itself.
52+
if (buflen < 0)
53+
{
54+
return -1;
55+
}
56+
57+
// IOVector - describes information buffer.
58+
struct iovec iov;
59+
iov.iov_base = buf;
60+
iov.iov_len = buflen;
61+
62+
struct msghdr msg;
63+
// Send only one buffer IOVector, however sendmsg() may work with several buffers by merging them together.
64+
msg.msg_name = NULL;
65+
msg.msg_namelen = 0;
66+
msg.msg_iov = &iov;
67+
msg.msg_iovlen = 1;
68+
69+
if (fd != -1)
70+
{
71+
// Declare it here because it may be absent in headers.
72+
union
73+
{
74+
struct cmsghdr cmsghdr;
75+
char control[CMSG_SPACE(sizeof(int))];
76+
} cmsgu;
77+
78+
// System info.
79+
msg.msg_control = cmsgu.control;
80+
msg.msg_controllen = sizeof(cmsgu.control);
81+
82+
// Use constans and macroses to fill info.
83+
struct cmsghdr *cmsg;
84+
cmsg = CMSG_FIRSTHDR(&msg);
85+
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
86+
cmsg->cmsg_level = SOL_SOCKET;
87+
cmsg->cmsg_type = SCM_RIGHTS;
88+
printf("passing fd %d\n", fd);
89+
*((int *)CMSG_DATA(cmsg)) = fd;
90+
}
91+
else
92+
{
93+
// We don't need to send file descriptor - work in usual sending mode.
94+
msg.msg_control = NULL;
95+
msg.msg_controllen = 0;
96+
printf("not passing fd\n");
97+
}
98+
99+
// Sending data via sockets, similar to sendto.
100+
ssize_t size = sendmsg(sock, &msg, 0);
101+
if (size < 0)
102+
perror("sendmsg");
103+
104+
return size;
105+
}
106+
107+
ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize,
108+
// Result file descriptor.
109+
int *fd)
110+
{
111+
ssize_t size;
112+
if (fd)
113+
{
114+
struct iovec iov;
115+
iov.iov_base = buf;
116+
iov.iov_len = bufsize;
117+
118+
struct msghdr msg;
119+
msg.msg_name = NULL;
120+
msg.msg_namelen = 0;
121+
msg.msg_iov = &iov;
122+
msg.msg_iovlen = 1;
123+
124+
union
125+
{
126+
struct cmsghdr cmsghdr;
127+
char control[CMSG_SPACE(sizeof(int))];
128+
} cmsgu;
129+
msg.msg_control = cmsgu.control;
130+
msg.msg_controllen = sizeof(cmsgu.control);
131+
132+
size = recvmsg(sock, &msg, 0);
133+
134+
if (size < 0)
135+
{
136+
perror("recvmsg");
137+
exit(EXIT_FAILURE);
138+
}
139+
140+
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
141+
if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
142+
{
143+
if (cmsg->cmsg_level != SOL_SOCKET)
144+
{
145+
fprintf(stderr, "invalid cmsg_level %d\n",
146+
cmsg->cmsg_level);
147+
exit(EXIT_FAILURE);
148+
}
149+
if (cmsg->cmsg_type != SCM_RIGHTS)
150+
{
151+
fprintf(stderr, "invalid cmsg_type %d\n",
152+
cmsg->cmsg_type);
153+
exit(EXIT_FAILURE);
154+
}
155+
*fd = *((int *)CMSG_DATA(cmsg));
156+
printf("received fd %d\n", *fd);
157+
}
158+
else
159+
*fd = -1;
160+
}
161+
else
162+
{
163+
size = read(sock, buf, bufsize);
164+
if (size < 0)
165+
{
166+
perror("read");
167+
exit(EXIT_FAILURE);
168+
}
169+
}
170+
171+
return size;
172+
}
173+
174+
void child_read(int sock)
175+
{
176+
int fd;
177+
char buf[16];
178+
ssize_t size;
179+
sleep(1);
180+
181+
for (;;)
182+
{
183+
size = sock_fd_read(sock, buf, sizeof(buf), &fd);
184+
if (size <= 0)
185+
break;
186+
printf ("read size: %d, fd: %d\n", size, fd);
187+
if (fd != -1)
188+
{
189+
write(fd, "hello, world\n", 13);
190+
close(fd);
191+
}
192+
}
193+
}
194+
195+
void parent_writes(int sock)
196+
{
197+
ssize_t size = sock_fd_write(sock, "1", 1, 1);
198+
printf ("wrote size: %d\n", size);
199+
}
200+
201+
/*
202+
// Multiple writes were made, some with file descriptors and some without
203+
void parent_writes(int sock)
204+
{
205+
ssize_t size = sock_fd_write(sock, "1", 1, -1);
206+
printf ("wrote %d without fd\n", size);
207+
size = sock_fd_write(sock, "1", 1, 1);
208+
printf ("wrote %d with fd\n", size);
209+
size = sock_fd_write(sock, "1", 1, -1);
210+
printf ("wrote %d without fd\n", size);
211+
}
212+
*/
213+
214+
/*
215+
216+
// This shows that the first passed file descriptor is picked up by the first sock_fd_read call, but the file descriptor is closed.
217+
// The second file descriptor passed is picked up by the second sock_fd_read call.
218+
void child_read(int sock)
219+
{
220+
int fd;
221+
char buf[16];
222+
ssize_t size;
223+
sleep(1);
224+
size = sock_fd_read(sock, buf, sizeof(buf), NULL);
225+
if (size <= 0)
226+
return;
227+
printf ("read %d\n", size);
228+
size = sock_fd_read(sock, buf, sizeof(buf), &fd);
229+
if (size <= 0)
230+
return;
231+
printf ("read %d\n", size);
232+
if (fd != -1)
233+
{
234+
write(fd, "hello, world\n", 13);
235+
close(fd);
236+
}
237+
}
238+
239+
void parent_writes(int sock)
240+
{
241+
ssize_t size = sock_fd_write(sock, "1", 1, 1);
242+
printf ("wrote %d without fd\n", size);
243+
size = sock_fd_write(sock, "1", 1, 2);
244+
printf ("wrote %d with fd\n", size);
245+
}
246+
*/
247+
248+
int main(int argc, char **argv)
249+
{
250+
int sv[2];
251+
int pid;
252+
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
253+
{
254+
perror("socketpair");
255+
exit(EXIT_FAILURE);
256+
}
257+
258+
// Handle child process killing.
259+
struct sigaction kill_child_signal;
260+
kill_child_signal.sa_handler = kill_child_handler;
261+
sigemptyset(&kill_child_signal.sa_mask);
262+
kill_child_signal.sa_flags = SA_RESTART; // Permanent handler.
263+
264+
if (sigaction(SIGCHLD, &kill_child_signal, 0) == -1)
265+
{
266+
perror("Error of calling sigaction");
267+
exit(EXIT_FAILURE);
268+
}
269+
270+
switch ((pid = fork()))
271+
{
272+
case 0:
273+
close(sv[0]);
274+
child_read(sv[1]);
275+
break;
276+
case -1:
277+
perror("fork");
278+
exit(EXIT_FAILURE);
279+
default:
280+
close(sv[1]);
281+
parent_writes(sv[0]);
282+
break;
283+
}
284+
285+
return 0;
286+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /