Created
September 24, 2015 06:10
-
-
Save SumiTomohiko/0bda3738b61e264d2b47 to your computer and use it in GitHub Desktop.
File descriptor passing with sendmsg(2) and recvmsg(2) over Unix domain socket
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/param.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <sys/un.h> | |
#include <sys/wait.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#define SIG SIGUSR1 | |
static void | |
diec(int error, const char *msg) | |
{ | |
fprintf(stderr, "%s: %s\n", msg, strerror(error)); | |
exit(1); | |
} | |
static void | |
die(const char *msg) | |
{ | |
diec(errno, msg); | |
} | |
#if 0 | |
static void | |
print_alive_fd(const char *tag) | |
{ | |
struct stat sb; | |
int i; | |
for (i = 0; i < 32; i++) { | |
if (fstat(i, &sb) == -1) | |
continue; | |
printf("%s: fd %d is alive.\n", tag, i); | |
} | |
} | |
#endif | |
static int | |
do_sendmsg(int sock, const char *sockpath, int fd) | |
{ | |
struct msghdr msg; | |
struct cmsghdr *cmsghdr; | |
struct iovec iov[1]; | |
ssize_t nbytes; | |
int i, *p; | |
char buf[CMSG_SPACE(sizeof(int))], c; | |
c = '*'; | |
iov[0].iov_base = &c; | |
iov[0].iov_len = sizeof(c); | |
memset(buf, 0x0b, sizeof(buf)); | |
cmsghdr = (struct cmsghdr *)buf; | |
cmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); | |
cmsghdr->cmsg_level = SOL_SOCKET; | |
cmsghdr->cmsg_type = SCM_RIGHTS; | |
msg.msg_name = NULL; | |
msg.msg_namelen = 0; | |
msg.msg_iov = iov; | |
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); | |
msg.msg_control = cmsghdr; | |
msg.msg_controllen = CMSG_LEN(sizeof(int)); | |
msg.msg_flags = 0; | |
p = (int *)CMSG_DATA(buf); | |
*p = fd; | |
printf("sendmsg: %d\n", fd); | |
nbytes = sendmsg(sock, &msg, 0); | |
if (nbytes == -1) | |
return (1); | |
return (0); | |
} | |
static int | |
server_main(pid_t ppid, const char *sockpath) | |
{ | |
struct sockaddr_storage storage; | |
struct sockaddr_un *addr; | |
int error, fd, s, sock; | |
const char *filepath = "fd_passing.txt"; | |
sock = socket(PF_LOCAL, SOCK_STREAM, 0); | |
if (sock == -1) | |
die("socket(2)"); | |
addr = (struct sockaddr_un *)&storage; | |
addr->sun_family = AF_LOCAL; | |
strlcpy(addr->sun_path, sockpath, sizeof(addr->sun_path)); | |
addr->sun_len = SUN_LEN(addr); | |
if (bind(sock, (struct sockaddr *)addr, addr->sun_len) == -1) | |
die("bind(2)"); | |
if (listen(sock, 0) == -1) | |
goto fail; | |
if (kill(ppid, SIG) == -1) | |
goto fail; | |
if ((s = accept(sock, NULL, 0)) == -1) | |
goto fail; | |
if (unlink(filepath) == -1) | |
goto fail; | |
if ((fd = open(filepath, O_WRONLY | O_CREAT, 0644)) == -1) | |
goto fail; | |
if (do_sendmsg(s, sockpath, fd) != 0) | |
goto fail; | |
if (close(fd) == -1) | |
goto fail; | |
if (close(s) == -1) | |
goto fail; | |
if (close(sock) == -1) | |
goto fail; | |
if (unlink(sockpath) == -1) | |
goto fail; | |
return (0); | |
fail: | |
error = errno; | |
unlink(sockpath); | |
diec(error, ""); | |
/* NOTREACHED */ | |
return (1); | |
} | |
static int | |
do_recvmsg(int sock) | |
{ | |
struct msghdr msg; | |
struct cmsghdr *cmsghdr; | |
struct iovec iov[1]; | |
FILE *fp; | |
ssize_t nbytes; | |
int i, *p; | |
char buf[CMSG_SPACE(sizeof(int))], c; | |
iov[0].iov_base = &c; | |
iov[0].iov_len = sizeof(c); | |
memset(buf, 0x0d, sizeof(buf)); | |
cmsghdr = (struct cmsghdr *)buf; | |
cmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); | |
cmsghdr->cmsg_level = SOL_SOCKET; | |
cmsghdr->cmsg_type = SCM_RIGHTS; | |
msg.msg_name = NULL; | |
msg.msg_namelen = 0; | |
msg.msg_iov = iov; | |
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); | |
msg.msg_control = cmsghdr; | |
msg.msg_controllen = CMSG_LEN(sizeof(int)); | |
msg.msg_flags = 0; | |
nbytes = recvmsg(sock, &msg, 0); | |
if (nbytes == -1) | |
return (1); | |
p = (int *)CMSG_DATA(buf); | |
printf("recvmsg: %d\n", *p); | |
fp = fdopen(*p, "w"); | |
fprintf(fp, "OK\n"); | |
fclose(fp); | |
return (0); | |
} | |
static int | |
client_main(pid_t pid, const char *sockpath) | |
{ | |
struct sockaddr_storage sockaddr; | |
struct sockaddr_un *addr; | |
sigset_t set; | |
int sig, sock, status; | |
const char *signame; | |
if (sigemptyset(&set) == -1) | |
die("sigemptyset(3)"); | |
if (sigaddset(&set, SIG) == -1) | |
die("sigaddset(3)"); | |
if (sigwait(&set, &sig) != 0) | |
die("sigwait(2)"); | |
if (sig != SIG) | |
return (2); | |
sock = socket(PF_LOCAL, SOCK_STREAM, 0); | |
if (sock == -1) | |
die("socket(2)"); | |
addr = (struct sockaddr_un *)&sockaddr; | |
addr->sun_family = AF_LOCAL; | |
strlcpy(addr->sun_path, sockpath, sizeof(addr->sun_path)); | |
addr->sun_len = SUN_LEN(addr); | |
if (connect(sock, (struct sockaddr *)addr, addr->sun_len) == -1) | |
die("connect(2)"); | |
if (do_recvmsg(sock) == -1) | |
return (3); | |
if (close(sock) == -1) | |
die("close(2)"); | |
if (wait4(pid, &status, 0, NULL) == -1) | |
die("wait4(2)"); | |
if (!WIFEXITED(status)) | |
return (4); | |
if (WEXITSTATUS(status) != 0) | |
return (32 + WEXITSTATUS(status)); | |
return (0); | |
} | |
int | |
main(int argc, const char *argv[]) | |
{ | |
sigset_t set; | |
pid_t pid, ppid; | |
char sockpath[MAXPATHLEN]; | |
if (sigfillset(&set) == -1) | |
die("sigfillset(3)"); | |
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) | |
die("sigprocmask(2)"); | |
ppid = getpid(); | |
snprintf(sockpath, sizeof(sockpath), "%d.sock", ppid); | |
pid = fork(); | |
switch (pid) { | |
case -1: | |
die("fork(2)"); | |
case 0: | |
return (server_main(ppid, sockpath)); | |
default: | |
break; | |
} | |
return (client_main(pid, sockpath)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment