view lib/passfd.c @ 14421:f23a6a383241

passfd module, part 3. * lib/passfd.h (recvfd): Add a flags argument. * lib/passfd.c: Include <fcntl.h>, cloexec.h. (recvfd): Add a flags argument. * m4/afunix.m4 (gl_SOCKET_AFUNIX): Test whether MSG_CMSG_CLOEXEC exists. * modules/passfd (Depends-on): Add cloexec. Suggested by Eric Blake.
author Bastien Roucariès <roucaries.bastien@gmail.com>
date Sun, 13 Mar 2011 16:36:30 +0100
parents 08622275f761
children 4c39cf933978
line wrap: on
line source

/* Copyright (C) 2011 Free Software Foundation, Inc.

   This program 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 of the License, or
   (at your option) any later version.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include <config.h>

/* Specification.  */
#include "passfd.h"

#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/socket.h>
#if HAVE_SYS_UN_H
# include <sys/un.h>
#endif

#include "cloexec.h"

/* sendfd sends the file descriptor fd along the socket
   to a process calling recvfd on the other end.

   Return 0 on success, or -1 with errno set in case of error.
*/
int
sendfd (int sock, int fd)
{
  char send = 0;
  struct iovec iov[1];
  struct msghdr msg;

  /* send at least one char */
  iov[0].iov_base = &send;
  iov[0].iov_len = 1;
  msg.msg_iov = iov;
  msg.msg_iovlen = 1;
  msg.msg_name = 0;
  msg.msg_namelen = 0;

  {
#if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
    struct cmsghdr *cmsg;
    char buf[CMSG_SPACE (sizeof (fd))];

    msg.msg_control = buf;
    msg.msg_controllen = sizeof (buf);
    cmsg = CMSG_FIRSTHDR (&msg);
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    cmsg->cmsg_len = CMSG_LEN (sizeof (int));
    /* Initialize the payload: */
    memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
    msg.msg_controllen = cmsg->cmsg_len;
#elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
    msg.msg_accrights = &fd;
    msg.msg_accrightslen = sizeof (fd);
#else
    errno = ENOSYS;
    return -1;
#endif
  }

  if (sendmsg (sock, &msg, 0) != iov[0].iov_len)
    return -1;
  return 0;
}

/* recvfd receives a file descriptor through the socket.
   The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>).

   Return 0 on success, or -1 with errno set in case of error.
*/
int
recvfd (int sock, int flags)
{
  char recv = 0;
  struct iovec iov[1];
  struct msghdr msg;

  if ((flags & ~O_CLOEXEC) != 0)
    {
      errno = EINVAL;
      return -1;
    }

  /* send at least one char */
  iov[0].iov_base = &recv;
  iov[0].iov_len = 1;
  msg.msg_iov = iov;
  msg.msg_iovlen = 1;
  msg.msg_name = 0;
  msg.msg_namelen = 0;

  {
#if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
    int fd;
    struct cmsghdr *cmsg;
    char buf[CMSG_SPACE (sizeof (fd))];
    const int mone = -1;
# if HAVE_MSG_CMSG_CLOEXEC
    int flags_recvmsg = (flags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0);
# else
    int flags_recvmsg = 0;
# endif

    msg.msg_control = buf;
    msg.msg_controllen = sizeof (buf);
    cmsg = CMSG_FIRSTHDR (&msg);
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    cmsg->cmsg_len = CMSG_LEN (sizeof (int));
    /* Initialize the payload: */
    memcpy (CMSG_DATA (cmsg), &mone, sizeof (mone));
    msg.msg_controllen = cmsg->cmsg_len;

    if (recvmsg (sock, &msg, flags_recvmsg) < 0)
      return -1;

    cmsg = CMSG_FIRSTHDR (&msg);
    /* be paranoiac */
    if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN (sizeof (int))
        || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
      {
        /* fake errno: at end the file is not available */
        errno = EACCES;
        return -1;
      }

    memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));

# if !HAVE_MSG_CMSG_CLOEXEC
    /* set close-on-exec flag */
    if (flags & O_CLOEXEC)
      {
        if (set_cloexec_flag (fd, true) < 0)
          {
            int saved_errno = errno;
            (void) close (fd);
            errno = saved_errno;
            return -1;
          }
      }
# endif

    return fd;

#elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
    int fd;

    msg.msg_accrights = &fd;
    msg.msg_accrightslen = sizeof (fd);
    if (recvmsg (sock, &msg, 0) < 0)
      return -1;

    /* set close-on-exec flag */
    if (flags & O_CLOEXEC)
      {
        if (set_cloexec_flag (fd, true) < 0)
          {
            int saved_errno = errno;
            (void) close (fd);
            errno = saved_errno;
            return -1;
          }
      }

    return fd;

#else
    errno = ENOSYS;
    return -1;
#endif
  }
}