#include <stdio.h>
+#include <ac/errno.h>
#include <ac/string.h>
#include <ac/socket.h>
#include <ac/unistd.h>
#include "slap.h"
#include "shell.h"
+#ifdef SHELL_SURROGATE_PARENT
+
+#include <sys/uio.h>
+
+/* Use several socketpairs to the surrogate parent, because *
+ * a single communication channel to it could be a bottleneck */
+ldap_pvt_thread_mutex_t shell_surrogate_fd_mutex[2];
+int shell_surrogate_fd[2] = { -1, -1 };
+/* Index to shell_surrogate_fd, and its mutex */
+ldap_pvt_thread_mutex_t shell_surrogate_index_mutex;
+static int shell_surrogate_index = 1;
+
+pid_t shell_surrogate_pid = -1;
+
+#define nread( fd, buf, len ) n_rw( 0, fd, buf, len )
+#define nwrite( fd, buf, len ) n_rw( 1, fd, buf, len )
+
+static int
+n_rw(
+ int do_write,
+ int fd,
+ void *buf,
+ int len
+)
+{
+ int ret = 0, i;
+ while( len ) {
+ for(;;) {
+ i = (do_write
+ ? write( fd, buf, len )
+ : read( fd, buf, len ));
+ if( i < 0 ) {
+ if( errno == EINTR )
+ continue;
+ if( ret == 0 )
+ ret = -1;
+ }
+ break;
+ }
+ if( i <= 0 )
+ break;
+ ret += i;
+ buf = (char *)buf + i;
+ len -= i;
+ }
+ return ret;
+}
+
+void
+make_surrogate_parent( void )
+{
+ int pair[2][2], io[2], i, j, p, argc;
+ ber_len_t len, buflen, offset;
+ char *buf, **argv;
+ pid_t pid;
+
+ if( socketpair( AF_LOCAL, SOCK_STREAM, 0, pair[0] ) < 0 ||
+ socketpair( AF_LOCAL, SOCK_STREAM, 0, pair[1] ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "socketpair failed\n", 0, 0, 0 );
+ exit( EXIT_FAILURE );
+ }
+ fflush( NULL );
+ switch( fork() ) {
+ case -1:
+ Debug( LDAP_DEBUG_ANY, "fork failed\n", 0, 0, 0 );
+ exit( EXIT_FAILURE );
+ case 0:
+ break;
+ default:
+ shell_surrogate_fd[0] = pair[0][0];
+ shell_surrogate_fd[1] = pair[1][0];
+ close( pair[0][1] );
+ close( pair[1][1] );
+ return;
+ }
+
+ /* Close unused file descriptors */
+ for( i = 3, j = 32; j && i < 1024; i++ )
+ if( i != pair[0][1] && i != pair[1][1] && close( i ) < 0 )
+ --j;
+ else if( j < 32 )
+ j = 32;
+
+ /* Surrogate parent running */
+
+ buflen = 0;
+ buf = NULL;
+ argc = 0;
+ argv = NULL;
+ p = 0;
+
+ for(;;) {
+ /* Read file descriptors io[] from socket */
+ static char dummy;
+ static struct iovec iov = { &dummy, 1 };
+ struct msghdr msg;
+# ifdef CMSG_SPACE
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(io))];
+ } control_un;
+ struct cmsghdr *cmptr;
+# endif
+
+ /* clear msghdr */
+ memset( &msg, 0, sizeof msg );
+
+# ifdef CMSG_SPACE
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+# else
+ msg.msg_accrights = (caddr_t) io;
+ msg.msg_accrightslen = sizeof(io);
+# endif
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ switch( recvmsg( pair[p][1], &msg, MSG_WAITALL ) ) {
+ case -1:
+ if( errno == EINTR )
+ continue;
+ _exit( EXIT_FAILURE );
+ case 0:
+ _exit( EXIT_SUCCESS );
+ }
+# ifdef CMSG_SPACE
+ if( (cmptr = CMSG_FIRSTHDR(&msg)) == NULL ||
+ cmptr->cmsg_len != CMSG_LEN(sizeof(io)) ||
+ cmptr->cmsg_level != SOL_SOCKET ||
+ cmptr->cmsg_type != SCM_RIGHTS ) {
+ fputs( "bad descriptor message received\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+ memcpy( io, CMSG_DATA( cmptr ), sizeof(io) );
+# else
+ if( msg.msg_accrightslen != sizeof(io) ) {
+ fputs( "bad descriptor message received\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+# endif
+
+ /* Read length of arguments and then arguments from socket */
+ if( nread( pair[p][1], &len, sizeof(len) ) != sizeof(len) ) {
+ fputs( "bad descriptor message received\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+ if( buflen < len ) {
+ buf = realloc( buf, buflen = len );
+ if( buf == NULL ) {
+ fputs( "realloc failed\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+ }
+ if( nread( pair[p][1], buf, len ) != len ) {
+ fputs( "bad descriptor message received\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+ i = 0;
+ offset = 0;
+ while( offset < len ) {
+ if( i >= argc-1 ) {
+ argc += i + 10;
+ argv = realloc( argv, argc * sizeof(*argv) );
+ if( argv == NULL ) {
+ fputs( "realloc failed\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+ }
+ argv[i++] = buf + offset;
+ offset += strlen( buf + offset ) + 1;
+ }
+ argv[i] = NULL;
+
+ /* Run program */
+ pid = fork();
+ switch( pid )
+ {
+ case 0: /* child */
+ if( dup2( io[0], 0 ) == -1 || dup2( io[1], 1 ) == -1 ) {
+ fputs( "dup2 failed\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+ close( io[0] );
+ close( io[1] );
+ close( pair[0][1] );
+ close( pair[1][1] );
+ execv( argv[0], argv );
+
+ fputs( "execv failed\n", stderr );
+ exit( EXIT_FAILURE );
+
+ case -1: /* trouble */
+ fputs( "fork failed\n", stderr );
+ break;
+
+ default: /* parent */
+ close( io[0] );
+ close( io[1] );
+ break;
+ }
+ if( nwrite( pair[p][1], &pid,
+ sizeof(pid_t) ) != sizeof(pid_t) ) {
+ fputs( "could not send pid\n", stderr );
+ exit( EXIT_FAILURE );
+ }
+ p ^= 1;
+ }
+}
+#endif /* SHELL_SURROGATE_PARENT */
+
pid_t
forkandexec(
- char **args,
+ Cmd_info args,
FILE **rfp,
FILE **wfp
)
{
- int p2c[2], c2p[2];
+ int p2c[2] = { -1, -1 }, c2p[2];
pid_t pid;
if ( pipe( p2c ) != 0 || pipe( c2p ) != 0 ) {
Debug( LDAP_DEBUG_ANY, "pipe failed\n", 0, 0, 0 );
+ close( p2c[0] );
+ close( p2c[1] );
return( -1 );
}
* parent *rfp <- c2p[0] | c2p[1] <- stdout child
*/
-#ifdef HAVE_THR
- switch ( (pid = fork1()) )
-#else
- switch ( (pid = fork()) )
-#endif
+#ifdef SHELL_SURROGATE_PARENT
+
{
- case 0: /* child */
+ int io[2] = { p2c[0], c2p[1] }, i, c;
+ static char dummy = '\0';
+ static struct iovec iov = { &dummy, 1 };
+ struct msghdr msg;
+# ifdef CMSG_SPACE
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(io))];
+ } control_un;
+ struct cmsghdr *cmptr;
+# endif
+
+ /* clear msghdr */
+ memset( &msg, 0, sizeof msg );
+
+# ifdef CMSG_SPACE
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+ cmptr = CMSG_FIRSTHDR(&msg);
+ cmptr->cmsg_len = CMSG_LEN(sizeof(io));
+ cmptr->cmsg_level = SOL_SOCKET;
+ cmptr->cmsg_type = SCM_RIGHTS;
+ memcpy( CMSG_DATA(cmptr), io, sizeof(io) );
+# else
+ msg.msg_accrights = (caddr_t) io;
+ msg.msg_accrightslen = sizeof(io);
+# endif
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ldap_pvt_thread_mutex_lock( &shell_surrogate_index_mutex );
+ i = shell_surrogate_index ^= 1;
+ ldap_pvt_thread_mutex_unlock( &shell_surrogate_index_mutex );
+ ldap_pvt_thread_mutex_lock( &shell_surrogate_fd_mutex[i] );
+ c = (sendmsg( shell_surrogate_fd[i], &msg, 0 ) == 1 &&
+ nwrite( shell_surrogate_fd[i], &args.bv_len,
+ sizeof(args.bv_len) ) == sizeof(args.bv_len) &&
+ nwrite( shell_surrogate_fd[i], args.bv_val,
+ args.bv_len ) == args.bv_len &&
+ nread( shell_surrogate_fd[i], &pid,
+ sizeof(pid) ) == sizeof(pid));
+ ldap_pvt_thread_mutex_unlock( &shell_surrogate_fd_mutex[i] );
+ close( p2c[0] );
+ close( c2p[1] );
+ if ( !c ) {
+ Debug( LDAP_DEBUG_ANY, "process creation failed\n", 0, 0, 0 );
+ close( p2c[1] );
+ close( c2p[0] );
+ close( shell_surrogate_fd[0] );
+ close( shell_surrogate_fd[1] );
+ shell_surrogate_fd[0] =
+ shell_surrogate_fd[1] = -1;
+ return( -1 );
+ }
+ }
+
+#else /* !SHELL_SURROGATE_PARENT */
+
+ fflush( NULL );
+# ifdef HAVE_THR
+ pid = fork1();
+# else
+ pid = fork();
+# endif
+ if ( pid == 0 ) { /* child */
/*
* child could deadlock here due to resources locked
* by our parent
*
- * If so, configure --without-threads or implement forking
- * via a surrogate parent.
+ * If so, configure --without-threads.
*/
- close( p2c[1] );
- close( c2p[0] );
if ( dup2( p2c[0], 0 ) == -1 || dup2( c2p[1], 1 ) == -1 ) {
Debug( LDAP_DEBUG_ANY, "dup2 failed\n", 0, 0, 0 );
exit( EXIT_FAILURE );
}
-
+ }
+ close( p2c[0] );
+ close( c2p[1] );
+ if ( pid <= 0 ) {
+ close( p2c[1] );
+ close( c2p[0] );
+ }
+ switch ( pid ) {
+ case 0:
execv( args[0], args );
Debug( LDAP_DEBUG_ANY, "execv failed\n", 0, 0, 0 );
case -1: /* trouble */
Debug( LDAP_DEBUG_ANY, "fork failed\n", 0, 0, 0 );
return( -1 );
-
- default: /* parent */
- close( p2c[0] );
- close( c2p[1] );
- break;
}
+#endif /* SHELL_SURROGATE_PARENT */
+
+ /* parent */
if ( (*rfp = fdopen( c2p[0], "r" )) == NULL || (*wfp = fdopen( p2c[1],
"w" )) == NULL ) {
Debug( LDAP_DEBUG_ANY, "fdopen failed\n", 0, 0, 0 );