Skip to content

Instantly share code, notes, and snippets.

@tokenrove
Last active July 12, 2017 22:42
Show Gist options
  • Save tokenrove/c1af799c84e9fa644a5abc9c64854f23 to your computer and use it in GitHub Desktop.
Save tokenrove/c1af799c84e9fa644a5abc9c64854f23 to your computer and use it in GitHub Desktop.
sending fds over unix domain sockets
/*
* Opens a Unix domain socket and cats any fds passed over it.
*/
#include <err.h>
#include <stdbool.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
void cat(int fd)
{
char buf[4096];
while (true) {
ssize_t n = read(fd, buf, sizeof(buf));
if (n == 0) {
close(fd);
return;
}
if (n < 0)
err(EX_IOERR, "read(%d)", fd);
if (n != write(1, buf, n))
err(EX_IOERR, "write");
}
__builtin_unreachable();
}
int main(int argc, char **argv)
{
if (2 != argc)
errx(EX_USAGE, "takes path to UDS to create");
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0)
err(EX_OSERR, "socket");
struct sockaddr_un name = { .sun_family = AF_UNIX };
char *path = argv[1];
if (strlen(path) > sizeof(name.sun_path)-1)
errx(EX_USAGE, "UDS pathnames can't be longer than %zu (yours is %zu)",
sizeof(name.sun_path)-1, strlen(path));
strncpy(name.sun_path, path, sizeof(name.sun_path)-1);
if (bind(sockfd, (const struct sockaddr *)&name, sizeof(name)))
err(EX_OSERR, "bind(%s)", path);
pid_t child = fork();
if (child < 0)
err(EX_OSERR, "fork");
if (child) {
sigaction(SIGINT,
&(struct sigaction){ .sa_handler = SIG_IGN,
.sa_flags = SA_RESETHAND },
NULL);
do { waitpid(child, NULL, 0); } while (!kill(child, 0));
unlink(path);
return 0;
}
while (true) {
char buf[CMSG_LEN(sizeof(int))]
__attribute__((aligned(__alignof__(struct cmsghdr))));
struct msghdr msg = {
// NB: can't receive any data
.msg_control = buf,
.msg_controllen = sizeof(buf),
};
if (recvmsg(sockfd, &msg, 0) < 0)
err(EX_IOERR, "recvmsg");
struct cmsghdr *c;
for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) {
if (c->cmsg_level == SOL_SOCKET &&
c->cmsg_type == SCM_RIGHTS)
cat(*(int *)CMSG_DATA(c));
}
}
return 0;
}
/*
* Send stdin on a Unix domain socket.
*
* You can use the shell to redirect arbitrary fds into this program.
*/
#include <assert.h>
#include <err.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
static void usage(void)
{
errx(EX_USAGE, "takes path to Unix domain socket; sends fd 0");
}
int main(int argc, char **argv)
{
if (2 != argc)
usage();
if (isatty(0)) /* NB: try removing this and see what happens */
errx(EX_USAGE, "you probably don't want to send your tty.");
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0)
err(EX_OSERR, "socket");
struct sockaddr_un name = { .sun_family = AF_UNIX };
char *path = argv[1];
if (strlen(path) > sizeof(name.sun_path)-1)
errx(EX_USAGE, "UDS pathnames can't be longer than %zu (yours is %zu)",
sizeof(name.sun_path)-1, strlen(path));
strncpy(name.sun_path, path, sizeof(name.sun_path)-1);
char buf[CMSG_LEN(sizeof(int))];
struct msghdr msg = {
.msg_name = &name,
.msg_namelen = sizeof(name),
.msg_control = buf,
.msg_controllen = CMSG_LEN(sizeof(int)),
};
struct cmsghdr *cmsg;
cmsg = CMSG_FIRSTHDR(&msg);
assert(cmsg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = sizeof(buf);
*(int *)CMSG_DATA(cmsg) = 0;
if (sendmsg(sockfd, &msg, 0) < 0)
err(EX_IOERR, "sendmsg");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment