summaryrefslogtreecommitdiff
path: root/src/lib/ecore/efl_exe.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/ecore/efl_exe.c')
-rw-r--r--src/lib/ecore/efl_exe.c844
1 files changed, 844 insertions, 0 deletions
diff --git a/src/lib/ecore/efl_exe.c b/src/lib/ecore/efl_exe.c
new file mode 100644
index 0000000000..7e1e3a242a
--- /dev/null
+++ b/src/lib/ecore/efl_exe.c
@@ -0,0 +1,844 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define EFL_IO_READER_PROTECTED 1
+#define EFL_IO_WRITER_PROTECTED 1
+#define EFL_IO_CLOSER_PROTECTED 1
+
+#include <Ecore.h>
+
+#include "ecore_private.h"
+
+#ifdef _WIN32
+#else
+# include <sys/time.h>
+# include <sys/resource.h>
+# include <stdlib.h>
+# include <stdio.h>
+# include <string.h>
+# include <errno.h>
+# include <sys/types.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <signal.h>
+# include <sys/socket.h>
+# ifdef HAVE_PRCTL
+# include <sys/prctl.h>
+# endif
+# ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+# endif
+# ifndef HAVE_CLEARENV
+extern char **environ;
+# endif
+#endif
+
+#define MY_CLASS EFL_EXE_CLASS
+
+typedef struct _Efl_Exe_Data Efl_Exe_Data;
+
+struct _Efl_Exe_Data
+{
+ int exit_signal;
+ Efl_Exe_Flags flags;
+#ifdef _WIN32
+ struct {
+ Eo *in_handler, *out_handler;
+ Eina_Bool can_read : 1;
+ Eina_Bool eos_read : 1;
+ Eina_Bool can_write : 1;
+ } fd;
+#else
+ Eina_Promise *promise;
+ Eo *exit_handler;
+ pid_t pid;
+ struct {
+ int in, out, exited_read, exited_write;
+ Eo *in_handler, *out_handler;
+ Eina_Bool can_read : 1;
+ Eina_Bool eos_read : 1;
+ Eina_Bool can_write : 1;
+ } fd;
+#endif
+ Eina_Bool exit_called : 1;
+ Eina_Bool run : 1;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef _WIN32
+#else
+static const signed char primap[EFL_TASK_PRIORITY_ULTRA + 1] =
+{
+ 10, // EFL_TASK_PRIORITY_NORMAL
+ 19, // EFL_TASK_PRIORITY_BACKGROUND
+ 15, // EFL_TASK_PRIORITY_LOW
+ 5, // EFL_TASK_PRIORITY_HIGH
+ 0 // EFL_TASK_PRIORITY_ULTRA
+};
+
+static void
+_close_fds(Efl_Exe_Data *pd)
+{
+ if (pd->fd.in >= 0) close(pd->fd.in);
+ if (pd->fd.out >= 0) close(pd->fd.out);
+ if (pd->fd.exited_read >= 0) close(pd->fd.exited_read);
+ if (pd->fd.exited_write >= 0) close(pd->fd.exited_write);
+ pd->fd.in = -1;
+ pd->fd.out = -1;
+ pd->fd.exited_read = -1;
+ pd->fd.exited_write = -1;
+}
+
+static void
+_exec(const char *cmd, Efl_Exe_Flags flags)
+{
+ char use_sh = 1, *buf = NULL, **args = NULL;
+
+ // Try to avoid wrapping the exe call with /bin/sh -c.
+ // We conservatively search for certain shell meta characters,
+ // If we don't find them, we can call the exe directly.
+ if (!strpbrk(cmd, "|&;<>()$\\\"'*?#"))
+ {
+ char *token, pre_command = 1;
+ int num_tokens = 0, len;
+
+ len = strlen(cmd);
+ buf = alloca(len + 1);
+ strcpy(buf, cmd);
+ buf[len] = 0;
+
+ token = strtok(buf, " \t\n\v");
+ while (token)
+ {
+ if (token[0] == '~') break;
+ if (pre_command)
+ {
+ if (token[0] == '[') break;
+ if (strchr(token, '=')) break;
+ else pre_command = 0;
+ }
+ num_tokens++;
+ token = strtok(NULL, " \t\n\v");
+ }
+ if ((!token) && (num_tokens))
+ {
+ int i = 0;
+
+ len = strlen(cmd);
+ buf = alloca(len + 1);
+ strcpy(buf, cmd);
+ buf[len] = 0;
+
+ token = strtok(buf, " \t\n\v");
+ use_sh = 0;
+ args = alloca((num_tokens + 1) * sizeof(char *));
+ for (i = 0; i < num_tokens; i++)
+ {
+ if (token) args[i] = token;
+ token = strtok(NULL, " \t\n\v");
+ }
+ args[num_tokens] = NULL;
+ }
+ }
+# ifdef HAVE_PRCTL
+ if (flags & EFL_EXE_FLAGS_EXIT_WITH_PARENT)
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+# endif
+
+ if (flags & EFL_EXE_FLAGS_GROUP_LEADER) setsid();
+ if (use_sh) // We have to use a shell to run this.
+ {
+ errno = 0;
+ execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
+ }
+ else
+ { // We can run this directly.
+ if (!args)
+ {
+ ERR("arg[0] is NULL!");
+ return;
+ }
+ errno = 0;
+ if (args[0]) execvp(args[0], args);
+ }
+}
+
+static Eina_Bool
+_foreach_env(const Eina_Hash *hash EINA_UNUSED, const void *key, void *data, void *fdata EINA_UNUSED)
+{
+ int keylen;
+ char *buf, *s;
+
+ if (!data) return EINA_TRUE;
+ keylen = strlen(key);
+ buf = alloca(keylen + 1 + strlen(data) + 1);
+ strcpy(buf, key);
+ buf[keylen] = '=';
+ strcpy(buf + keylen + 1, data);
+ if ((s = strdup(buf))) putenv(s);
+ return EINA_TRUE;
+}
+
+static void
+_exe_exit_eval(Eo *obj, Efl_Exe_Data *pd)
+{
+ if ((pd->fd.out == -1) && /*(pd->fd.in == -1) &&*/
+ (pd->fd.exited_read == -1) && (!pd->exit_called))
+ {
+ pd->exit_called = EINA_TRUE;
+ if (pd->promise)
+ {
+ Eina_Promise *p = pd->promise;
+ int exit_code = efl_task_exit_code_get(obj);
+ pd->promise = NULL;
+ if ((exit_code != 0) && (!(efl_task_flags_get(obj) &
+ EFL_TASK_FLAGS_NO_EXIT_CODE_ERROR)))
+ {
+ Eina_Error err = exit_code + 1000000;
+ // Code Meaning Example Comments
+ // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ // 1 Catchall for general errors let "var1 = 1/0" Miscellaneous errors, such as "divide by zero" and other impermissible operations
+ // 2 Misuse of shell builtins empty_function() {} Missing keyword or command, or permission problem (and diff return code on a failed binary file comparison).
+ // 126 Command invoked cannot execute /dev/null Permission problem or command is not an executable
+ // 127 "command not found" illegal_command Possible problem with $PATH or a typo
+ // 128 Invalid argument to exit exit 3.14159 exit takes only integer args in the range 0 - 255 (see first footnote)
+ // 128+n Fatal error signal "n" kill -9 $PPID $? returns 137 (128 + 9)
+ // 130 Script terminated by Control-C Ctl-C Control-C is fatal error signal 2, (130 = 128 + 2, see above)
+ // 255* Exit status out of range exit -1 exit takes only integer args in the range 0 - 255
+ //
+ // According to the above table, exit codes 1 - 2,
+ // 126 - 165, and 255 [1] have special meanings, and
+ // should therefore be avoided for user-specified exit
+ // parameters. Ending a script with exit 127 would
+ // certainly cause confusion when troubleshooting (is
+ // the error code a "command not found" or a user-defined
+ // one?). However, many scripts use an exit 1 as a general
+ // bailout-upon-error. Since exit code 1 signifies so many
+ // possible errors, it is not particularly useful in
+ // debugging.
+ if (exit_code == 1 ) err = EBADF;
+ else if (exit_code == 2 ) err = EDOM;
+ else if (exit_code == 126) err = ENOEXEC;
+ else if (exit_code == 127) err = ENOENT;
+ else if (exit_code == 128) err = EINVAL;
+ else if (exit_code == 129) err = EFAULT;
+ else if (exit_code == 130) err = EINTR;
+ else if ((exit_code >= 131) && (exit_code <= 165)) err = EFAULT;
+ eina_promise_reject(p, err);
+ }
+ else eina_promise_resolve(p, eina_value_int_init(exit_code));
+ }
+ }
+}
+
+static void
+_cb_exe_exit_read(void *data, const Efl_Event *event EINA_UNUSED)
+{
+ Eo *obj = data;
+ Efl_Exe_Data *pd = efl_data_scope_get(obj, MY_CLASS);
+ Ecore_Signal_Pid_Info pinfo;
+
+ if (!pd) return;
+ if (read(pd->fd.exited_read, &pinfo, sizeof(Ecore_Signal_Pid_Info)) ==
+ sizeof(Ecore_Signal_Pid_Info))
+ {
+ Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
+ if (td)
+ {
+ td->exited = EINA_TRUE;
+ td->exit_code = pinfo.exit_code;
+ pd->exit_signal = pinfo.exit_signal;
+ }
+ }
+ // we don't need this fd and handler anymore now we code exit status
+ close(pd->fd.exited_read);
+ pd->fd.exited_read = -1;
+ efl_del(pd->exit_handler);
+ pd->exit_handler = NULL;
+ _exe_exit_eval(obj, pd);
+ // XXX: autodel of object here is the sensible easy thing to do in C
+ // because then you can just run exe's and not have to listen to them exit
+ // and do your own del every time - they will then not leak and just
+ // self-cleanup without needing a del of the obj on run. but other
+ // languages don't like this, so if you dont care to listen to end/death
+ // and then del/unref the obj there... always del/unref it immediately.
+}
+
+static void
+_cb_exe_out(void *data, const Efl_Event *event EINA_UNUSED)
+{
+ Eo *obj = data;
+ efl_io_reader_can_read_set(obj, EINA_TRUE);
+}
+
+static void
+_cb_exe_in(void *data, const Efl_Event *event EINA_UNUSED)
+{
+ Eo *obj = data;
+ efl_io_writer_can_write_set(obj, EINA_TRUE);
+}
+
+static void
+_run_cancel_cb(void *data, const Eina_Promise *dead_promise EINA_UNUSED)
+{
+ Eo *obj = data;
+ Efl_Exe_Data *pd = efl_data_scope_get(obj, MY_CLASS);
+ pd->promise = NULL;
+ efl_task_end(obj);
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+
+EOLIAN static void
+_efl_exe_signal(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd, Efl_Exe_Signal sig)
+{
+#ifdef _WIN32
+#else
+ int s = 0;
+ if (pd->pid == -1) return;
+
+ switch (sig)
+ {
+ case EFL_EXE_SIGNAL_INT: s = SIGINT; break;
+ case EFL_EXE_SIGNAL_QUIT: s = SIGQUIT; break;
+ case EFL_EXE_SIGNAL_TERM: s = SIGTERM; break;
+ case EFL_EXE_SIGNAL_KILL: s = SIGKILL; break;
+ case EFL_EXE_SIGNAL_CONT: s = SIGCONT; break;
+ case EFL_EXE_SIGNAL_STOP: s = SIGSTOP; break;
+ case EFL_EXE_SIGNAL_HUP: s = SIGHUP; break;
+ case EFL_EXE_SIGNAL_USR1: s = SIGUSR1; break;
+ case EFL_EXE_SIGNAL_USR2: s = SIGUSR2; break;
+ default: return;
+ }
+ kill(pd->pid, s);
+#endif
+}
+
+EOLIAN static void
+_efl_exe_exe_flags_set(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd, Efl_Exe_Flags flags)
+{
+ pd->flags = flags;
+}
+
+EOLIAN static Efl_Exe_Flags
+_efl_exe_exe_flags_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+ return pd->flags;
+}
+
+EOLIAN static void
+_efl_exe_efl_task_priority_set(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd, Efl_Task_Priority priority)
+{
+ efl_task_priority_set(efl_super(obj, MY_CLASS), priority);
+#ifdef _WIN32
+#else
+ int p = 0;
+
+ if (pd->pid != -1)
+ {
+ if ((priority >= EFL_TASK_PRIORITY_NORMAL) &&
+ (priority <= EFL_TASK_PRIORITY_ULTRA))
+ p = primap[priority];
+ }
+ setpriority(PRIO_PROCESS, pd->pid, p);
+#endif
+}
+
+EOLIAN static Efl_Task_Priority
+_efl_exe_efl_task_priority_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+ Efl_Task_Priority pri = EFL_TASK_PRIORITY_NORMAL;
+
+#ifdef _WIN32
+#else
+ int p, i, dist = 0x7fffffff, d;
+
+ if (pd->pid == -1)
+ return efl_task_priority_get(efl_super(obj, MY_CLASS));
+ // p is -20 -> 19
+ errno = 0;
+ p = getpriority(PRIO_PROCESS, pd->pid);
+ if (errno != 0)
+ return efl_task_priority_get(efl_super(obj, MY_CLASS));
+
+ // find the closest matching priority in primap
+ for (i = EFL_TASK_PRIORITY_NORMAL; i <= EFL_TASK_PRIORITY_ULTRA; i++)
+ {
+ d = primap[i] - p;
+ if (d < 0) d = -d;
+ if (d < dist)
+ {
+ pri = i;
+ dist = d;
+ }
+ }
+
+ Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
+ if (td) td->priority = pri;
+#endif
+ return pri;
+}
+
+EOLIAN static Eina_Future *
+_efl_exe_efl_task_run(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+#ifdef _WIN32
+ return EINA_FALSE;
+#else
+ Eo *loop;
+ Efl_Task_Data *tdl, *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
+ const char *cmd;
+ int devnull;
+ int pipe_stdin[2];
+ int pipe_stdout[2];
+ int pipe_exited[2];
+ int ret;
+
+ if (pd->run) return NULL;
+ if (pd->pid != -1) return NULL;
+ if (!td) return NULL;
+
+ // get a cmdline to run
+ cmd = efl_task_command_get(obj);
+ if (!cmd) return NULL;
+
+ ret = pipe(pipe_exited);
+ if (EINA_UNLIKELY(ret != 0))
+ {
+ const int error = errno;
+ ERR("pipe() failed: %s", strerror(error));
+ return NULL;
+ }
+
+ pd->fd.exited_read = pipe_exited[0];
+ eina_file_close_on_exec(pd->fd.exited_write, EINA_TRUE);
+ pd->fd.exited_write = pipe_exited[1];
+ eina_file_close_on_exec(pd->fd.exited_read, EINA_TRUE);
+
+ if (td->flags & EFL_TASK_FLAGS_USE_STDIN)
+ {
+ ret = pipe(pipe_stdin);
+ if (EINA_UNLIKELY(ret != 0))
+ {
+ const int error = errno;
+ ERR("pipe() failed: %s", strerror(error));
+ return NULL;
+ }
+ pd->fd.in = pipe_stdin[1];
+ fcntl(pd->fd.in, F_SETFL, O_NONBLOCK);
+ eina_file_close_on_exec(pd->fd.in, EINA_TRUE);
+ pd->fd.in_handler =
+ efl_add(EFL_LOOP_HANDLER_CLASS, obj,
+ efl_loop_handler_fd_set(efl_added, pd->fd.in),
+ efl_event_callback_add
+ (efl_added, EFL_LOOP_HANDLER_EVENT_WRITE, _cb_exe_in, obj));
+ }
+ if (td->flags & EFL_TASK_FLAGS_USE_STDOUT)
+ {
+ ret = pipe(pipe_stdout);
+ if (EINA_UNLIKELY(ret != 0))
+ {
+ const int error = errno;
+ ERR("pipe() failed: %s", strerror(error));
+ return NULL;
+ }
+ pd->fd.out = pipe_stdout[0];
+ fcntl(pd->fd.out, F_SETFL, O_NONBLOCK);
+ eina_file_close_on_exec(pd->fd.out, EINA_TRUE);
+ pd->fd.out_handler =
+ efl_add(EFL_LOOP_HANDLER_CLASS, obj,
+ efl_loop_handler_fd_set(efl_added, pd->fd.out),
+ efl_event_callback_add
+ (efl_added, EFL_LOOP_HANDLER_EVENT_READ, _cb_exe_out, obj),
+ efl_loop_handler_active_set
+ (efl_added, EFL_LOOP_HANDLER_FLAGS_READ));
+ }
+
+ _ecore_signal_pid_lock();
+ pd->pid = fork();
+ if (pd->pid != 0)
+ {
+ // parent process is here inside this if block
+ if (td->flags & EFL_TASK_FLAGS_USE_STDIN) close(pipe_stdin[0]);
+ if (td->flags & EFL_TASK_FLAGS_USE_STDOUT) close(pipe_stdout[1]);
+ // fork failed... close up and clean and release locks
+ if (pd->pid == -1)
+ {
+ _close_fds(pd);
+ _ecore_signal_pid_unlock();
+ return NULL;
+ }
+ // register this pid in the core sigchild/pid exit code watcher
+ _ecore_signal_pid_register(pd->pid, pd->fd.exited_write);
+ pd->exit_handler =
+ efl_add(EFL_LOOP_HANDLER_CLASS, obj,
+ efl_loop_handler_fd_set(efl_added, pd->fd.exited_read),
+ efl_event_callback_add(efl_added,
+ EFL_LOOP_HANDLER_EVENT_READ,
+ _cb_exe_exit_read, obj),
+ efl_loop_handler_active_set(efl_added,
+ EFL_LOOP_HANDLER_FLAGS_READ));
+ _ecore_signal_pid_unlock();
+ pd->run = EINA_TRUE;
+ pd->promise = efl_loop_promise_new(obj, _run_cancel_cb, obj);
+ Eina_Future *f = eina_future_new(pd->promise);
+ return efl_future_Eina_FutureXXX_then(obj, f);
+ }
+ // this code is in the child here, and is temporary setup until we
+ // exec() the child to replace everything.
+
+ if (td->flags & EFL_TASK_FLAGS_USE_STDIN) close(pipe_stdin[1]);
+ if (td->flags & EFL_TASK_FLAGS_USE_STDOUT) close(pipe_stdout[0]);
+ // set priority of self
+ if ((td->priority >= EFL_TASK_PRIORITY_NORMAL) &&
+ (td->priority <= EFL_TASK_PRIORITY_ULTRA))
+ setpriority(PRIO_PROCESS, 0, primap[td->priority]);
+
+ // if we want to hide or use any of the stdio, close the fd's
+ if ((td->flags & EFL_TASK_FLAGS_USE_STDIN) ||
+ (pd->flags & EFL_EXE_FLAGS_HIDE_IO))
+ close(STDIN_FILENO);
+ if ((td->flags & EFL_TASK_FLAGS_USE_STDOUT) ||
+ (pd->flags & EFL_EXE_FLAGS_HIDE_IO))
+ close(STDOUT_FILENO);
+ if ((pd->flags & EFL_EXE_FLAGS_HIDE_IO))
+ close(STDERR_FILENO);
+
+ if (!(td->flags & EFL_TASK_FLAGS_USE_STDIN) &&
+ (pd->flags & EFL_EXE_FLAGS_HIDE_IO))
+ {
+ // hide stdin
+ devnull = open("/dev/null", O_RDONLY);
+ dup2(devnull, STDIN_FILENO);
+ close(devnull);
+ }
+ else if ((td->flags & EFL_TASK_FLAGS_USE_STDIN))
+ {
+ // hook up stdin to the pipe going to the parent
+ dup2(pipe_stdin[0], STDIN_FILENO);
+ close(pipe_stdin[0]);
+ }
+
+ if (!(td->flags & EFL_TASK_FLAGS_USE_STDOUT) &&
+ (pd->flags & EFL_EXE_FLAGS_HIDE_IO))
+ {
+ // hide stdout
+ devnull = open("/dev/null", O_WRONLY);
+ dup2(devnull, STDOUT_FILENO);
+ close(devnull);
+ }
+ else if ((td->flags & EFL_TASK_FLAGS_USE_STDOUT))
+ {
+ // hook up stdout to the pipe going to the parent
+ dup2(pipe_stdout[1], STDOUT_FILENO);
+ close(pipe_stdout[1]);
+ }
+
+ if ((pd->flags & EFL_EXE_FLAGS_HIDE_IO))
+ {
+ // hide stderr
+ devnull = open("/dev/null", O_WRONLY);
+ dup2(devnull, STDERR_FILENO);
+ close(devnull);
+ }
+
+ if (!(loop = efl_provider_find(obj, EFL_LOOP_CLASS))) exit(1);
+
+ if (!(tdl = efl_data_scope_get(loop, EFL_TASK_CLASS))) exit(1);
+
+ // clear systemd notify socket... only relevant for systemd world,
+ // otherwise shouldn't be trouble
+ putenv("NOTIFY_SOCKET=");
+ // force the env hash to update from env vars
+ efl_task_env_get(loop, "HOME");
+
+ // actually setenv the env hash (clear what was there before so it is
+ // the only env there)
+#ifdef HAVE_CLEARENV
+ clearenv();
+#else
+ environ = NULL;
+#endif
+ eina_hash_foreach(td->env, _foreach_env, NULL);
+
+ // actually execute!
+ _exec(cmd, pd->flags);
+ // we couldn't exec... uh oh. HAAAAAAAALP!
+ if ((errno == EACCES) || (errno == EINVAL) || (errno == ELOOP) ||
+ (errno == ENOEXEC) || (errno == ENOMEM))
+ exit(126);
+ exit(127);
+ return NULL;
+#endif
+}
+
+EOLIAN static void
+_efl_exe_efl_task_end(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+#ifdef _WIN32
+#else
+ if (pd->pid == -1) return;
+ kill(pd->pid, SIGINT);
+#endif
+}
+
+EOLIAN static int
+_efl_exe_exit_signal_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+ return pd->exit_signal;
+}
+
+EOLIAN static Efl_Object *
+_efl_exe_efl_object_constructor(Eo *obj, Efl_Exe_Data *pd)
+{
+ obj = efl_constructor(efl_super(obj, MY_CLASS));
+#ifdef _WIN32
+#else
+ pd->pid = -1;
+ pd->fd.in = -1;
+ pd->fd.out = -1;
+ pd->fd.exited_read = -1;
+#endif
+ pd->fd.can_write = EINA_TRUE;
+ pd->flags = EFL_EXE_FLAGS_EXIT_WITH_PARENT;
+ pd->exit_signal = -1;
+ return obj;
+}
+
+EOLIAN static void
+_efl_exe_efl_object_destructor(Eo *obj, Efl_Exe_Data *pd)
+{
+#ifdef _WIN32
+#else
+ if (pd->promise)
+ ERR("Exe being destroyed while child has not exited yet.");
+ if (pd->fd.exited_read >= 0)
+ {
+ _ecore_signal_pid_lock();
+ _ecore_signal_pid_unregister(pd->pid, pd->fd.exited_read);
+ _ecore_signal_pid_unlock();
+ close(pd->fd.exited_read);
+ pd->fd.exited_read = -1;
+ efl_del(pd->exit_handler);
+ pd->exit_handler = NULL;
+ }
+ if (pd->fd.in_handler) efl_del(pd->fd.in_handler);
+ if (pd->fd.out_handler) efl_del(pd->fd.out_handler);
+ pd->fd.in_handler = NULL;
+ pd->fd.out_handler = NULL;
+ _close_fds(pd);
+#endif
+ efl_destructor(efl_super(obj, MY_CLASS));
+}
+
+EOLIAN static Eina_Error
+_efl_exe_efl_io_closer_close(Eo *obj, Efl_Exe_Data *pd)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(obj), EBADF);
+ efl_io_writer_can_write_set(obj, EINA_FALSE);
+ efl_io_reader_can_read_set(obj, EINA_FALSE);
+ efl_io_reader_eos_set(obj, EINA_TRUE);
+#ifdef _WIN32
+#else
+ if (pd->fd.in >= 0) close(pd->fd.in);
+ if (pd->fd.out >= 0) close(pd->fd.out);
+ if (pd->fd.exited_read >= 0) close(pd->fd.exited_read);
+ if (pd->fd.in_handler) efl_del(pd->fd.in_handler);
+ if (pd->fd.out_handler) efl_del(pd->fd.out_handler);
+ pd->fd.in = -1;
+ pd->fd.out = -1;
+ pd->fd.exited_read = -1;
+ pd->fd.in_handler = NULL;
+ pd->fd.out_handler = NULL;
+#endif
+ return 0;
+}
+
+EOLIAN static Eina_Bool
+_efl_exe_efl_io_closer_closed_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+#ifdef _WIN32
+ return EINA_FALSE;
+#else
+ if ((pd->fd.in == -1) && (pd->fd.out == -1)) return EINA_TRUE;
+#endif
+ return EINA_FALSE;
+}
+
+EOLIAN static Eina_Error
+_efl_exe_efl_io_reader_read(Eo *obj, Efl_Exe_Data *pd, Eina_Rw_Slice *rw_slice)
+{
+#ifdef _WIN32
+ return EINVAL;
+#else
+ ssize_t r;
+
+ errno = 0;
+ if (pd->fd.out == -1) goto err;
+
+ do
+ {
+ errno = 0;
+ r = read(pd->fd.out, rw_slice->mem, rw_slice->len);
+ if (r == -1)
+ {
+ if (errno == EINTR) continue;
+ goto err;
+ }
+ }
+ while (r == -1);
+
+ rw_slice->len = r;
+ if (r == 0)
+ {
+ efl_io_reader_can_read_set(obj, EINA_FALSE);
+ efl_io_reader_eos_set(obj, EINA_TRUE);
+ close(pd->fd.out);
+ pd->fd.out = -1;
+ efl_del(pd->fd.out_handler);
+ pd->fd.out_handler = NULL;
+ _exe_exit_eval(obj, pd);
+ return EPIPE;
+ }
+ return 0;
+err:
+ if ((pd->fd.out != -1) && (errno != EAGAIN))
+ {
+ close(pd->fd.out);
+ pd->fd.out = -1;
+ efl_del(pd->fd.out_handler);
+ pd->fd.out_handler = NULL;
+ }
+ rw_slice->len = 0;
+ rw_slice->mem = NULL;
+ efl_io_reader_can_read_set(obj, EINA_FALSE);
+ _exe_exit_eval(obj, pd);
+ return EINVAL;
+#endif
+}
+
+EOLIAN static void
+_efl_exe_efl_io_reader_can_read_set(Eo *obj, Efl_Exe_Data *pd, Eina_Bool can_read)
+{
+ Eina_Bool old = efl_io_reader_can_read_get(obj);
+ if (old == can_read) return;
+ pd->fd.can_read = can_read;
+ if (!pd->fd.out_handler) return;
+ if (can_read)
+ efl_loop_handler_active_set(pd->fd.out_handler, 0);
+ else
+ efl_loop_handler_active_set(pd->fd.out_handler,
+ EFL_LOOP_HANDLER_FLAGS_READ);
+ efl_event_callback_call(obj, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_exe_efl_io_reader_can_read_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+ return pd->fd.can_read;
+}
+
+EOLIAN static void
+_efl_exe_efl_io_reader_eos_set(Eo *obj, Efl_Exe_Data *pd, Eina_Bool is_eos)
+{
+ Eina_Bool old = efl_io_reader_eos_get(obj);
+ if (old == is_eos) return;
+
+ pd->fd.eos_read = is_eos;
+ if (!is_eos) return;
+ if (pd->fd.out_handler)
+ efl_loop_handler_active_set(pd->fd.out_handler, 0);
+ efl_event_callback_call(obj, EFL_IO_READER_EVENT_EOS, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_exe_efl_io_reader_eos_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+ return pd->fd.eos_read;
+}
+
+EOLIAN static Eina_Error
+_efl_exe_efl_io_writer_write(Eo *obj, Efl_Exe_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
+{
+#ifdef _WIN32
+ return EINVAL;
+#else
+ ssize_t r;
+
+ errno = 0;
+ if (pd->fd.in == -1) goto err;
+
+ do
+ {
+ errno = 0;
+ r = write(pd->fd.in, slice->mem, slice->len);
+ if (r == -1)
+ {
+ if (errno == EINTR) continue;
+ goto err;
+ }
+ }
+ while (r == -1);
+
+ if (remaining)
+ {
+ remaining->len = slice->len - r;
+ remaining->bytes = slice->bytes + r;
+ }
+ slice->len = r;
+
+ if ((slice) && (slice->len > 0))
+ efl_io_writer_can_write_set(obj, EINA_FALSE);
+ if (r == 0)
+ {
+ close(pd->fd.in);
+ pd->fd.in = -1;
+ efl_del(pd->fd.in_handler);
+ pd->fd.in_handler = NULL;
+ _exe_exit_eval(obj, pd);
+ return EPIPE;
+ }
+ return 0;
+err:
+ if ((pd->fd.in != -1) && (errno != EAGAIN))
+ {
+ close(pd->fd.in);
+ pd->fd.in = -1;
+ efl_del(pd->fd.in_handler);
+ pd->fd.in_handler = NULL;
+ }
+ if (remaining) *remaining = *slice;
+ slice->len = 0;
+ slice->mem = NULL;
+ efl_io_writer_can_write_set(obj, EINA_FALSE);
+ _exe_exit_eval(obj, pd);
+ return EINVAL;
+#endif
+}
+
+EOLIAN static void
+_efl_exe_efl_io_writer_can_write_set(Eo *obj, Efl_Exe_Data *pd, Eina_Bool can_write)
+{
+ Eina_Bool old = efl_io_writer_can_write_get(obj);
+ if (old == can_write) return;
+ pd->fd.can_write = can_write;
+ if (can_write)
+ efl_loop_handler_active_set(pd->fd.in_handler, 0);
+ else
+ efl_loop_handler_active_set(pd->fd.in_handler,
+ EFL_LOOP_HANDLER_FLAGS_WRITE);
+ efl_event_callback_call(obj, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_exe_efl_io_writer_can_write_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
+{
+ return pd->fd.can_write;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "efl_exe.eo.c"