Re: authopen source code and -stdoutpipe usage
Re: authopen source code and -stdoutpipe usage
- Subject: Re: authopen source code and -stdoutpipe usage
- From: Soren Spies <email@hidden>
- Date: Sat, 16 Apr 2005 04:32:03 -0700
On Sat, Apr 16, 2005 at 01:09:16AM +0300, Dan Bernstein wrote:
I'm looking for the authopen source code, if it's available, and for
an example of how to use it from within another program with the
-stdoutpipe option.
The source to authopen is not currently part of Darwin.
/usr/libexec/authopen is an implementation of an "open server" as
described in Stevens' "Advanced Programming in the Unix Environment."
The send_fd and recv_fd functions come from that text.
Here's an example authopen() function with support (apologies that I'm
cribbing this together from various sources; it does compile :):
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#if qDEBUG
#include <stdio.h> /* needed for perror */
#endif // qDEBUG
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#if qDEBUG
#define dperrret(x) do { perror x; return -1; } while(0)
#define dprintf(x) do { printf x; fflush(stdout); } while(0)
#define dfflush(x) do { fflush(x); } while(0)
#else
#define dperrret(x) do { return -1; } while(0)
#define dprintf(x) do { } while(0)
#define dfflush(x) do { } while(0)
#endif
// passfd.h
int recv_fd(int servfd, ssize_t (*userfunc)(int, const void *, size_t));
int send_fd(int clifd, int fd);
// authopen.h
/* authopen(3) uses these and they may come in handy */
#define AO_PROGNAME "authopen"
#define AO_INSTALL_PATH "/usr/libexec/"
#define AO_PATH AO_INSTALL_PATH AO_PROGNAME
#define AO_WRITEOPT "-w"
#define AO_APPENDOPT "-a"
#define AO_PIPEOPT "-stdoutpipe"
#define AO_CREATOPT "-c"
#define AO_EXCLOPT "-x"
#define AO_MODEOPT "-m"
#define AO_OFLAGOPT "-o"
ssize_t discardfunc(int i, const void* p, size_t l) { return l; }
/*
* authopen tries to be like open(2) ... returning -1 and letting caller
* deal with errno, etc ... now passes everything through to open; needs
* to learn to set errno appropriately after getting info from the socket.
*/
int
authopen(const char *path, int flags, mode_t omode)
{
int pipe[2], pid, rval, status;
ssize_t (*userfunc)(int, const void *, size_t);
char oarg[11], marg[6]; /* 0x12345678\0 and 01234\0 */
/* could use pipe(2)? */
if(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) < 0)
dperrret(("socketpair failed"));
if((pid = fork()) < 0)
dperrret(("fork() error"));
else
if(pid == 0) { /* am child */
close(pipe[0]); /* close the parent's side */
/* ensure authopen's STDOUT is a copy of the pipe */
if(pipe[1] != STDOUT_FILENO)
if(dup2(pipe[1], STDOUT_FILENO) != STDOUT_FILENO)
err(1, "dup2 failed for STDOUT_FILENO");
/* errors result in child exit */
/* set up arguments */
if(snprintf(oarg, 11, "0xx", flags) != 10)
dperrret(("snprintf into oarg failed"));
if(snprintf(marg, 6, "0o", omode) != 5)
dperrret(("snprintf into marg failed"));
/* fire off authopen itself with flags (execl NULL-termated) */
dprintf((". %s %s %s %s %s %s %s\n", AO_PROGNAME,
AO_PIPEOPT, AO_MODEOPT, marg, AO_OFLAGOPT, oarg, path));
if(execl(AO_PATH, AO_PATH, AO_PIPEOPT, AO_MODEOPT, marg,
AO_OFLAGOPT, oarg, path, NULL))
err(1, "exec of " AO_PATH " failed");
}
/* parent; no 'else' since child exec's or exit's above */
if(close(pipe[1]) < 0)
dperrret(("close should not fail"));
/* allows recv_fd to spew messages on console or not */
#if qDEBUG
#warning enabling output of excess data from authopen
userfunc = write;
#else
userfunc = discardfunc;
#endif
/* now to actually get the fd back */
rval = recv_fd(pipe[0], userfunc);
waitpid(pid, &status, 0);
return rval;
}
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h> /* struct msghdr */
#include <sys/uio.h> /* struct iovec */
#include <stddef.h>
#include <stdlib.h> /* malloc(3) */
#include <unistd.h>
static struct cmsghdr *r_cmptr = NULL; /* malloc'ed first time */
#define CONTROLLEN (sizeof(struct cmsghdr) + sizeof(int))
/* size of control buffer to send/recv one file descriptor */
/* Receive a file descriptor from another process (a server).
* In addition, any data received from the server is passed
* to (*userfunc)(STDERR_FILENO, buf, nbytes). We have a
* 2-byte protocol for receiving the fd from send_fd(). */
int
recv_fd(int servfd, ssize_t (*userfunc)(int, const void *, size_t))
{
int newfd = -1, nread, status;
char *ptr, buf[4096];
struct iovec iov[1];
struct msghdr msg;
status = -1;
for ( ; ; ) {
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (r_cmptr == NULL && (r_cmptr = malloc(CONTROLLEN)) == NULL)
return(-1);
msg.msg_control = (caddr_t) r_cmptr;
msg.msg_controllen = CONTROLLEN;
if ((nread = recvmsg(servfd, &msg, 0)) < 0) {
if(errno == EINTR) {
errno = 0;
dprintf(("retrying...\n"));
continue;
}
else
dperrret(("recvmsg error"));
}
else if (nread == 0) {
errno = ECONNRESET;
dprintf(("connection closed by server\n"));
return -1;
}
/* See if this is the final data with null & status.
Null must be next to last byte of buffer, status
byte is last byte. Zero status means there must
be a file descriptor to receive. */
for (ptr = buf; ptr < &buf[nread]; ) {
if (*ptr++ == 0) {
if (ptr != &buf[nread-1]) {
/* err_dump("message format error"); * this dumped core */
dprintf(("protocol error\n"));
errno = EIO;
return -1;
}
status = *ptr & 255;
if (status == 0) {
if (msg.msg_controllen != CONTROLLEN) {
dprintf(("fatal error msg_controllen != CONTROLLEN\n"));
errno = EIO;
return -1;
}
/* err_dump("status = 0 but no fd"); */
newfd = *(int *)CMSG_DATA(r_cmptr); /* new descriptor */
} else {
newfd = -1;
errno = status;
}
nread -= 2;
}
}
/* if userfunc is NULL, we don't call it */
if (userfunc && nread > 0)
if ((*userfunc)(STDERR_FILENO, buf, nread) != nread)
return(-1);
if (status >= 0) /* final data has arrived */
return(newfd); /* descriptor, or -status */
}
}
#define CONTROLLEN (sizeof(struct cmsghdr) + sizeof(int))
static struct cmsghdr *s_cmptr = NULL; /* buffer is malloc'ed first time */
/* size of control buffer to send/recv one file descriptor */
/* Pass a file descriptor to another process.
* If fd<0, then -fd is sent back instead as the error status. */
int
send_fd(int clifd, int fd)
{
struct iovec iov[1];
struct msghdr msg;
char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
iov[0].iov_base = buf;
iov[0].iov_len = 2;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (fd < 0) {
dprintf(("sending error %d: %s\n", -fd, strerror(-fd)));
msg.msg_control = NULL;
msg.msg_controllen = 0;
buf[1] = -(char)fd; /* nonzero status means error */
if (buf[1] == 0)
buf[1] = 1; /* -256, etc. would screw up protocol */
} else {
dprintf(("sending fd\n"));
if (s_cmptr==NULL&&(s_cmptr=(struct cmsghdr*)malloc(CONTROLLEN))==NULL)
return(-1);
s_cmptr->cmsg_level = SOL_SOCKET;
s_cmptr->cmsg_type = SCM_RIGHTS;
s_cmptr->cmsg_len = CONTROLLEN;
msg.msg_control = (caddr_t) s_cmptr;
msg.msg_controllen = CONTROLLEN;
*(int *)CMSG_DATA(s_cmptr) = fd; /* the fd to pass */
buf[1] = 0; /* zero status means OK */
}
buf[0] = 0; /* null byte flag to recv_fd() */
dfflush(stdout);
if (sendmsg(clifd, &msg, 0) != 2)
return(-1);
dprintf(("sendmsg successful\n"));
return(0);
}
--
Soren Spies
DiskImages, etc; I/O Kit team
Apple Computer, Inc
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden