Major redesign of the Exec API, in order to make linux more uniform with windows and introduce python shell
This commit is contained in:
@@ -257,204 +257,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
|
||||
return it;
|
||||
}
|
||||
|
||||
struct UnixProcess {
|
||||
pid_t pid;
|
||||
int child_stdout_read;
|
||||
int stdin_write;
|
||||
};
|
||||
|
||||
API Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Scratch scratch;
|
||||
const int PIPE_READ = 0;
|
||||
const int PIPE_WRITE = 1;
|
||||
bool error = false;
|
||||
|
||||
working_dir = Copy(scratch, working_dir);
|
||||
chdir(working_dir.data);
|
||||
|
||||
Process process = {};
|
||||
UnixProcess *plat = (UnixProcess *)&process.platform;
|
||||
Array<char *> args = SplitCommand(scratch, command_line);
|
||||
Array<char *> env = {scratch};
|
||||
|
||||
For (enviroment) {
|
||||
Add(&env, Copy(scratch, it).data);
|
||||
}
|
||||
|
||||
int stdout_desc[2] = {};
|
||||
int stdin_desc[2] = {};
|
||||
posix_spawn_file_actions_t actions = {};
|
||||
|
||||
if (posix_spawn_file_actions_init(&actions) != 0) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_init, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
};
|
||||
|
||||
if (pipe(stdout_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdout_desc[PIPE_READ]);
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
}
|
||||
};
|
||||
|
||||
if (pipe(stdin_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
close(stdin_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
}
|
||||
};
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdout_desc[PIPE_READ]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDOUT_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDOUT_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDERR_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDERR_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdin_desc[PIPE_WRITE]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdin_desc[PIPE_READ], STDIN_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDIN_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
pid_t process_pid = 0;
|
||||
error = posix_spawnp(&process_pid, args[0], &actions, NULL, args.data, env.data) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: failed to create process\n, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
|
||||
plat->child_stdout_read = stdout_desc[PIPE_READ];
|
||||
plat->stdin_write = stdin_desc[PIPE_WRITE];
|
||||
plat->pid = process_pid;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
process.id = process_pid;
|
||||
process.is_valid = true;
|
||||
return process;
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)&process->platform;
|
||||
if (process->is_valid == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLRDHUP | POLLERR | POLLHUP | POLLNVAL;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0) {
|
||||
pid_t result = waitpid(plat->pid, &status, 0);
|
||||
Assert(result != -1);
|
||||
process->exit_code = WEXITSTATUS(status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
kill(plat->pid, SIGKILL);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
|
||||
String result = {};
|
||||
result.data = AllocArray(allocator, char, 16 * 4096);
|
||||
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLIN;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0 || force_read) {
|
||||
result.len = read(plat->child_stdout_read, result.data, 4 * 4096);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
ssize_t size = write(plat->stdin_write, string.data, string.len);
|
||||
|
||||
Assert(size == string.len);
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
close(plat->stdin_write);
|
||||
}
|
||||
|
||||
#elif OS_WINDOWS
|
||||
|
||||
@@ -725,210 +527,6 @@ API MakeDirResult MakeDir(String path) {
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Win32Process {
|
||||
HANDLE handle;
|
||||
HANDLE child_stdout_read;
|
||||
HANDLE child_stdout_write;
|
||||
HANDLE child_stdin_read;
|
||||
HANDLE child_stdin_write;
|
||||
};
|
||||
// static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
||||
|
||||
static void Win32ReportError(String msg, String cmd) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
defer { LocalFree(lpMsgBuf); };
|
||||
char *buff = (char *)lpMsgBuf;
|
||||
size_t buffLen = strlen((const char *)buff);
|
||||
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
|
||||
Error("%S: %S! %s", msg, cmd, (char *)lpMsgBuf);
|
||||
}
|
||||
|
||||
static void Win32CloseProcess(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
|
||||
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
|
||||
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
|
||||
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
|
||||
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
|
||||
process->is_valid = false;
|
||||
}
|
||||
|
||||
static void Win32ProcessError(Process *process, String msg, String cmd) {
|
||||
Win32ReportError(msg, cmd);
|
||||
Win32CloseProcess(process);
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD written = 0;
|
||||
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
|
||||
|
||||
Assert(write_error == false);
|
||||
Assert(written == string.len);
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
CloseHandle(p->child_stdin_write);
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Process process = {};
|
||||
Win32Process *p = (Win32Process *)process.platform;
|
||||
|
||||
Scratch scratch;
|
||||
command_line = Format(scratch, "/C %S", command_line);
|
||||
|
||||
p->handle = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
SECURITY_ATTRIBUTES security_atrb = {};
|
||||
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security_atrb.bInheritHandle = TRUE;
|
||||
|
||||
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
STARTUPINFOW startup = {};
|
||||
startup.cb = sizeof(STARTUPINFO);
|
||||
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
startup.hStdInput = p->child_stdin_read;
|
||||
startup.hStdOutput = p->child_stdout_write;
|
||||
startup.hStdError = p->child_stdout_write;
|
||||
startup.wShowWindow = SW_HIDE;
|
||||
|
||||
String16 cwd = ToString16(scratch, working_dir);
|
||||
String16 cmd = ToString16(scratch, command_line);
|
||||
|
||||
char *env = NULL;
|
||||
if (enviroment.len) {
|
||||
Int size = GetSize(enviroment) + enviroment.len + 1;
|
||||
env = AllocArray(scratch, char, size);
|
||||
Int i = 0;
|
||||
For(enviroment) {
|
||||
MemoryCopy(env + i, it.data, it.len);
|
||||
i += it.len;
|
||||
|
||||
env[i++] = 0;
|
||||
}
|
||||
env[i++] = 0;
|
||||
}
|
||||
|
||||
DWORD dwCreationFlags = 0;
|
||||
BOOL bInheritHandles = TRUE;
|
||||
|
||||
PROCESS_INFORMATION info = {};
|
||||
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) {
|
||||
Win32ProcessError(&process, "failed to create process", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
// Close handles to the stdin and stdout pipes no longer needed by the child process.
|
||||
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
|
||||
CloseHandle(info.hThread);
|
||||
CloseHandle(p->child_stdin_read);
|
||||
CloseHandle(p->child_stdout_write);
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
p->handle = info.hProcess;
|
||||
process.is_valid = true;
|
||||
process.id = (int64_t)p->handle;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
if (process->is_valid == false) return false;
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
|
||||
if (get_exit_code_failed) {
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
process->exit_code = (int)exit_code;
|
||||
Win32CloseProcess(process);
|
||||
return false;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
|
||||
if (terminate_process_error) {
|
||||
Assert(0);
|
||||
}
|
||||
Win32CloseProcess(process);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
DWORD bytes_avail = 0;
|
||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||
if (peek_error) {
|
||||
return {};
|
||||
} else if (bytes_avail == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
|
||||
char *buffer = AllocArray(allocator, char, buffer_size);
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
||||
if (read_error) {
|
||||
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
||||
Dealloc(allocator, buffer);
|
||||
Win32CloseProcess(process);
|
||||
return {};
|
||||
}
|
||||
|
||||
String result = {buffer, bytes_read};
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif OS_WASM
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -1122,54 +720,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
|
||||
return it;
|
||||
}
|
||||
|
||||
API Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
return {};
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
return false;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
return {};
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
return;
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
API double GetTimeSeconds() {
|
||||
|
||||
Reference in New Issue
Block a user