static apr_status_t sock_readhdr(int fd, int *errfd, void *vbuf, size_t buf_size)
{
    int rc;
#ifndef HAVE_CGID_FDPASSING
    char *buf = vbuf;
    size_t bytes_read = 0;

    if (errfd) *errfd = 0;
    
    do {
        do {
            rc = read(fd, buf + bytes_read, buf_size - bytes_read);
        } while (rc < 0 && errno == EINTR);
        switch(rc) {
        case -1:
            return errno;
        case 0: /* unexpected */
            return ECONNRESET;
        default:
            bytes_read += rc;
        }
    } while (bytes_read < buf_size);

   
#else /* with FD passing */
    struct msghdr msg = {0};
    struct iovec vec = {vbuf, buf_size};
    struct cmsghdr *cmsg;
    union {  /* union to ensure alignment */
        struct cmsghdr cm;
        char buf[CMSG_SPACE(sizeof(int))];
    } u;
    
    msg.msg_iov = &vec;
    msg.msg_iovlen = 1;
    
    msg.msg_control = u.buf;
    msg.msg_controllen = sizeof(u.buf);

    if (errfd) *errfd = 0;
    
    /* use MSG_WAITALL to skip loop on truncated reads */
    do {
        rc = recvmsg(fd, &msg, MSG_WAITALL);
    } while (rc < 0 && errno == EINTR);

    if (rc == 0) {
        return ECONNRESET;
    }
    
    cmsg = CMSG_FIRSTHDR(&msg);
    if (errfd
        && cmsg
        && cmsg->cmsg_len == CMSG_LEN(sizeof(*errfd))
        && cmsg->cmsg_level == SOL_SOCKET
        && cmsg->cmsg_type == SCM_RIGHTS) {
        *errfd = *((int *) CMSG_DATA(cmsg));
    }
#endif
    
    return APR_SUCCESS;
}