aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/unix
diff options
context:
space:
mode:
authorLukas Larsson <[email protected]>2015-08-27 10:56:15 +0200
committerLukas Larsson <[email protected]>2015-12-15 10:05:45 +0100
commit3599b995428032eb26e8b7cc6ac52bc5260ee454 (patch)
treea1d0757e32d682e3bc91e7d5db462742fca18330 /erts/emulator/sys/unix
parent05823de18bd48b70b398a6b6fd756a0f71587283 (diff)
downloadotp-3599b995428032eb26e8b7cc6ac52bc5260ee454.tar.gz
otp-3599b995428032eb26e8b7cc6ac52bc5260ee454.tar.bz2
otp-3599b995428032eb26e8b7cc6ac52bc5260ee454.zip
erts: Make child_setup work with large environments
Diffstat (limited to 'erts/emulator/sys/unix')
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c26
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c66
2 files changed, 61 insertions, 31 deletions
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 8bec36be60..6e0a7c143f 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -109,7 +109,7 @@ static int sigchld_pipe[2];
static int
start_new_child(int pipes[])
{
- int size, res, i;
+ int size, res, i, pos = 0;
char *buff, *o_buff;
char *cmd, *wd, **new_environ, **args = NULL, cbuff[1];
@@ -126,10 +126,19 @@ start_new_child(int pipes[])
DEBUG_PRINT("size = %d", size);
- if ((res = read(pipes[0], buff, size)) != size) {
- ABORT("Failed to read %d bytes from %d (%d,%d)",
- size, pipes[0], res, errno);
- }
+ do {
+ if ((res = read(pipes[0], buff + pos, size - pos)) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ ABORT("Failed to read %d bytes from %d (%d,%d)",
+ size, pipes[0], res, errno);
+ }
+ if (res == 0) {
+ errno = EPIPE;
+ goto child_error;
+ }
+ pos += res;
+ } while(size - pos != 0);
o_buff = buff;
@@ -154,6 +163,7 @@ start_new_child(int pipes[])
buff += sizeof(Sint32);
new_environ = malloc(sizeof(char*)*(cnt + 1));
+ DEBUG_PRINT("env_len = %ld", cnt);
for (i = 0; i < cnt; i++, buff++) {
new_environ[i] = buff;
while(*buff != '\0') buff++;
@@ -176,6 +186,7 @@ start_new_child(int pipes[])
ABORT("Buff error: %p, %p:%p", o_buff, o_buff+size, buff);
}
+ DEBUG_PRINT("read ack");
if (read(pipes[0], cbuff, 1) < 1)
goto child_error;
@@ -230,7 +241,8 @@ start_new_child(int pipes[])
execle(SHELL, "sh", "-c", cmd, (char *) NULL, new_environ);
}
child_error:
- _exit(1);
+ DEBUG_PRINT("exec error: %d\r\n",errno);
+ _exit(128 + errno);
}
@@ -392,7 +404,7 @@ main(int argc, char *argv[])
int ibuff[2];
char buff[256];
res = read(sigchld_pipe[0], ibuff, sizeof(ibuff));
- if (res < 0) {
+ if (res <= 0) {
if (errno == EINTR)
continue;
ABORT("Failed to read from sigchld pipe: %d (%d)", res, errno);
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 97a9c3dfaa..6a1f2f6b2c 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -558,7 +558,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
int len;
char **new_environ;
- ErtsSysDriverData *res;
+ ErtsSysDriverData *dd;
char *cmd_line;
char wd_buff[MAXPATHLEN+1];
char *wd;
@@ -658,10 +658,10 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
{
struct iovec *io_vector;
- int buffsz = 0, iov_len = 5;
+ int iov_len = 5;
char nullbuff[] = "\0";
- int j, i = 0;
- Sint32 env_len = 0, argv_len = 0,
+ int j, i = 0, res;
+ Sint32 buffsz = 0, env_len = 0, argv_len = 0,
flags = (opts->use_stdio ? FORKER_FLAG_USE_STDIO : 0)
| (opts->exit_status ? FORKER_FLAG_EXIT_STATUS : 0)
| (opts->read_write & DO_READ ? FORKER_FLAG_DO_READ : 0)
@@ -753,16 +753,29 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
}
/* we send the request to do the fork */
- if (writev(ofd[1], io_vector, iov_len) < 0) {
- int err = errno;
- close_pipes(ifd, ofd);
- erts_free(ERTS_ALC_T_TMP, io_vector);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- errno = err;
- return ERL_DRV_ERROR_ERRNO;
+ if ((res = writev(ofd[1], io_vector, iov_len > MAXIOV ? MAXIOV : iov_len)) < 0) {
+ if (errno == ERRNO_BLOCK) {
+ res = 0;
+ } else {
+ int err = errno;
+ close_pipes(ifd, ofd);
+ erts_free(ERTS_ALC_T_TMP, io_vector);
+ if (new_environ != environ)
+ erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ errno = err;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ }
+
+ if (res < buffsz) {
+ /* we only wrote part of the command payload. Enqueue the rest. */
+ for (i = 0; i < iov_len; i++) {
+ driver_enq(port_num, io_vector[i].iov_base, io_vector[i].iov_len);
+ }
+ driver_deq(port_num, res);
+ driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1);
}
erts_free(ERTS_ALC_T_TMP, io_vector);
@@ -775,7 +788,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
erts_smp_rwmtx_runlock(&environ_rwmtx);
- res = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
+ dd = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
DO_WRITE | DO_READ, opts->exit_status,
0, 0);
@@ -797,12 +810,12 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
/* we set these fds to negative to mark if
they should be closed after the handshake */
if (!(opts->read_write & DO_READ))
- res->ifd->fd *= -1;
+ dd->ifd->fd *= -1;
if (!(opts->read_write & DO_WRITE))
- res->ofd->fd *= -1;
+ dd->ofd->fd *= -1;
- return (ErlDrvData)res;
+ return (ErlDrvData)dd;
#undef CMD_LINE_PREFIX_STR
#undef CMD_LINE_PREFIX_STR_SZ
}
@@ -1365,10 +1378,16 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
return;
}
- if (write(abs(dd->ofd->fd), "A", 1) < 0)
- ; /* do nothing on failure here. If the ofd is broken, then
- the ifd will probably also be broken and trigger
- a port_inp_failure */
+ if (driver_sizeq(port_num) > 0) {
+ driver_enq(port_num, "A", 1);
+ } else {
+ if (write(abs(dd->ofd->fd), "A", 1) < 0)
+ if (errno == ERRNO_BLOCK || errno == EINTR)
+ driver_enq(port_num, "A", 1);
+ /* do nothing on failure here. If the ofd is broken, then
+ the ifd will probably also be broken and trigger
+ a port_inp_failure */
+ }
if (dd->ifd->fd < 0) {
driver_select(port_num, abs(dd->ifd->fd), ERL_DRV_READ|ERL_DRV_USE, 0);
@@ -1381,9 +1400,8 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
child setup will close this fd if fd < 0 */
driver_select(port_num, abs(dd->ofd->fd), ERL_DRV_WRITE|ERL_DRV_USE, 1);
- if (dd->alive == 1) {
+ if (dd->alive == 1)
forker_add_os_pid_mapping(dd);
- }
erl_drv_set_os_pid(port_num, dd->pid);
erl_drv_init_ack(port_num, e);