]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-shell/fork.c
Suck in latest changes from HEAD
[openldap] / servers / slapd / back-shell / fork.c
index aff0e5bbe446778c06ec249e3312b0d435c077b5..fc1c0768c356f39d9ce2fd8b38409050db2429be 100644 (file)
 /* fork.c - fork and exec a process, connecting stdin/out w/pipes */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
 
 #include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.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;
+               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;
+               msg.msg_flags = 0;
+               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     pid;
+       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 );
        }
 
@@ -26,30 +246,102 @@ forkandexec(
         *      parent *rfp <- c2p[0] | c2p[1] <- stdout child
         */
 
-       switch ( (pid = fork()) ) {
-       case 0:         /* child */
-               close( p2c[1] );
-               close( c2p[0] );
+#ifdef SHELL_SURROGATE_PARENT
+
+       {
+               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;
+               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;
+               msg.msg_flags = 0;
+               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.
+                */
                if ( dup2( p2c[0], 0 ) == -1 || dup2( c2p[1], 1 ) == -1 ) {
                        Debug( LDAP_DEBUG_ANY, "dup2 failed\n", 0, 0, 0 );
-                       exit( -1 );
+                       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 );
-               exit( -1 );
+               exit( EXIT_FAILURE );
 
        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 );