/** Copy an environment and extend certain paths. * This will create a vector which comprises the environment in @p environ. * The path_ext are assumed to be pairwise entries of environment variable * name followed by an entry for the path extensions. Paths are here * colon-separated strings of paths, e.g. like the PATH environment variable. * If the variable had already been set, the given paths are appended to * the variable (a closing colon will be maintained if it exists). If they * were not set before, the entry is added. * @param environ environment to copy * @param path_ext path extension, an array of an odd number of elements, * always pairwise an entry for the variable name followed by the path extension. * The last element must always be NULL. * @return vector of strings with copied and extended environment */ std::vector<std::string> envp_copy_expand(char *environ[], const char *path_ext[]) { std::list<std::tuple<std::string, std::string, bool>> path_ext_m; for (size_t i = 0; path_ext[i] && path_ext[i+1]; i += 2) { std::string match = std::string(path_ext[i]) + "="; path_ext_m.push_back(std::make_tuple(match, std::string(path_ext[i+1]), false)); } unsigned int extra_ent = 0; size_t environ_length = 0; for (size_t i = 0; environ[i]; ++i) { ++environ_length; std::string ev = environ[i]; for (auto &m : path_ext_m) { if (ev.find(std::get<0>(m)) == 0) { std::get<2>(m) = true; ++extra_ent; break; } } } size_t envp_length = environ_length + extra_ent; std::vector<std::string> envp(envp_length); for (size_t i = 0; environ[i]; ++i) { std::string ev(environ[i]); for (auto m : path_ext_m) { if (ev.find(std::get<0>(m)) == 0) { // modify if (ev[ev.length()-1] == ':') { ev += std::get<1>(m) + ":"; } else { ev += ":" + std::get<1>(m); } } } envp[i] = ev; } unsigned int extra_ind = 0; for (auto m : path_ext_m) { if (! std::get<2>(m)) { std::string ev = std::get<0>(m) + std::get<1>(m) + ":"; envp[envp_length - extra_ent + extra_ind++] = ev; } } return envp; }
EIF_BOOLEAN basic_exec_win32_execute(se_exec_data_t*data, char*args, EIF_BOOLEAN keep_env, char*add_env, HANDLE*in_h, HANDLE*out_h, HANDLE*err_h) { STARTUPINFO start_info; EIF_BOOLEAN result = 0; ZeroMemory( &start_info, sizeof(STARTUPINFO) ); start_info.cb = sizeof(STARTUPINFO); if(in_h) { start_info.hStdInput = in_h[0]; SetHandleInformation(in_h[1], HANDLE_FLAG_INHERIT, 0); } else { start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); } if(INVALID_HANDLE_VALUE == start_info.hStdInput) goto leave; if(out_h) { start_info.hStdOutput = out_h[1]; SetHandleInformation(out_h[0], HANDLE_FLAG_INHERIT, 0); } else { start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); } if(INVALID_HANDLE_VALUE == start_info.hStdOutput) goto leave; if(err_h) { start_info.hStdError = err_h[1]; SetHandleInformation(err_h[0], HANDLE_FLAG_INHERIT, 0); } else { start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); } if(INVALID_HANDLE_VALUE == start_info.hStdError) goto leave; start_info.dwFlags |= STARTF_USESTDHANDLES; if(CreateProcess(NULL, args, NULL, /* process security attributes */ NULL, /* primary thread security attributes */ TRUE, /* handles are inherited */ 0, /* creation flags */ keep_env?NULL:envp(), NULL, /* use parent's current directory */ &start_info, /* STARTUPINFO pointer */ &data->process_information)) { /* receives PROCESS_INFORMATION */ CloseHandle(data->process_information.hThread); data->running = 1; result = 1; } leave: if(in_h) CloseHandle(in_h[0]); if(out_h) CloseHandle(out_h[1]); if(err_h) CloseHandle(err_h[1]); return result; }
void sys_game_process_exitspawn2( u32 path_addr, u32 argv_addr, u32 envp_addr, u32 data_addr, u32 data_size, u32 prio, u64 flags) { sc_p.Error("sys_game_process_exitspawn2 UNIMPLEMENTED"); sc_p.Warning("path: %s", Memory.ReadString(path_addr).c_str()); sc_p.Warning("argv: 0x%x", argv_addr); sc_p.Warning("envp: 0x%x", envp_addr); sc_p.Warning("data: 0x%x", data_addr); sc_p.Warning("data_size: 0x%x", data_size); sc_p.Warning("prio: %d", prio); sc_p.Warning("flags: %d", flags); std::string path = Memory.ReadString(path_addr); std::vector<std::string> argv; std::vector<std::string> env; mem_ptr_t<u32> argvp(argv_addr); while (argvp.GetAddr() && argvp.IsGood() && *argvp) { argv.push_back(Memory.ReadString(Memory.Read32(argvp.GetAddr()))); argvp++; } mem_ptr_t<u32> envp(envp_addr); while (envp.GetAddr() && envp.IsGood() && *envp) { env.push_back(Memory.ReadString(Memory.Read32(envp.GetAddr()))); envp++; } for (auto &arg : argv){ sc_p.Log("argument: %s", arg.c_str()); } for (auto &en : env){ sc_p.Log("env_argument: %s", en.c_str()); } //TODO: execute the file in <path> with the args in argv //and the environment parameters in envp and copy the data //from data_addr into the adress space of the new process //then kill the current process return; }
EIF_BOOLEAN basic_exec_posix_execute(se_exec_data_t*data, char*prog, char**args, EIF_BOOLEAN keep_env, char**add_env, int* in_fd, int* out_fd, int* err_fd) { int id = fork(); if (id == 0) { /* child */ if(in_fd) { dup2(in_fd[0], 0); close(in_fd[1]); } if(out_fd) { dup2(out_fd[1], 1); close(out_fd[0]); } if(err_fd) { dup2(err_fd[1], 2); close(err_fd[0]); } if (prog == NULL && args == NULL) { data->running = 1; data->child = 1; #ifdef SE_SEDB sedb_duplicate(); #endif return 1; } else { if (add_env == NULL && keep_env) { execvp(prog, args); /* NO RETURN in child */ se_print_run_time_stack(); exit(1); }else{ char** new_env; char** old_env; int old_size, add_size; int src, dest = 0; if(keep_env){ old_env = environ; }else{ old_env = envp(); } old_size = arr_size(old_env); add_size = arr_size(add_env); new_env = malloc(sizeof(void*) * (old_size + add_size)); /* we first copy the pointers from the old env */ for(src = 0; src < old_size; src++){ new_env[dest++] = old_env[src]; } /* now the ones from add_env */ for(src = 0; src < add_size; src++){ int override = find_variable(old_env, add_env[src]); if (override >= 0){ new_env[override] = add_env[src]; }else{ new_env[dest++] = add_env[src]; } } execve(prog, args, new_env); /* NO RETURN in child */ se_print_run_time_stack(); exit(1); } } }
/* * Class: VM_0005fProcess * Method: exec4 * Signature: (Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_ibm_JikesRVM_VM_1Process_exec4 (JNIEnv *env, jobject self, jstring programName, jobjectArray argvArguments, jobjectArray environment, jstring dirPathStr) { // Get the program name StringPtr programString(convertString(env, programName)); #ifdef DEBUG fprintf(stderr, "program name is %s\n", programString.get()); #endif // Build argv array jsize argvLen = env->GetArrayLength((jarray) argvArguments); StringArray argv(argvLen); for (int i = 0; i < argvLen; ++i) { jstring arg = (jstring) env->GetObjectArrayElement(argvArguments, i); assert(arg); char *str = convertString(env, arg); #ifdef DEBUG fprintf(stderr, "arg %d is %s\n", i, str); #endif argv.setAndAdoptString(i, str); } // Build environment array (if any) jsize envpLen = (environment != 0) ? env->GetArrayLength((jarray) environment) : 0; StringArray envp(envpLen); for (int i = 0; i < envpLen; ++i) { jstring arg = (jstring) env->GetObjectArrayElement(environment, i); assert(arg); char *str = convertString(env, arg); #ifdef DEBUG fprintf(stderr, "env %d is %s\n", i, str); #endif envp.setAndAdoptString(i, str); } // Get the directory path (if any) StringPtr dirPath( (dirPathStr != 0) ? convertString(env, dirPathStr) : 0 ); #ifdef DEBUG fprintf(stderr, "working directory is %s\n", (dirPath.get() != 0) ? dirPath.get() : "unspecified, will use current"); #endif // Create pipes to communicate with child process. jclass ProcessClassID = env->FindClass( "com/ibm/JikesRVM/VM_Process" ); assert(ProcessClassID); int inputPipe[2], outputPipe[2], errorPipe[2]; pid_t fid = -1; int ret = createPipe(inputPipe, env, ProcessClassID, self, "inputDescriptor", OUTPUT); if (ret) goto fail; ret = createPipe(outputPipe, env, ProcessClassID, self, "outputDescriptor", INPUT); if (ret) goto close_inputPipe_and_fail; ret = createPipe(errorPipe, env, ProcessClassID, self, "errorDescriptor", INPUT); if (ret) goto close_outputPipe_and_fail; // do the exec fid = fork(); if (fid == 0) { // child // If a working directory was specified, try to // make it the current directory. if (dirPath.get() != 0) { if (chdir(dirPath.get()) != 0) { #ifdef DEBUG fprintf(stderr, "chdir() failed: %s\n", strerror(errno)); #endif // FIXME: // Presumably we should throw some sort of I/O error // (from Runtime.exec()) if we can't change into the // working directory the caller specified. // Instead, we just return this value as the exit code. exit(EXIT_STATUS_BAD_WORKING_DIR); } } #define SHOULD_NEVER_FAIL(cmd) do \ { \ if ((cmd) < 0) { \ perror(#cmd " failed, but should never; aborting"); \ abort(); \ } \ } while(0) /* Attach pipes to stdin, stdout, stderr These absolutely should never fail. */ SHOULD_NEVER_FAIL(dup2(inputPipe[INPUT], 0)); SHOULD_NEVER_FAIL(dup2(outputPipe[OUTPUT], 1)); SHOULD_NEVER_FAIL(dup2(errorPipe[OUTPUT], 2)); /* Close the original file descriptors returned by pipe(). Since they're already open, they should never fail either. */ SHOULD_NEVER_FAIL(closePipe(inputPipe)); SHOULD_NEVER_FAIL(closePipe(outputPipe)); SHOULD_NEVER_FAIL(closePipe(errorPipe)); // Set environment for child process. if (environment != 0) { environ = envp.get(); } #if 0 else { fprintf(stderr, "Current environment:\n"); char **p = environ; while (*p != 0 ) { fprintf(stderr, "\t%s\n", *p); ++p; } } #endif // Execute the program. // XXX See comment below on error handling. // int err = execvp(programString.get(), argv.get()); (void) execvp(programString.get(), argv.get()); // We get here only if an error occurred. #ifdef DEBUG fprintf(stderr, "execvp() failed: %s\n", strerror(errno)); #endif programString.release(); argv.release(); envp.release(); dirPath.release(); // FIXME: // Unfortunately, it's difficult to convey an error code // back to the parent process to let it know that we couldn't // actually execute the program. We could use shared memory // or a special pipe to send the error information. // For now, just exit with a non-zero status. /* However, traditionally the shell and xargs use status 127 to mean that * they were unable to find something to execute. * To quote the bash manpage, "If a command is found * but is not executable, the return status is 126.¨ * We shall adopt those customs here. --Steve Augart*/ if (errno == ENOENT || errno == ENOTDIR) exit(EXIT_STATUS_EXECUTABLE_NOT_FOUND); exit(EXIT_STATUS_COULD_NOT_EXECUTE); // couldn't be executed for some // other reason. } else if (fid > 0) { // parent // Store child's pid jfieldID pidFieldID = env->GetFieldID(ProcessClassID, "pid", "I"); assert(pidFieldID); env->SetIntField(self, pidFieldID, fid); #ifdef DEBUG fprintf(stderr, "child process id is %d\n", fid); #endif // Close unused ends of pipes // input side of child's stdin: SHOULD_NEVER_FAIL(close(inputPipe[INPUT])); // output side of child's stdout: SHOULD_NEVER_FAIL(close(outputPipe[OUTPUT])); // output side of child's stderr SHOULD_NEVER_FAIL(close(errorPipe[OUTPUT])); // Note: memory for programName, argv, and envp will be cleaned // up automatically #ifdef DEBUG fprintf(stderr, "done exec\n"); #endif return fid; } else { // An error occurred in fork() #ifdef DEBUG fprintf(stderr, "fork() failed: %s\n", strerror(errno)); #endif // Close pipes closePipe(errorPipe); close_outputPipe_and_fail: closePipe(outputPipe); close_inputPipe_and_fail: closePipe(inputPipe); fail: return -1; } }
// Runs the provided command in a subprocess. Try<Subprocess> subprocess( const string& command, const Option<map<string, string> >& environment, const Option<lambda::function<int()> >& setup) { // Create pipes for stdin, stdout, stderr. // Index 0 is for reading, and index 1 is for writing. int stdinPipe[2]; int stdoutPipe[2]; int stderrPipe[2]; if (pipe(stdinPipe) == -1) { return ErrnoError("Failed to create pipe"); } else if (pipe(stdoutPipe) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); return ErrnoError("Failed to create pipe"); } else if (pipe(stderrPipe) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stdoutPipe[1]); return ErrnoError("Failed to create pipe"); } // We need to do this construction before doing the fork as it // might not be async-safe. // TODO(tillt): Consider optimizing this to not pass an empty map // into the constructor or even further to use execl instead of // execle once we have no user supplied environment. os::ExecEnv envp(environment.get(map<string, string>())); pid_t pid; if ((pid = fork()) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[0]); os::close(stderrPipe[1]); return ErrnoError("Failed to fork"); } Subprocess process; process.data->pid = pid; if (process.data->pid == 0) { // Child. // Close parent's end of the pipes. os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stderrPipe[0]); // Make our pipes look like stdin, stderr, stdout before we exec. while (dup2(stdinPipe[0], STDIN_FILENO) == -1 && errno == EINTR); while (dup2(stdoutPipe[1], STDOUT_FILENO) == -1 && errno == EINTR); while (dup2(stderrPipe[1], STDERR_FILENO) == -1 && errno == EINTR); // Close the copies. os::close(stdinPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[1]); if (setup.isSome()) { int status = setup.get()(); if (status != 0) { _exit(status); } } execle("/bin/sh", "sh", "-c", command.c_str(), (char*) NULL, envp()); ABORT("Failed to execle '/bin sh -c ", command.c_str(), "'\n"); } // Parent. // Close the child's end of the pipes. os::close(stdinPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[1]); process.data->in = stdinPipe[1]; process.data->out = stdoutPipe[0]; process.data->err = stderrPipe[0]; // Rather than directly exposing the future from process::reap, we // must use an explicit promise so that we can ensure we can receive // the termination signal. Otherwise, the caller can discard the // reap future, and we will not know when it is safe to close the // file descriptors. Promise<Option<int> >* promise = new Promise<Option<int> >(); process.data->status = promise->future(); // We need to bind a copy of this Subprocess into the onAny callback // below to ensure that we don't close the file descriptors before // the subprocess has terminated (i.e., because the caller doesn't // keep a copy of this Subprocess around themselves). process::reap(process.data->pid) .onAny(lambda::bind(internal::cleanup, lambda::_1, promise, process)); return process; }