UNIXworkcode

/* * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE * Version 2, December 2004 * * Copyright (C) 2017 Olaf Wintermann <olaf.wintermann@gmail.com> * * Everyone is permitted to copy and distribute verbatim or modified * copies of this license document, and changing it is allowed as long * as the name is changed. * * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION * * 0. You just DO WHAT THE FUCK YOU WANT TO. */ #define _POSIX_C_SOURCE 200112L #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <fcntl.h> /* * sends a buffer and an additional file descriptor to a socket */ ssize_t send_fd(int sock, const char *buf, size_t len, int fd); /* * receives a buffer and an additional file descriptor from a socket */ ssize_t recv_fd(int sock, char *buf, size_t len, int *fd); void child_process(); const char *socket_path = "fd.sock"; int main(int argc, char **argv) { // create unix domain socket struct sockaddr_un addr; memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, socket_path); int sock = socket(AF_UNIX, SOCK_STREAM, 0); if(sock == -1) { perror("socket"); return -1; } if(bind(sock, (struct sockaddr*)&addr, sizeof(addr))) { perror("bind"); return -1; } if(listen(sock, 8) == -1) { perror("listen"); return -1; } // create new process pid_t pid = fork(); if(pid == 0) { close(sock); child_process(); return 0; } // accept clients int fd = accept(sock, NULL, 0); if(fd == -1) { perror("accept"); return -1; } printf("client connected\n"); // open test file mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; int file = open("testfile", O_WRONLY | O_CREAT, mode); printf("testfile fd = %d\n", file); // send fd const char *msg = "Hello World!"; send_fd(fd, msg, strlen(msg), file); int status; waitpid(pid, &status, 0); // cleanup close(file); close(fd); close(sock); if(unlink(socket_path)) { perror("unlink"); } return 0; } void child_process() { // connect to a unix domain socket struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, socket_path); int client = socket(AF_UNIX, SOCK_STREAM, 0); if(client == -1) { perror("client: socket"); return; } if(connect(client, (struct sockaddr*)&addr, sizeof(addr))) { perror("client: connect"); return; } printf("client connected\n"); // receive data and fd char buf[1024]; int fd; ssize_t r = recv_fd(client, buf, 1024, &fd); printf("msg: {%.*s}\n", (int)r, buf); printf("fd: %d\n", fd); if(r > 0 && fd != -1) { printf("write msg to fd\n"); write(fd, buf, r); close(fd); } close(client); } ssize_t send_fd(int sock, const char *buf, size_t len, int fd) { struct msghdr msg; msg.msg_name = NULL; // unused in this case msg.msg_namelen = 0; // a message contains an iovec for the normal payload data struct iovec iov[1] ; iov[0].iov_base = (void*)buf; iov[0].iov_len = len; msg.msg_iov = iov; msg.msg_iovlen = 1; // set ancillary data // the ancillary data is a struct cmsghdr followed by additional data // in this case just an int for the file descriptor, but you can send // multiple file descriptors with an array of ints // CMSG_SPACE returns the number of bytes needed for the struct and the // payload data char cmsg_buf[CMSG_SPACE(sizeof(int))]; struct cmsghdr *cmsg = (struct cmsghdr*)cmsg_buf; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; // indicates fd payload data int *data = (int*)CMSG_DATA(cmsg); *data = fd; msg.msg_control = cmsg; msg.msg_controllen = CMSG_LEN(sizeof(int)); msg.msg_flags = 0; return sendmsg(sock, &msg, 0); } ssize_t recv_fd(int sock, char *buf, size_t len, int *fd) { // just fill in the msghdr fields like in the send_fd function struct msghdr msg; msg.msg_name = NULL; msg.msg_namelen = 0; struct iovec iov[1]; iov[0].iov_base = buf; iov[0].iov_len = len; msg.msg_iov = iov; msg.msg_iovlen = 1; char cmsg_buf[CMSG_SPACE(sizeof(int))]; memset(cmsg_buf, 0, CMSG_SPACE(sizeof(int))); struct cmsghdr *cmsg = (struct cmsghdr*)cmsg_buf; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; msg.msg_control = cmsg; msg.msg_controllen = CMSG_LEN(sizeof(int)); msg.msg_flags = 0; ssize_t r = recvmsg(sock, &msg, 0); if(r != -1) { // get file descriptor int *data = (int*)CMSG_DATA(cmsg); *fd = *data; } else { *fd = -1; } return r; }