|
| 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