From 46e16383d62990b4b07eac32560ca8abec82b710 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 21 Jun 2016 14:49:03 +0200 Subject: erts: Improve error printouts in erl_child_setup Add prints to stderr with a small description of the error so that errors will be easier to debug. Also if a protocol error is detected, erl_child_setup will abort instead of exit. --- erts/emulator/sys/unix/erl_child_setup.c | 51 +++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 6beb316350..b2263371dd 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -123,6 +123,7 @@ static int sigchld_pipe[2]; static int start_new_child(int pipes[]) { + int errln = -1; int size, res, i, pos = 0; char *buff, *o_buff; @@ -137,6 +138,7 @@ start_new_child(int pipes[]) } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); if (res <= 0) { + errln = __LINE__; goto child_error; } @@ -148,10 +150,12 @@ start_new_child(int pipes[]) if ((res = read(pipes[0], buff + pos, size - pos)) < 0) { if (errno == ERRNO_BLOCK || errno == EINTR) continue; + errln = __LINE__; goto child_error; } if (res == 0) { errno = EPIPE; + errln = __LINE__; goto child_error; } pos += res; @@ -201,7 +205,12 @@ start_new_child(int pipes[]) if (o_buff + size != buff) { errno = EINVAL; - goto child_error; + errln = __LINE__; + fprintf(stderr,"erl_child_setup: failed with protocol " + "error %d on line %d", errno, errln); + /* we abort here as it is most likely a symptom of an + emulator/erl_child_setup bug */ + abort(); } DEBUG_PRINT("read ack"); @@ -215,11 +224,17 @@ start_new_child(int pipes[]) } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); if (res < 1) { errno = EPIPE; + errln = __LINE__; goto child_error; } DEBUG_PRINT("Do that forking business: '%s'\n",cmd); + if (wd && chdir(wd) < 0) { + errln = __LINE__; + goto child_error; + } + /* When the dup2'ing below is done, only fd's 0, 1, 2 and maybe 3, 4 should survive the exec. All other fds (i.e. the unix domain sockets @@ -228,25 +243,34 @@ start_new_child(int pipes[]) if (flags & FORKER_FLAG_USE_STDIO) { /* stdin for process */ if (flags & FORKER_FLAG_DO_WRITE && - dup2(pipes[0], 0) < 0) + dup2(pipes[0], 0) < 0) { + errln = __LINE__; goto child_error; + } /* stdout for process */ if (flags & FORKER_FLAG_DO_READ && - dup2(pipes[1], 1) < 0) + dup2(pipes[1], 1) < 0) { + errln = __LINE__; goto child_error; + } } else { /* XXX will fail if pipes[0] == 4 (unlikely..) */ - if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0) + if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0) { + errln = __LINE__; goto child_error; - if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0) + } + if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0) { + errln = __LINE__; goto child_error; + } } - if (dup2(pipes[2], 2) < 0) - goto child_error; - - if (wd && chdir(wd) < 0) + /* we do the dup2 of stderr last so that errors + in child_error will be printed to stderr */ + if (dup2(pipes[2], 2) < 0) { + errln = __LINE__; goto child_error; + } #if defined(USE_SETPGRP_NOARGS) /* SysV */ (void) setpgrp(); @@ -268,9 +292,14 @@ start_new_child(int pipes[]) } else { execle(SHELL, "sh", "-c", cmd, (char *) NULL, new_environ); } + + DEBUG_PRINT("exec error: %d",errno); + _exit(errno); + child_error: - DEBUG_PRINT("exec error: %d\r\n",errno); - _exit(128 + errno); + fprintf(stderr,"erl_child_setup: failed with error %d on line %d", + errno, errln); + _exit(errno); } -- cgit v1.2.3 From 3411d9e13bd8a4b3aee530d8f66b5118e14e447b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 21 Jun 2016 14:57:37 +0200 Subject: erts: Fix HARD_DEBUG printouts in erl_child_setup --- erts/emulator/sys/unix/erl_child_setup.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index b2263371dd..219ca98c71 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -74,7 +74,7 @@ //#define HARD_DEBUG #ifdef HARD_DEBUG -#define DEBUG_PRINT(fmt, ...) fprintf(stderr, fmt "\r\n", ##__VA_ARGS__) +#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "%d:" fmt "\r\n", getpid(), ##__VA_ARGS__) #else #define DEBUG_PRINT(fmt, ...) #endif @@ -129,7 +129,7 @@ start_new_child(int pipes[]) char *cmd, *wd, **new_environ, **args = NULL; - Sint cnt, flags; + Sint32 cnt, flags; /* only child executes here */ @@ -164,7 +164,7 @@ start_new_child(int pipes[]) o_buff = buff; flags = get_int32(buff); - buff += sizeof(Sint32); + buff += sizeof(flags); DEBUG_PRINT("flags = %d", flags); @@ -181,10 +181,10 @@ start_new_child(int pipes[]) DEBUG_PRINT("wd = %s", wd); cnt = get_int32(buff); - buff += sizeof(Sint32); + buff += sizeof(cnt); new_environ = malloc(sizeof(char*)*(cnt + 1)); - DEBUG_PRINT("env_len = %ld", cnt); + DEBUG_PRINT("env_len = %d", cnt); for (i = 0; i < cnt; i++, buff++) { new_environ[i] = buff; while(*buff != '\0') buff++; @@ -194,7 +194,7 @@ start_new_child(int pipes[]) if (o_buff + size != buff) { /* This is a spawn executable call */ cnt = get_int32(buff); - buff += sizeof(Sint32); + buff += sizeof(cnt); args = malloc(sizeof(char*)*(cnt + 1)); for (i = 0; i < cnt; i++, buff++) { args[i] = buff; @@ -228,13 +228,15 @@ start_new_child(int pipes[]) goto child_error; } - DEBUG_PRINT("Do that forking business: '%s'\n",cmd); + DEBUG_PRINT("Set wd to: '%s'",wd); if (wd && chdir(wd) < 0) { errln = __LINE__; goto child_error; } + DEBUG_PRINT("Do that forking business: '%s'",cmd); + /* When the dup2'ing below is done, only fd's 0, 1, 2 and maybe 3, 4 should survive the exec. All other fds (i.e. the unix domain sockets @@ -297,7 +299,7 @@ start_new_child(int pipes[]) _exit(errno); child_error: - fprintf(stderr,"erl_child_setup: failed with error %d on line %d", + fprintf(stderr,"erl_child_setup: failed with error %d on line %d\r\n", errno, errln); _exit(errno); } @@ -490,7 +492,7 @@ main(int argc, char *argv[]) proto.action = ErtsSysForkerProtoAction_SigChld; proto.u.sigchld.error_number = ibuff[1]; - DEBUG_PRINT("send %s to %d", buff, uds_fd); + DEBUG_PRINT("send sigchld to %d (errno = %d)", uds_fd, ibuff[1]); if (write(uds_fd, &proto, sizeof(proto)) < 0) { if (errno == EINTR) continue; -- cgit v1.2.3 From 412d7dfcfb667bb2b329b8e35c929c2b5abcc2b1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 27 Jun 2016 18:40:36 +0200 Subject: erts: Fix spawn driver with relative cd option --- erts/emulator/sys/unix/erl_child_setup.c | 22 ++++++++++++-- erts/emulator/sys/unix/sys_drivers.c | 42 +++++++++++++++------------ erts/emulator/test/port_SUITE.erl | 50 ++++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 23 deletions(-) diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 219ca98c71..fa4750a6e9 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -127,7 +127,7 @@ start_new_child(int pipes[]) int size, res, i, pos = 0; char *buff, *o_buff; - char *cmd, *wd, **new_environ, **args = NULL; + char *cmd, *cwd, *wd, **new_environ, **args = NULL; Sint32 cnt, flags; @@ -170,6 +170,10 @@ start_new_child(int pipes[]) cmd = buff; buff += strlen(buff) + 1; + + cwd = buff; + buff += strlen(buff) + 1; + if (*buff == '\0') { wd = NULL; } else { @@ -222,17 +226,29 @@ start_new_child(int pipes[]) ASSERT(res == sizeof(proto)); } } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); + if (res < 1) { errno = EPIPE; errln = __LINE__; goto child_error; } + DEBUG_PRINT("Set cwd to: '%s'",cwd); + + if (chdir(cwd) < 0) { + /* This is not good, it probably means that the cwd of + beam is invalid. We ignore it and try anyways as + the child might now need a cwd or the chdir below + could take us to a valid directory. + */ + } + DEBUG_PRINT("Set wd to: '%s'",wd); if (wd && chdir(wd) < 0) { - errln = __LINE__; - goto child_error; + int err = errno; + fprintf(stderr,"spawn: Could not cd to %s\r\n", wd); + _exit(err); } DEBUG_PRINT("Do that forking business: '%s'",cmd); diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 812112fb91..78e7086bd8 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -554,7 +554,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, ErtsSysDriverData *dd; char *cmd_line; char wd_buff[MAXPATHLEN+1]; - char *wd; + char *wd, *cwd; int ifd[2], ofd[2], stderrfd; if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO; @@ -631,24 +631,22 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, return ERL_DRV_ERROR_ERRNO; } - if (opts->wd == NULL) { - if ((wd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) { - /* on some OSs this call opens a fd in the - background which means that this can - return EMFILE */ - int err = errno; - close_pipes(ifd, ofd); - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - if (new_environ != environ) - erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); - erts_smp_rwmtx_runlock(&environ_rwmtx); - errno = err; - return ERL_DRV_ERROR_ERRNO; - } - } else { - wd = opts->wd; + if ((cwd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) { + /* on some OSs this call opens a fd in the + background which means that this can + return EMFILE */ + int err = errno; + close_pipes(ifd, ofd); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + if (new_environ != environ) + erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); + erts_smp_rwmtx_runlock(&environ_rwmtx); + errno = err; + return ERL_DRV_ERROR_ERRNO; } + wd = opts->wd; + { struct iovec *io_vector; int iov_len = 5; @@ -660,6 +658,8 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, | (opts->read_write & DO_READ ? FORKER_FLAG_DO_READ : 0) | (opts->read_write & DO_WRITE ? FORKER_FLAG_DO_WRITE : 0); + if (wd) iov_len++; + /* count number of elements in environment */ while(new_environ[env_len] != NULL) env_len++; @@ -700,10 +700,16 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, io_vector[i++].iov_len = len; buffsz += len; - io_vector[i].iov_base = wd; + io_vector[i].iov_base = cwd; io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1; buffsz += io_vector[i++].iov_len; + if (wd) { + io_vector[i].iov_base = wd; + io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1; + buffsz += io_vector[i++].iov_len; + } + io_vector[i].iov_base = nullbuff; io_vector[i++].iov_len = 1; buffsz += io_vector[i-1].iov_len; diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index ee07699884..6115071b79 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -83,6 +83,7 @@ bad_port_messages/1, basic_ping/1, cd/1, + cd_relative/1, close_deaf_port/1, count_fds/1, dying_port/1, @@ -137,7 +138,7 @@ win_massive_client/1 ]). --export([do_iter_max_ports/2]). +-export([do_iter_max_ports/2, relative_cd/0]). %% Internal exports. -export([tps/3]). @@ -158,7 +159,7 @@ all() -> {group, multiple_packets}, parallell, dying_port, port_program_with_path, open_input_file_port, open_output_file_port, name1, env, huge_env, bad_env, cd, - bad_args, + cd_relative, bad_args, exit_status, iter_max_ports, count_fds, t_exit, {group, tps}, line, stderr_to_stdout, otp_3906, otp_4389, win_massive, mix_up_ports, otp_5112, otp_5119, @@ -1065,6 +1066,51 @@ cd(Config) when is_list(Config) -> end, ok. +%% Test that an emulator that has set it's cwd to +%% something other then when it started, can use +%% relative {cd,"./"} to open port and that cd will +%% be relative the new cwd and not the original +cd_relative(Config) -> + + Program = atom_to_list(lib:progname()), + DataDir = proplists:get_value(data_dir, Config), + TestDir = filename:join(DataDir, "dir"), + + Cmd = Program ++ " -pz " ++ filename:dirname(code:where_is_file("port_SUITE.beam")) ++ + " -noshell -s port_SUITE relative_cd -s erlang halt", + + _ = open_port({spawn, Cmd}, [{line, 256}, {cd, TestDir}]), + + receive + {_, {data, {eol, String}}} -> + case filename_equal(String, TestDir) of + true -> + ok; + false -> + ct:fail({cd_relative, String}) + end; + Other -> + ct:fail(Other) + end. + +relative_cd() -> + + Program = atom_to_list(lib:progname()), + ok = file:set_cwd(".."), + {ok, Cwd} = file:get_cwd(), + + Cmd = Program ++ " -pz " ++ Cwd ++ + " -noshell -s port_test pwd -s erlang halt", + + _ = open_port({spawn, Cmd}, [{line, 256}, {cd, "./dir"}, exit_status]), + + receive + {_, {data, {eol, String}}} -> + io:format("~s~n",[String]); + Other -> + io:format("ERROR: ~p~n",[Other]) + end. + filename_equal(A, B) -> case os:type() of {win32, _} -> -- cgit v1.2.3 From a93036e0cd9c3620ece4bfcb56c6b0c44abf9a58 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 1 Jul 2016 11:16:42 +0200 Subject: erts: Add port_SUITE:cd invalid dir testcase --- erts/emulator/test/port_SUITE.erl | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 6115071b79..a683307722 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1037,8 +1037,7 @@ cd(Config) when is_list(Config) -> Cmd = Program ++ " -pz " ++ DataDir ++ " -noshell -s port_test pwd -s erlang halt", _ = open_port({spawn, Cmd}, - [{cd, TestDir}, - {line, 256}]), + [{cd, TestDir}, {line, 256}]), receive {_, {data, {eol, String}}} -> case filename_equal(String, TestDir) of @@ -1064,7 +1063,29 @@ cd(Config) when is_list(Config) -> Other3 -> ct:fail({env, Other3}) end, - ok. + + InvalidDir = filename:join(DataDir, "invaliddir"), + try open_port({spawn, Cmd}, + [{cd, InvalidDir}, exit_status, {line, 256}]) of + _ -> + receive + {_, {exit_status, _}} -> + ok; + Other4 -> + ct:fail({env, Other4}) + end + catch error:eacces -> + %% This happens on Windows + ok + end, + + %% Check that there are no lingering messages + receive + Other5 -> + ct:fail({env, Other5}) + after 10 -> + ok + end. %% Test that an emulator that has set it's cwd to %% something other then when it started, can use -- cgit v1.2.3