]> git.sur5r.net Git - openldap/blob - servers/slapd/back-shell/fork.c
Cleanup up LDAP_CLIENT_UPDATE code... including some bug fixing.
[openldap] / servers / slapd / back-shell / fork.c
1 /* fork.c - fork and exec a process, connecting stdin/out w/pipes */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/errno.h>
13 #include <ac/string.h>
14 #include <ac/socket.h>
15 #include <ac/unistd.h>
16
17 #include "slap.h"
18 #include "shell.h"
19
20 #ifdef SHELL_SURROGATE_PARENT
21
22 #include <sys/uio.h>
23
24 /* Use several socketpairs to the surrogate parent, because   *
25  * a single communication channel to it could be a bottleneck */
26 ldap_pvt_thread_mutex_t shell_surrogate_fd_mutex[2];
27 int                     shell_surrogate_fd[2] = { -1, -1 };
28 /* Index to shell_surrogate_fd, and its mutex */
29 ldap_pvt_thread_mutex_t shell_surrogate_index_mutex;
30 static int              shell_surrogate_index = 1;
31
32 pid_t                   shell_surrogate_pid = -1;
33
34 #define  nread( fd, buf, len ) n_rw( 0, fd, buf, len )
35 #define nwrite( fd, buf, len ) n_rw( 1, fd, buf, len )
36
37 static int
38 n_rw(
39         int do_write,
40         int fd,
41         void *buf,
42         int len
43 )
44 {
45         int ret = 0, i;
46         while( len ) {
47                 for(;;) {
48                         i = (do_write
49                              ? write( fd, buf, len )
50                              :  read( fd, buf, len ));
51                         if( i < 0 ) {
52                                 if( errno == EINTR )
53                                         continue;
54                                 if( ret == 0 )
55                                         ret = -1;
56                         }
57                         break;
58                 }
59                 if( i <= 0 )
60                         break;
61                 ret += i;
62                 buf = (char *)buf + i;
63                 len -= i;
64         }
65         return ret;
66 }
67
68 void
69 make_surrogate_parent( void )
70 {
71         int pair[2][2], io[2], i, j, p, argc;
72         ber_len_t len, buflen, offset;
73         char *buf, **argv;
74         pid_t pid;
75
76         if( socketpair( AF_LOCAL, SOCK_STREAM, 0, pair[0] ) < 0 ||
77             socketpair( AF_LOCAL, SOCK_STREAM, 0, pair[1] ) < 0   ) {
78                 Debug( LDAP_DEBUG_ANY, "socketpair failed\n", 0, 0, 0 );
79                 exit( EXIT_FAILURE );
80         }
81         fflush( NULL );
82         switch( fork() ) {
83         case -1:
84                 Debug( LDAP_DEBUG_ANY, "fork failed\n", 0, 0, 0 );
85                 exit( EXIT_FAILURE );
86         case 0:
87                 break;
88         default:
89                 shell_surrogate_fd[0] = pair[0][0];
90                 shell_surrogate_fd[1] = pair[1][0];
91                 close( pair[0][1] );
92                 close( pair[1][1] );
93                 return;
94         }
95
96         /* Close unused file descriptors */
97         for( i = 3, j = 32; j && i < 1024; i++ )
98                 if( i != pair[0][1] && i != pair[1][1] && close( i ) < 0 )
99                         --j;
100                 else if( j < 32 )
101                         j = 32;
102
103         /* Surrogate parent running */
104
105         buflen = 0;
106         buf = NULL;
107         argc = 0;
108         argv = NULL;
109         p = 0;
110
111         for(;;) {
112                 /* Read file descriptors io[] from socket */ 
113                 static char dummy;
114                 static struct iovec iov = { &dummy, 1 };
115                 struct msghdr msg;
116 # ifdef CMSG_SPACE
117                 union {
118                         struct cmsghdr cm;
119                         char control[CMSG_SPACE(sizeof(io))];
120                 } control_un;
121                 struct cmsghdr *cmptr;
122 # endif
123
124                 /* clear msghdr */
125                 memset( &msg, 0, sizeof msg );
126
127 # ifdef CMSG_SPACE
128                 msg.msg_control = control_un.control;
129                 msg.msg_controllen = sizeof(control_un.control);
130 # else
131                 msg.msg_accrights = (caddr_t) io;
132                 msg.msg_accrightslen = sizeof(io);
133 # endif
134
135                 msg.msg_name = NULL;
136                 msg.msg_namelen = 0;
137                 msg.msg_iov = &iov;
138                 msg.msg_iovlen = 1;
139
140                 switch( recvmsg( pair[p][1], &msg, MSG_WAITALL ) ) {
141                 case -1:
142                         if( errno == EINTR )
143                                 continue;
144                         _exit( EXIT_FAILURE );
145                 case 0:
146                         _exit( EXIT_SUCCESS );
147                 }
148 # ifdef CMSG_SPACE
149                 if( (cmptr = CMSG_FIRSTHDR(&msg)) == NULL   ||
150                     cmptr->cmsg_len != CMSG_LEN(sizeof(io)) ||
151                     cmptr->cmsg_level != SOL_SOCKET         ||
152                     cmptr->cmsg_type != SCM_RIGHTS            ) {
153                         fputs( "bad descriptor message received\n", stderr );
154                         exit( EXIT_FAILURE );
155                 }
156                 memcpy( io, CMSG_DATA( cmptr ), sizeof(io) );
157 # else
158                 if( msg.msg_accrightslen != sizeof(io) ) {
159                         fputs( "bad descriptor message received\n", stderr );
160                         exit( EXIT_FAILURE );
161                 }
162 # endif
163
164                 /* Read length of arguments and then arguments from socket */
165                 if( nread( pair[p][1], &len, sizeof(len) ) != sizeof(len) ) {
166                         fputs( "bad descriptor message received\n", stderr );
167                         exit( EXIT_FAILURE );
168                 }
169                 if( buflen < len ) {
170                         buf = realloc( buf, buflen = len );
171                         if( buf == NULL ) {
172                                 fputs( "realloc failed\n", stderr );
173                                 exit( EXIT_FAILURE );
174                         }
175                 }
176                 if( nread( pair[p][1], buf, len ) != len ) {
177                         fputs( "bad descriptor message received\n", stderr );
178                         exit( EXIT_FAILURE );
179                 }
180                 i = 0;
181                 offset = 0;
182                 while( offset < len ) {
183                         if( i >= argc-1 ) {
184                                 argc += i + 10;
185                                 argv = realloc( argv, argc * sizeof(*argv) );
186                                 if( argv == NULL ) {
187                                         fputs( "realloc failed\n", stderr );
188                                         exit( EXIT_FAILURE );
189                                 }
190                         }
191                         argv[i++] = buf + offset;
192                         offset += strlen( buf + offset ) + 1;
193                 }
194                 argv[i] = NULL;
195
196                 /* Run program */
197                 pid = fork();
198                 switch( pid )
199                 {
200                 case 0:         /* child */
201                         if( dup2( io[0], 0 ) == -1 || dup2( io[1], 1 ) == -1 ) {
202                                 fputs( "dup2 failed\n", stderr );
203                                 exit( EXIT_FAILURE );
204                         }
205                         close( io[0] );
206                         close( io[1] );
207                         close( pair[0][1] );
208                         close( pair[1][1] );
209                         execv( argv[0], argv );
210
211                         fputs( "execv failed\n", stderr );
212                         exit( EXIT_FAILURE );
213
214                 case -1:        /* trouble */
215                         fputs( "fork failed\n", stderr );
216                         break;
217
218                 default:        /* parent */
219                         close( io[0] );
220                         close( io[1] );
221                         break;
222                 }
223                 if( nwrite( pair[p][1], &pid,
224                             sizeof(pid_t) ) != sizeof(pid_t) )  {
225                         fputs( "could not send pid\n", stderr );
226                         exit( EXIT_FAILURE );
227                 }
228                 p ^= 1;
229         }
230 }
231 #endif /* SHELL_SURROGATE_PARENT */
232
233 pid_t
234 forkandexec(
235     Cmd_info  args,
236     FILE        **rfp,
237     FILE        **wfp
238 )
239 {
240         int     p2c[2] = { -1, -1 }, c2p[2];
241         pid_t   pid;
242
243         if ( pipe( p2c ) != 0 || pipe( c2p ) != 0 ) {
244                 Debug( LDAP_DEBUG_ANY, "pipe failed\n", 0, 0, 0 );
245                 close( p2c[0] );
246                 close( p2c[1] );
247                 return( -1 );
248         }
249
250         /*
251          * what we're trying to set up looks like this:
252          *      parent *wfp -> p2c[1] | p2c[0] -> stdin child
253          *      parent *rfp <- c2p[0] | c2p[1] <- stdout child
254          */
255
256 #ifdef SHELL_SURROGATE_PARENT
257
258         {
259                 int io[2] = { p2c[0], c2p[1] }, i, c;
260                 static char dummy = '\0';
261                 static struct iovec iov = { &dummy, 1 };
262                 struct msghdr msg;
263 # ifdef CMSG_SPACE
264                 union {
265                         struct cmsghdr cm;
266                         char control[CMSG_SPACE(sizeof(io))];
267                 } control_un;
268                 struct cmsghdr *cmptr;
269 # endif
270
271                 /* clear msghdr */
272                 memset( &msg, 0, sizeof msg );
273
274 # ifdef CMSG_SPACE
275                 msg.msg_control = control_un.control;
276                 msg.msg_controllen = sizeof(control_un.control);
277                 cmptr = CMSG_FIRSTHDR(&msg);
278                 cmptr->cmsg_len = CMSG_LEN(sizeof(io));
279                 cmptr->cmsg_level = SOL_SOCKET;
280                 cmptr->cmsg_type = SCM_RIGHTS;
281                 memcpy( CMSG_DATA(cmptr), io, sizeof(io) );
282 # else
283                 msg.msg_accrights = (caddr_t) io;
284                 msg.msg_accrightslen = sizeof(io);
285 # endif
286
287                 msg.msg_name = NULL;
288                 msg.msg_namelen = 0;
289                 msg.msg_iov = &iov;
290                 msg.msg_iovlen = 1;
291
292                 ldap_pvt_thread_mutex_lock( &shell_surrogate_index_mutex );
293                 i = shell_surrogate_index ^= 1;
294                 ldap_pvt_thread_mutex_unlock( &shell_surrogate_index_mutex );
295                 ldap_pvt_thread_mutex_lock( &shell_surrogate_fd_mutex[i] );
296                 c = (sendmsg( shell_surrogate_fd[i], &msg, 0 ) == 1 &&
297                          nwrite( shell_surrogate_fd[i], &args.bv_len,
298                                  sizeof(args.bv_len) ) == sizeof(args.bv_len) &&
299                          nwrite( shell_surrogate_fd[i], args.bv_val,
300                                  args.bv_len ) == args.bv_len &&
301                          nread( shell_surrogate_fd[i], &pid,
302                                 sizeof(pid) ) == sizeof(pid));
303                 ldap_pvt_thread_mutex_unlock( &shell_surrogate_fd_mutex[i] );
304                 close( p2c[0] );
305                 close( c2p[1] );
306                 if ( !c ) {
307                         Debug( LDAP_DEBUG_ANY, "process creation failed\n", 0, 0, 0 );
308                         close( p2c[1] );
309                         close( c2p[0] );
310                         close( shell_surrogate_fd[0] );
311                         close( shell_surrogate_fd[1] );
312                         shell_surrogate_fd[0] =
313                                 shell_surrogate_fd[1] = -1;
314                         return( -1 );
315                 }
316         }
317
318 #else /* !SHELL_SURROGATE_PARENT */
319
320         fflush( NULL );
321 # ifdef HAVE_THR
322         pid = fork1();
323 # else
324         pid = fork();
325 # endif
326         if ( pid == 0 ) {               /* child */
327                 /*
328                  * child could deadlock here due to resources locked
329                  * by our parent
330                  *
331                  * If so, configure --without-threads.
332                  */
333                 if ( dup2( p2c[0], 0 ) == -1 || dup2( c2p[1], 1 ) == -1 ) {
334                         Debug( LDAP_DEBUG_ANY, "dup2 failed\n", 0, 0, 0 );
335                         exit( EXIT_FAILURE );
336                 }
337         }
338         close( p2c[0] );
339         close( c2p[1] );
340         if ( pid <= 0 ) {
341                 close( p2c[1] );
342                 close( c2p[0] );
343         }
344         switch ( pid ) {
345         case 0:
346                 execv( args[0], args );
347
348                 Debug( LDAP_DEBUG_ANY, "execv failed\n", 0, 0, 0 );
349                 exit( EXIT_FAILURE );
350
351         case -1:        /* trouble */
352                 Debug( LDAP_DEBUG_ANY, "fork failed\n", 0, 0, 0 );
353                 return( -1 );
354         }
355
356 #endif /* SHELL_SURROGATE_PARENT */
357
358         /* parent */
359         if ( (*rfp = fdopen( c2p[0], "r" )) == NULL || (*wfp = fdopen( p2c[1],
360             "w" )) == NULL ) {
361                 Debug( LDAP_DEBUG_ANY, "fdopen failed\n", 0, 0, 0 );
362                 close( c2p[0] );
363                 close( p2c[1] );
364
365                 return( -1 );
366         }
367
368         return( pid );
369 }