359 lines
8.1 KiB
C
359 lines
8.1 KiB
C
|
|
#include <fx/collections/array.h>
|
||
|
|
#include <fx/diagnostics/process.h>
|
||
|
|
#include <fx/io/pipe.h>
|
||
|
|
#include <fx/io/stream.h>
|
||
|
|
#include <fx/iterator.h>
|
||
|
|
#include <fx/macros.h>
|
||
|
|
#include <fx/string.h>
|
||
|
|
#include <signal.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
FX_API fx_type_id fx_process_iterator_get_type();
|
||
|
|
|
||
|
|
FX_DECLARE_TYPE(fx_process_iterator);
|
||
|
|
|
||
|
|
FX_TYPE_CLASS_DECLARATION_BEGIN(fx_process_iterator)
|
||
|
|
FX_TYPE_CLASS_DECLARATION_END(fx_process_iterator)
|
||
|
|
|
||
|
|
struct fx_process_p {
|
||
|
|
fx_string *proc_exec_path;
|
||
|
|
fx_array *proc_args;
|
||
|
|
bool proc_redirect_stdin, proc_redirect_stdout, proc_redirect_stderr;
|
||
|
|
fx_stream *proc_stdin, *proc_stdout, *proc_stderr;
|
||
|
|
pid_t proc_id;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct fx_process_iterator_p {
|
||
|
|
};
|
||
|
|
|
||
|
|
/*** PRIVATE FUNCTIONS ********************************************************/
|
||
|
|
|
||
|
|
static fx_status process_start(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
fx_iostream *in_r = NULL, *in_w = NULL;
|
||
|
|
fx_iostream *out_r = NULL, *out_w = NULL;
|
||
|
|
fx_iostream *err_r = NULL, *err_w = NULL;
|
||
|
|
fx_status status = FX_SUCCESS;
|
||
|
|
|
||
|
|
if (proc->proc_redirect_stdin) {
|
||
|
|
status = fx_pipe_create(&in_r, &in_w);
|
||
|
|
if (!FX_OK(status)) {
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (proc->proc_redirect_stdout) {
|
||
|
|
status = fx_pipe_create(&out_r, &out_w);
|
||
|
|
if (!FX_OK(status)) {
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (proc->proc_redirect_stderr) {
|
||
|
|
status = fx_pipe_create(&err_r, &err_w);
|
||
|
|
if (!FX_OK(status)) {
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pid_t pid = fork();
|
||
|
|
|
||
|
|
if (pid < 0) {
|
||
|
|
status = FX_ERR_INVALID_ARGUMENT;
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pid > 0) {
|
||
|
|
proc->proc_id = pid;
|
||
|
|
proc->proc_stdin = fx_iostream_ref(in_w);
|
||
|
|
proc->proc_stdout = fx_iostream_ref(out_r);
|
||
|
|
proc->proc_stderr = fx_iostream_ref(err_r);
|
||
|
|
status = FX_SUCCESS;
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
|
||
|
|
int fd = -1;
|
||
|
|
if (proc->proc_redirect_stdin) {
|
||
|
|
fx_iostream_unref(in_w);
|
||
|
|
fd = fx_iostream_steal_os_handle(in_r);
|
||
|
|
fprintf(stderr, "dup2(%d, %d)\n", fd, 0);
|
||
|
|
int err = dup2(fd, 0);
|
||
|
|
close(fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (proc->proc_redirect_stdout) {
|
||
|
|
fx_iostream_unref(out_r);
|
||
|
|
fd = fx_iostream_steal_os_handle(out_w);
|
||
|
|
fprintf(stderr, "dup2(%d, %d)\n", fd, 1);
|
||
|
|
dup2(fd, 1);
|
||
|
|
close(fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (proc->proc_redirect_stderr) {
|
||
|
|
fx_iostream_unref(err_r);
|
||
|
|
fd = fx_iostream_steal_os_handle(err_w);
|
||
|
|
fprintf(stderr, "dup2(%d, %d)\n", fd, 2);
|
||
|
|
dup2(fd, 2);
|
||
|
|
close(fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *args[] = {
|
||
|
|
"cowsay",
|
||
|
|
NULL,
|
||
|
|
};
|
||
|
|
printf("exec '%s'\n", fx_string_get_cstr(proc->proc_exec_path));
|
||
|
|
execv(fx_string_get_cstr(proc->proc_exec_path), (char *const *)args);
|
||
|
|
exit(127);
|
||
|
|
|
||
|
|
cleanup:
|
||
|
|
fx_iostream_unref(in_r);
|
||
|
|
fx_iostream_unref(in_w);
|
||
|
|
fx_iostream_unref(out_r);
|
||
|
|
fx_iostream_unref(out_w);
|
||
|
|
fx_iostream_unref(err_r);
|
||
|
|
fx_iostream_unref(err_w);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
static fx_status process_wait(struct fx_process_p *proc, int *out_result)
|
||
|
|
{
|
||
|
|
if (proc->proc_id == 0) {
|
||
|
|
return FX_ERR_BAD_STATE;
|
||
|
|
}
|
||
|
|
|
||
|
|
int status = 0;
|
||
|
|
|
||
|
|
do {
|
||
|
|
int err = waitpid(proc->proc_id, &status, WUNTRACED);
|
||
|
|
if (err != 0) {
|
||
|
|
return FX_ERR_BAD_STATE;
|
||
|
|
}
|
||
|
|
} while (!WIFEXITED(status));
|
||
|
|
|
||
|
|
proc->proc_id = 0;
|
||
|
|
*out_result = WEXITSTATUS(status);
|
||
|
|
return FX_ERR_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
static fx_status process_kill(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
if (proc->proc_id == 0) {
|
||
|
|
return FX_ERR_BAD_STATE;
|
||
|
|
}
|
||
|
|
|
||
|
|
int err = kill(proc->proc_id, SIGTERM);
|
||
|
|
if (err != 0) {
|
||
|
|
return FX_ERR_BAD_STATE;
|
||
|
|
}
|
||
|
|
|
||
|
|
return FX_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
static fx_stream *process_get_stdin(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
return proc->proc_stdin;
|
||
|
|
}
|
||
|
|
|
||
|
|
static fx_stream *process_get_stdout(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
return proc->proc_stdout;
|
||
|
|
}
|
||
|
|
|
||
|
|
static fx_stream *process_get_stderr(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
return proc->proc_stderr;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void process_close_stdin(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
fx_iostream_unref(proc->proc_stdin);
|
||
|
|
proc->proc_stdin = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void process_close_stdout(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
fx_iostream_unref(proc->proc_stdout);
|
||
|
|
proc->proc_stdout = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void process_close_stderr(struct fx_process_p *proc)
|
||
|
|
{
|
||
|
|
fx_iostream_unref(proc->proc_stderr);
|
||
|
|
proc->proc_stderr = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*** PUBLIC FUNCTIONS *********************************************************/
|
||
|
|
|
||
|
|
fx_process *fx_process_create(const fx_process_start_info *info)
|
||
|
|
{
|
||
|
|
fx_process *out = fx_object_create(FX_DIAGNOSTICS_TYPE_PROCESS);
|
||
|
|
if (!out) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct fx_process_p *proc
|
||
|
|
= fx_object_get_private(out, FX_DIAGNOSTICS_TYPE_PROCESS);
|
||
|
|
|
||
|
|
proc->proc_exec_path = fx_string_create_from_cstr(info->proc_file_name);
|
||
|
|
if (!proc->proc_exec_path) {
|
||
|
|
fx_process_unref(out);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (info->proc_args_count > 0) {
|
||
|
|
proc->proc_args = fx_array_create();
|
||
|
|
if (!proc->proc_args) {
|
||
|
|
fx_process_unref(out);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t i = 0; i < info->proc_args_count; i++) {
|
||
|
|
fx_string *arg = fx_string_create_from_cstr(info->proc_args[i]);
|
||
|
|
if (!arg) {
|
||
|
|
fx_process_unref(out);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_array_push_back(proc->proc_args, FX_VALUE_OBJECT(arg));
|
||
|
|
fx_string_unref(arg);
|
||
|
|
}
|
||
|
|
|
||
|
|
proc->proc_redirect_stdin = info->proc_redirect_stdin;
|
||
|
|
proc->proc_redirect_stdout = info->proc_redirect_stdout;
|
||
|
|
proc->proc_redirect_stderr = info->proc_redirect_stderr;
|
||
|
|
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_status fx_process_start(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_start,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_status fx_process_wait(fx_process *proc, int *out_result)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_wait,
|
||
|
|
proc,
|
||
|
|
out_result);
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_status fx_process_kill(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_kill,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_stream *fx_process_get_stdin(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_get_stdin,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_stream *fx_process_get_stdout(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_get_stdout,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_stream *fx_process_get_stderr(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_get_stderr,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
void fx_process_close_stdin(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_V0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_close_stdin,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
void fx_process_close_stdout(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_V0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_close_stdout,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
void fx_process_close_stderr(fx_process *proc)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC_V0(
|
||
|
|
FX_DIAGNOSTICS_TYPE_PROCESS,
|
||
|
|
process_close_stderr,
|
||
|
|
proc);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*** VIRTUAL FUNCTIONS ********************************************************/
|
||
|
|
|
||
|
|
static void process_init(fx_object *obj, void *priv)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
static void process_fini(fx_object *obj, void *priv)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
/*** ITERATOR DEFINITION ******************************************************/
|
||
|
|
|
||
|
|
static enum fx_status process_iterator_move_next(const fx_iterator *obj)
|
||
|
|
{
|
||
|
|
return FX_ERR_NO_DATA;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const fx_value *process_iterator_get_value(const fx_iterator *obj)
|
||
|
|
{
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*** CLASS DEFINITION *********************************************************/
|
||
|
|
|
||
|
|
FX_TYPE_CLASS_BEGIN(fx_process)
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
|
||
|
|
FX_INTERFACE_ENTRY(to_string) = NULL;
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
|
||
|
|
FX_TYPE_CLASS_END(fx_process)
|
||
|
|
|
||
|
|
FX_TYPE_DEFINITION_BEGIN(fx_process)
|
||
|
|
FX_TYPE_ID(0x7334594f, 0xa1c3, 0x4715, 0xab69, 0x68a709e1b64b);
|
||
|
|
FX_TYPE_NAME("fx.diagnostics.process");
|
||
|
|
FX_TYPE_CLASS(fx_process_class);
|
||
|
|
FX_TYPE_INSTANCE_PRIVATE(struct fx_process_p);
|
||
|
|
FX_TYPE_INSTANCE_INIT(process_init);
|
||
|
|
FX_TYPE_INSTANCE_FINI(process_fini);
|
||
|
|
FX_TYPE_DEFINITION_END(fx_process)
|
||
|
|
|
||
|
|
FX_TYPE_CLASS_BEGIN(fx_process_iterator)
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
|
||
|
|
FX_INTERFACE_ENTRY(to_string) = NULL;
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
|
||
|
|
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterator, FX_TYPE_ITERATOR)
|
||
|
|
FX_INTERFACE_ENTRY(it_move_next) = process_iterator_move_next;
|
||
|
|
FX_INTERFACE_ENTRY(it_get_value) = process_iterator_get_value;
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_END(fx_iterator, FX_TYPE_ITERATOR)
|
||
|
|
FX_TYPE_CLASS_END(fx_process_iterator)
|
||
|
|
|
||
|
|
FX_TYPE_DEFINITION_BEGIN(fx_process_iterator)
|
||
|
|
FX_TYPE_ID(0x67eb13b6, 0x25d1, 0x424a, 0xb136, 0x871a07829089);
|
||
|
|
FX_TYPE_NAME("fx.diagnostics.process.iterator");
|
||
|
|
FX_TYPE_EXTENDS(FX_TYPE_ITERATOR);
|
||
|
|
FX_TYPE_CLASS(fx_process_iterator_class);
|
||
|
|
FX_TYPE_INSTANCE_PRIVATE(struct fx_process_iterator_p);
|
||
|
|
FX_TYPE_DEFINITION_END(fx_process_iterator)
|