/*++ /* NAME /* upass_listen 3 /* SUMMARY /* start UNIX-domain file descriptor listener /* SYNOPSIS /* #include <listen.h> /* /* int upass_listen(path, backlog, block_mode) /* const char *path; /* int backlog; /* int block_mode; /* /* int upass_accept(fd) /* int fd; /* DESCRIPTION /* This module implements a listener that receives one file descriptor /* across each UNIX-domain connection that is made to it. /* /* upass_listen() creates a listener endpoint with the specified /* permissions, and returns a file descriptor to be used for accepting /* descriptors. /* /* upass_accept() accepts a descriptor. /* /* Arguments: /* .IP path /* Null-terminated string with connection destination. /* .IP backlog /* This argument exists for compatibility and is ignored. /* .IP block_mode /* Either NON_BLOCKING or BLOCKING. This does not affect the /* mode of accepted connections. /* .IP fd /* File descriptor returned by upass_listen(). /* DIAGNOSTICS /* Fatal errors: upass_listen() aborts upon any system call failure. /* upass_accept() leaves all error handling up to the caller. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include <sys_defs.h> #include <sys/socket.h> #include <errno.h> #include <unistd.h> /* Utility library. */ #include <msg.h> #include <sane_accept.h> #include <listen.h> /* upass_accept - accept descriptor */ int upass_accept(int listen_fd) { const char *myname = "upass_accept"; int accept_fd; int recv_fd; accept_fd = sane_accept(listen_fd, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0); if (accept_fd < 0) { if (errno != EAGAIN) msg_warn("%s: accept connection: %m", myname); return (-1); } else { if ((recv_fd = unix_recv_fd(accept_fd)) < 0) msg_warn("%s: cannot receive file descriptor: %m", myname); if (close(accept_fd) < 0) msg_warn("%s: close: %m", myname); return (recv_fd); } } #if 0 /* System library. */ #include <sys_defs.h> /* Utility library. */ #include <msg.h> #include <events.h> #include <sane_accept.h> #include <iostuff.h> #include <listen.h> /* * It would be nice if a client could make one UNIX-domain connection to a * Postfix master service, send multiple descriptors, and have each * descriptor handled by the first available child process. * * Possible solutions: * * - Either the master process accepts the UNIX-domain connection and forwards * each descriptor sent by the client to the first available child process. * That's what the code below does. Unfortunately, this approach is * inconsistent with the Postfix architecture which tries to eliminate the * master from connection management as much as possible. * * - Or one child processes accepts the UNIX-domain connection and sends a * shared socketpair half to the client. The other socketpair half is shared * with the master and all the child's siblings. The client then sends its * descriptors over the socketpair, and each descriptor is available to any * child process that is waiting for work. * * If the second solution did not use a shared socketpair, then all the * client's descriptors would be available only to the child process that * accepted the UNIX-domain connection. That results in poor performance. * * Unfortunately, having to receive a descriptor before being able to send one * or more descriptors is ugly from the client's point of view. */ #define upass_accept(fd) unix_recv_fd(fd) /* upass_plumbing - operate the hidden descriptor passing machinery */ static void upass_plumbing(int unused_event, char *context) { const char *myname = "upass_plumbing"; UPASS_INFO *info = (UNIX_UPASS_INFO *) context; int fd; /* * Each time a client connects to the hidden UNIX-domain socket, call * unix_send_fd() to send one half of the hidden socketpair across a * short-lived UNIX-domain connection. Wait until the client closes the * UNIX-domain connection before closing the connection. This wait needs * to be time limited. */ fd = sane_accept(info->unixsock, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0); if (fd < 0) { if (errno != EAGAIN) msg_fatal("%s: accept connection: %m", myname); } else { if (unix_send_fd(fd, info->halfpair) < 0) msg_warn("%s: cannot send file descriptor: %m", myname); if (read_wait(fd, 5) < 0) msg_warn("%s: read timeout", myname); if (close(fd) < 0) msg_warn("%s: close: %m", myname); } } /* upass_listen - set up hidden descriptor passing machinery */ int upass_listen(const char *path, int backlog, int blocking, UPASS_INFO **ip) { int pair[2]; UPASS_INFO *info; /* * Create a UNIX-domain socket with unix_listen() and create a * socketpair. One socketpair half is returned to the caller. The other * half is part of the hidden machinery, together with the UNIX-domain * socket. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) msg_fatal("socketpair: %m"); info = (UPASS_INFO *) mymalloc(sizeof(*info)); info->halfpair = pair[0]; info->unixsock = unix_listen(path, backlog, blocking); event_request_read(info->unixsock, upass_plumbing, (char *) info); *ip = info; return (pair[1]); } /* upass_shutdown - tear down hidden descriptor passing machinery */ void upass_shutdown(UPASS_INFO *info) { event_disable_readwrite(upass_info->unixsock) if (close(info->unixsock) < 0) msg_warn("%s: close unixsock: %m", myname); if (close(info->halfpair) < 0) msg_warn("%s: close halfpair: %m", myname); myfree((char *) info); } #endif