diff options
Diffstat (limited to 'tests/recvfd.c')
-rw-r--r-- | tests/recvfd.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/tests/recvfd.c b/tests/recvfd.c new file mode 100644 index 0000000..82455c7 --- /dev/null +++ b/tests/recvfd.c @@ -0,0 +1,223 @@ +/* This file is part of GNU Pies testsuite. + Copyright (C) 2019 Sergey Poznyakoff + + GNU Pies is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Pies is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include "libpies.h" + +char const *progname; + +void +usage (void) +{ + fprintf (stderr, "usage: %s SOCKET COMMAND ARGS...\n", progname); + fprintf (stderr, "Test tool for pass-fd pies components.\n"); + fprintf (stderr, "Listens on the file descriptor obtained from SOCKET.\n"); + fprintf (stderr, "For each connection, execs COMMAND ARGS as a separate process.\n"); + exit (64); +} + +static int +listen_socket (char const *socket_name) +{ + struct sockaddr_un addr; + int sockfd; + + if (strlen (socket_name) > sizeof addr.sun_path) + { + fprintf (stderr, "%s: UNIX socket name too long\n", progname); + return -1; + } + addr.sun_family = AF_UNIX; + strcpy (addr.sun_path, socket_name); + + sockfd = socket (PF_UNIX, SOCK_STREAM, 0); + if (sockfd == -1) + { + perror ("socket"); + exit (1); + } + + umask (0117); + if (bind (sockfd, (struct sockaddr *) &addr, sizeof (addr)) < 0) + { + perror ("bind"); + exit (1); + } + + if (listen (sockfd, 8) < 0) + { + perror ("listen"); + exit (1); + } + return sockfd; +} + +static int +read_fd (int fd) +{ + struct msghdr msg; + struct iovec iov[1]; + char base[1]; + +#if HAVE_STRUCT_MSGHDR_MSG_CONTROL + union + { + struct cmsghdr cm; + char control[CMSG_SPACE (sizeof (int))]; + } control_un; + struct cmsghdr *cmptr; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof (control_un.control); +#elif HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS + int newfd; + + msg.msg_accrights = (caddr_t) &newfd; + msg.msg_accrightslen = sizeof (int); +#else + fprintf (stderr, "no way to get fd\n"); + exit (77); +#endif + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + iov[0].iov_base = base; + iov[0].iov_len = sizeof (base); + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if (recvmsg (fd, &msg, 0) > 0) + { +#if HAVE_STRUCT_MSGHDR_MSG_CONTROL + if ((cmptr = CMSG_FIRSTHDR (&msg)) != NULL + && cmptr->cmsg_len == CMSG_LEN (sizeof (int)) + && cmptr->cmsg_level == SOL_SOCKET + && cmptr->cmsg_type == SCM_RIGHTS) + return *((int*) CMSG_DATA (cmptr)); +#elif HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS + if (msg.msg_accrightslen == sizeof (int)) + return newfd; +#endif + } + return -1; +} + +static int +get_fd (int lfd) +{ + int sfd, fd = accept (lfd, NULL, NULL); + if (fd == -1) + { + perror ("accept"); + exit (1); + } + + sfd = read_fd (fd); + close (fd); + return sfd; +} + +static void +sigchld (int sig) +{ + pid_t pid; + + while ((pid = waitpid ((pid_t)-1, NULL, WNOHANG)) >= 0) + ; + signal (sig, sigchld); +} + +static void +sigquit (int sig) +{ + kill (0, sig); + exit (0); +} + +int +main (int argc, char **argv) +{ + int sfd, fd; + + progname = argv[0]; + + if (argc < 3) + usage (); + + sfd = listen_socket (argv[1]); + + argc -= 2; + argv += 2; + + fd = get_fd (sfd); + close (sfd); + + signal (SIGCHLD, sigchld); + signal (SIGTERM, sigquit); + signal (SIGHUP, sigquit); + signal (SIGINT, sigquit); + signal (SIGQUIT, sigquit); + + while (1) + { + int cfd = accept (fd, NULL, NULL); + if (cfd == -1) + { + perror ("accept"); + exit (1); + } + + pid_t pid = fork (); + if (pid == 0) + { + int i; + + for (i = getmaxfd (); i >= 0; i--) + if (i != cfd) + close (i); + + if (cfd != 0) + dup2 (cfd, 0); + if (cfd != 1) + dup2 (cfd, 1); + if (cfd != 2) + dup2 (cfd, 2); + if (cfd > 2) + close (cfd); + + execvp (argv[0], argv); + exit (127); + } + if (pid == -1) + { + perror ("fork"); + } + close (cfd); + } + return 0; +} |