forked from enlightenment/efl
1129 lines
34 KiB
C
1129 lines
34 KiB
C
#include <errno.h>
|
|
#include <sys/wait.h>
|
|
#include "ecore_private.h"
|
|
#include "Ecore.h"
|
|
|
|
#ifndef WIN32
|
|
|
|
struct _ecore_exe_dead_exe
|
|
{
|
|
pid_t pid;
|
|
char *cmd;
|
|
};
|
|
|
|
static void _ecore_exe_exec_it(const char *exe_cmd);
|
|
static int _ecore_exe_data_read_handler(void *data, Ecore_Fd_Handler *fd_handler);
|
|
static int _ecore_exe_data_write_handler(void *data, Ecore_Fd_Handler *fd_handler);
|
|
static void _ecore_exe_flush(Ecore_Exe *exe);
|
|
static void _ecore_exe_event_exe_data_free(void *data __UNUSED__, void *ev);
|
|
static int _ecore_exe_is_it_alive(pid_t pid);
|
|
static int _ecore_exe_make_sure_its_dead(void *data);
|
|
static int _ecore_exe_make_sure_its_really_dead(void *data);
|
|
|
|
static Ecore_Exe *exes = NULL;
|
|
static char *shell = NULL;
|
|
|
|
|
|
/* FIXME: This errno checking stuff should be put elsewhere for everybody to use.
|
|
* For now it lives here though, just to make testing easier.
|
|
*/
|
|
int _ecore_exe_check_errno(int result, char *file, int line);
|
|
#define E_IF_NO_ERRNO(result, foo, ok) \
|
|
while (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)) == -1) sleep(1); \
|
|
if (ok)
|
|
|
|
#define E_NO_ERRNO(result, foo, ok) \
|
|
while (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)) == -1) sleep(1)
|
|
|
|
#define E_IF_NO_ERRNO_NOLOOP(result, foo, ok) \
|
|
if (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)))
|
|
|
|
int _ecore_exe_check_errno(int result, char *file, int line)
|
|
{
|
|
int saved_errno = errno;
|
|
|
|
if (result == -1)
|
|
{
|
|
perror("*** errno reports ");
|
|
/* What is currently supported -
|
|
*
|
|
* pipe
|
|
* EFAULT Argument is not valid.
|
|
* EMFILE Too many file descriptors used by process.
|
|
* ENFILE Too many open files by system.
|
|
* read
|
|
* EAGAIN No data now, try again.
|
|
* EBADF This is not an fd that can be read.
|
|
* EFAULT This is not a valid buffer.
|
|
* EINTR Interupted by signal, try again.
|
|
* EINVAL This is not an fd that can be read.
|
|
* EIO I/O error.
|
|
* EISDIR This is a directory, and cannot be read.
|
|
* others Depending on what sort of thing we are reading from.
|
|
* close
|
|
* EBADF This is not an fd that can be closed.
|
|
* EINTR Interupted by signal, try again.
|
|
* EIO I/O error.
|
|
* dup2
|
|
* EBADF This is not an fd that can be dup2'ed.
|
|
* EBUSY Race condition between open() and dup()
|
|
* EINTR Interupted by signal, try again.
|
|
* EMFILE Too many file descriptors used by process.
|
|
* fcntl
|
|
* EACCES, EAGAIN Locked or mapped by something else, try again later.
|
|
* EBADF This is not an fd that can be fcntl'ed.
|
|
* EDEADLK This will cause a deadlock.
|
|
* EFAULT This is not a valid lock.
|
|
* EINTR Interupted by signal, try again.
|
|
* EINVAL This is not a valid arg.
|
|
* EMFILE Too many file descriptors used by process.
|
|
* ENOLCK Problem getting a lock.
|
|
* EPERM Not allowed to do that.
|
|
* fsync
|
|
* EBADF This is not an fd that is open for writing.
|
|
* EINVAL, EROFS This is not an fd that can be fsynced.
|
|
* EIO I/O error.
|
|
*
|
|
* How to use it -
|
|
* int ok = 0;
|
|
* int result;
|
|
*
|
|
* E_IF_NO_ERRNO(result, foo(bar), ok)
|
|
* {
|
|
* E_IF_NO_ERRNO_NOLOOP(result, foo(bar), ok)
|
|
* {
|
|
* }
|
|
* }
|
|
*
|
|
* if (!ok)
|
|
* {
|
|
* // Something failed, cleanup.
|
|
* }
|
|
*/
|
|
switch (saved_errno)
|
|
{
|
|
case EACCES :
|
|
case EAGAIN :
|
|
case EINTR :
|
|
{ /* Not now, try later. */
|
|
fprintf(stderr, "*** Must try again in %s @%u.\n", file, line);
|
|
result = -1;
|
|
break;
|
|
}
|
|
|
|
case EMFILE :
|
|
case ENFILE :
|
|
case ENOLCK :
|
|
{ /* Low on resources. */
|
|
fprintf(stderr, "*** Low on resources in %s @%u.\n", file, line);
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
case EIO :
|
|
{ /* I/O error. */
|
|
fprintf(stderr, "*** I/O error in %s @%u.\n", file, line);
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
case EFAULT :
|
|
case EBADF :
|
|
case EINVAL :
|
|
case EROFS :
|
|
case EISDIR :
|
|
case EDEADLK :
|
|
case EPERM :
|
|
case EBUSY :
|
|
{ /* Programmer fucked up. */
|
|
fprintf(stderr,
|
|
"*** NAUGHTY PROGRAMMER!!!\n"
|
|
"*** SPANK SPANK SPANK!!!\n"
|
|
"*** Now go fix your code in %s @%u. Tut tut tut!\n"
|
|
"\n", file, line);
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
default :
|
|
{ /* Unsupported errno code, please add this one. */
|
|
fprintf(stderr,
|
|
"*** NAUGHTY PROGRAMMER!!!\n"
|
|
"*** SPANK SPANK SPANK!!!\n"
|
|
"*** Unsupported errno code %d, please add this one.\n"
|
|
"*** Now go fix your code in %s @%u, from %s @%u. Tut tut tut!\n"
|
|
"\n", saved_errno, __FILE__, __LINE__, file, line);
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else /* Everything is fine. */
|
|
result = 1;
|
|
|
|
errno = saved_errno;
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @defgroup Ecore_Exe_Basic_Group Process Spawning Functions
|
|
*
|
|
* Functions that deal with spawned processes.
|
|
*/
|
|
|
|
/**
|
|
* Spawns a child process.
|
|
*
|
|
* This function forks and runs the given command using @c /bin/sh.
|
|
*
|
|
* Note that the process handle is only valid until a child process
|
|
* terminated event is received. After all handlers for the child process
|
|
* terminated event have been called, the handle will be freed by Ecore.
|
|
*
|
|
* @param exe_cmd The command to run with @c /bin/sh.
|
|
* @param data Data to attach to the returned process handle.
|
|
* @return A process handle to the spawned process.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
Ecore_Exe *
|
|
ecore_exe_run(const char *exe_cmd, const void *data)
|
|
{
|
|
Ecore_Exe *exe;
|
|
pid_t pid;
|
|
|
|
if (!exe_cmd) return NULL;
|
|
pid = fork();
|
|
if (pid)
|
|
{
|
|
exe = calloc(1, sizeof(Ecore_Exe));
|
|
if (!exe)
|
|
{
|
|
kill(pid, SIGKILL);
|
|
return NULL;
|
|
}
|
|
ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE);
|
|
exe->pid = pid;
|
|
exe->data = (void *)data;
|
|
exe->cmd = strdup(exe_cmd);
|
|
exes = _ecore_list2_append(exes, exe);
|
|
return exe;
|
|
}
|
|
_ecore_exe_exec_it(exe_cmd);
|
|
exit(127);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Spawns a child process with its stdin/out available for communication.
|
|
*
|
|
* This function does the same thing as ecore_exe_run(), but also makes the
|
|
* standard in and/or out from the child process available for reading or
|
|
* writing. To write use ecore_exe_send(). To read listen to
|
|
* ECORE_EVENT_EXE_DATA events (set up a handler). Ecore may buffer read
|
|
* data until a newline character if asked for with the @p flags. All
|
|
* data will be included in the events (newlines will be replaced with
|
|
* NULLS if line buffered). ECORE_EVENT_EXE_DATA events will only happen
|
|
* if the process is run with ECORE_EXE_PIPE_READ enabled in the flags.
|
|
* Writing will only be allowed with ECORE_EXE_PIPE_WRITE enabled in the
|
|
* flags.
|
|
*
|
|
* @param exe_cmd The command to run with @c /bin/sh.
|
|
* @param flags The flag parameters for how to deal with inter-process I/O
|
|
* @param data Data to attach to the returned process handle.
|
|
* @return A process handle to the spawned process.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
Ecore_Exe *
|
|
ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data)
|
|
{
|
|
/* FIXME: Maybe we should allow STDERR reading as well. */
|
|
Ecore_Exe *exe = NULL;
|
|
int readPipe[2] = { -1, -1 };
|
|
int n = 0;
|
|
int ok = 0;
|
|
int result;
|
|
|
|
if (!exe_cmd) return NULL;
|
|
|
|
if ((flags & (ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE)) == 0) return ecore_exe_run(exe_cmd, data);
|
|
|
|
exe = calloc(1, sizeof(Ecore_Exe));
|
|
if (exe == NULL) return NULL;
|
|
|
|
/* Create some pipes. */
|
|
E_IF_NO_ERRNO_NOLOOP(result, pipe(readPipe), ok)
|
|
{
|
|
int writePipe[2] = { -1, -1 };
|
|
|
|
E_IF_NO_ERRNO_NOLOOP(result, pipe(writePipe), ok)
|
|
{
|
|
int statusPipe[2] = { -1, -1 };
|
|
|
|
E_IF_NO_ERRNO_NOLOOP(result, pipe(statusPipe), ok)
|
|
{
|
|
pid_t pid = 0;
|
|
volatile int vfork_exec_errno = 0;
|
|
|
|
/* FIXME: I should double check this. After a quick look around, this is already done, but via a more modern method. */
|
|
/* signal(SIGPIPE, SIG_IGN); We only want EPIPE on errors */
|
|
pid = fork();
|
|
|
|
if (pid == -1)
|
|
{
|
|
fprintf(stderr, "Failed to fork process\n");
|
|
pid = 0;
|
|
}
|
|
else if (pid == 0) /* child */
|
|
{
|
|
/* Close and/or dup STDIN and STDOUT. */
|
|
E_IF_NO_ERRNO(result, close(STDIN_FILENO), ok);
|
|
{
|
|
if (flags & ECORE_EXE_PIPE_WRITE)
|
|
E_NO_ERRNO(result, dup2(writePipe[0], STDIN_FILENO), ok);
|
|
else
|
|
E_NO_ERRNO(result, close(writePipe[0]), ok);
|
|
|
|
if (ok)
|
|
{
|
|
E_IF_NO_ERRNO(result, close(STDOUT_FILENO), ok)
|
|
{
|
|
if (flags & ECORE_EXE_PIPE_READ)
|
|
E_NO_ERRNO(result, dup2(readPipe[1], STDOUT_FILENO), ok);
|
|
else
|
|
E_NO_ERRNO(result, close(readPipe[1]), ok);
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
/* Setup the status pipe. */
|
|
E_NO_ERRNO(result, close(statusPipe[0]), ok);
|
|
E_IF_NO_ERRNO(result, fcntl(statusPipe[1], F_SETFD, FD_CLOEXEC), ok) /* close on exec shows sucess */
|
|
{
|
|
/* Close STDERR. */
|
|
E_NO_ERRNO(result, close(STDERR_FILENO), ok);
|
|
/* Run the actual command. */
|
|
_ecore_exe_exec_it(exe_cmd); /* Should not return from this. */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Something went 'orribly wrong. */
|
|
vfork_exec_errno = errno;
|
|
|
|
/* Close the pipes. */
|
|
if (flags & ECORE_EXE_PIPE_READ) E_NO_ERRNO(result, close(readPipe[1]), ok);
|
|
if (flags & ECORE_EXE_PIPE_WRITE) E_NO_ERRNO(result, close(writePipe[0]), ok);
|
|
E_NO_ERRNO(result, close(statusPipe[1]), ok);
|
|
|
|
_exit(-1);
|
|
}
|
|
else /* parent */
|
|
{
|
|
/* Close the unused pipes. */
|
|
if (! (flags & ECORE_EXE_PIPE_READ)) E_NO_ERRNO(result, close(readPipe[0]), ok);
|
|
if (! (flags & ECORE_EXE_PIPE_WRITE)) E_NO_ERRNO(result, close(writePipe[1]), ok);
|
|
E_NO_ERRNO(result, close(statusPipe[1]), ok);
|
|
|
|
/* FIXME: after having a good look at the current e fd handling, investigate fcntl(dataPipe[x], F_SETSIG, ...) */
|
|
|
|
/* Wait for it to start executing. */
|
|
while (1)
|
|
{
|
|
char buf;
|
|
|
|
E_NO_ERRNO(result, read(statusPipe[0], &buf, 1), ok);
|
|
if (result == 0)
|
|
{
|
|
if (vfork_exec_errno != 0)
|
|
{
|
|
n = vfork_exec_errno;
|
|
fprintf(stderr, "Could not start \"%s\"\n", exe_cmd);
|
|
pid = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Close the status pipe. */
|
|
E_NO_ERRNO(result, close(statusPipe[0]), ok);
|
|
}
|
|
|
|
if (pid)
|
|
{
|
|
/* Setup the exe structure. */
|
|
ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE);
|
|
exe->pid = pid;
|
|
exe->flags = flags;
|
|
exe->data = (void *)data;
|
|
if ((exe->cmd = strdup(exe_cmd)))
|
|
{
|
|
if (flags & ECORE_EXE_PIPE_READ)
|
|
{ /* Setup the read stuff. */
|
|
exe->child_fd_read = readPipe[0];
|
|
E_IF_NO_ERRNO(result, fcntl(exe->child_fd_read, F_SETFL, O_NONBLOCK), ok)
|
|
{
|
|
exe->read_fd_handler = ecore_main_fd_handler_add(exe->child_fd_read,
|
|
ECORE_FD_READ, _ecore_exe_data_read_handler, exe,
|
|
NULL, NULL);
|
|
if (exe->read_fd_handler == NULL)
|
|
ok = 0;
|
|
}
|
|
}
|
|
if (ok && (flags & ECORE_EXE_PIPE_WRITE))
|
|
{ /* Setup the write stuff. */
|
|
exe->child_fd_write = writePipe[1];
|
|
E_IF_NO_ERRNO(result, fcntl(exe->child_fd_write, F_SETFL, O_NONBLOCK), ok)
|
|
{
|
|
exe->write_fd_handler = ecore_main_fd_handler_add(exe->child_fd_write,
|
|
ECORE_FD_WRITE, _ecore_exe_data_write_handler, exe,
|
|
NULL, NULL);
|
|
if (exe->write_fd_handler)
|
|
ecore_main_fd_handler_active_set(exe->write_fd_handler, 0); /* Nothing to write to start with. */
|
|
else
|
|
ok = 0;
|
|
}
|
|
}
|
|
|
|
exes = _ecore_list2_append(exes, exe);
|
|
n = 0;
|
|
}
|
|
else
|
|
ok = 0;
|
|
}
|
|
else
|
|
ok = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ok)
|
|
{ /* Something went wrong, so pull down everything. */
|
|
if (exe->pid) ecore_exe_terminate(exe);
|
|
IF_FN_DEL(_ecore_exe_free, exe);
|
|
}
|
|
else
|
|
printf("Running as %d for %s.\n", exe->pid, exe->cmd);
|
|
|
|
errno = n;
|
|
return exe;
|
|
}
|
|
|
|
/**
|
|
* Sends data to the given child process which it recieves on stdin.
|
|
*
|
|
* This function writes to a child processes standard in, with unlimited
|
|
* buffering. This call will never block. It may fail if the system runs out
|
|
* of memory.
|
|
*
|
|
* @param exe The child process to send to
|
|
* @param data The data to send
|
|
* @param size The size of the data to send, in bytes
|
|
* @return 1 if successful, 0 on failure.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
int
|
|
ecore_exe_send(Ecore_Exe *exe, void *data, int size)
|
|
{
|
|
void *buf;
|
|
|
|
buf = realloc(exe->write_data_buf, exe->write_data_size + size);
|
|
if (buf == NULL) return 0;
|
|
|
|
exe->write_data_buf = buf;
|
|
memcpy(exe->write_data_buf + exe->write_data_size, data, size);
|
|
exe->write_data_size += size;
|
|
|
|
if (exe->write_fd_handler)
|
|
ecore_main_fd_handler_active_set(exe->write_fd_handler, ECORE_FD_WRITE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* The stdin of the given child process will close when the write buffer is empty.
|
|
*
|
|
* @param exe The child process
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
void
|
|
ecore_exe_close_stdin(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_close_stdin");
|
|
return;
|
|
}
|
|
exe->close_stdin = 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the string tag for the given process handle
|
|
*
|
|
* @param exe The given process handle.
|
|
* @param tag The string tag to set on the process handle.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
void
|
|
ecore_exe_tag_set(Ecore_Exe *exe, const char *tag)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_tag_set");
|
|
return;
|
|
}
|
|
IF_FREE(exe->tag);
|
|
if (tag) exe->tag = strdup(tag);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the tag attached to the given process handle. There is no need to
|
|
* free it as it just returns the internal pointer value. This value is only
|
|
* valid as long as the @p exe is valid or until the tag is set to something
|
|
* else on this @p exe.
|
|
*
|
|
* @param exe The given process handle.
|
|
* @return The string attached to @p exe.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
char *
|
|
ecore_exe_tag_get(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_tag_get");
|
|
return NULL;
|
|
}
|
|
return exe->tag;
|
|
}
|
|
|
|
/**
|
|
* Frees the given process handle.
|
|
*
|
|
* Note that the process that the handle represents is unaffected by this
|
|
* function.
|
|
*
|
|
* @param exe The given process handle.
|
|
* @return The data attached to the handle when @ref ecore_exe_run was
|
|
* called.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
void *
|
|
ecore_exe_free(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_free");
|
|
return NULL;
|
|
}
|
|
return _ecore_exe_free(exe);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the process ID of the given spawned process.
|
|
* @param exe Handle to the given spawned process.
|
|
* @return The process ID on success. @c -1 otherwise.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
pid_t
|
|
ecore_exe_pid_get(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_pid_get");
|
|
return -1;
|
|
}
|
|
return exe->pid;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the data attached to the given process handle.
|
|
* @param exe The given process handle.
|
|
* @return The data pointer attached to @p exe.
|
|
* @ingroup Ecore_Exe_Basic_Group
|
|
*/
|
|
void *
|
|
ecore_exe_data_get(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_data_get");
|
|
return NULL;
|
|
}
|
|
return exe->data;
|
|
}
|
|
|
|
/**
|
|
* @defgroup Ecore_Exe_Signal_Group Spawned Process Signal Functions
|
|
*
|
|
* Functions that send signals to spawned processes.
|
|
*/
|
|
|
|
/**
|
|
* Pauses the given process by sending it a @c SIGSTOP signal.
|
|
* @param exe Process handle to the given process.
|
|
* @ingroup Ecore_Exe_Signal_Group
|
|
*/
|
|
void
|
|
ecore_exe_pause(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_pause");
|
|
return;
|
|
}
|
|
kill(exe->pid, SIGSTOP);
|
|
}
|
|
|
|
/**
|
|
* Continues the given paused process by sending it a @c SIGCONT signal.
|
|
* @param exe Process handle to the given process.
|
|
* @ingroup Ecore_Exe_Signal_Group
|
|
*/
|
|
void
|
|
ecore_exe_continue(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_continue");
|
|
return;
|
|
}
|
|
kill(exe->pid, SIGCONT);
|
|
}
|
|
|
|
/**
|
|
* Sends the given spawned process a terminate (@c SIGTERM) signal.
|
|
* @param exe Process handle to the given process.
|
|
* @ingroup Ecore_Exe_Signal_Group
|
|
*/
|
|
void
|
|
ecore_exe_terminate(Ecore_Exe *exe)
|
|
{
|
|
struct _ecore_exe_dead_exe *dead;
|
|
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_terminate");
|
|
return;
|
|
}
|
|
printf("Sending TERM signal to %s (%d).\n", exe->cmd, exe->pid);
|
|
kill(exe->pid, SIGTERM);
|
|
|
|
dead = calloc(1, sizeof(struct _ecore_exe_dead_exe));
|
|
if (dead)
|
|
{
|
|
dead->pid = exe->pid;
|
|
dead->cmd = strdup(exe->cmd);
|
|
ecore_timer_add(10.0, _ecore_exe_make_sure_its_dead, dead);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kills the given spawned process by sending it a @c SIGKILL signal.
|
|
* @param exe Process handle to the given process.
|
|
* @ingroup Ecore_Exe_Signal_Group
|
|
*/
|
|
void
|
|
ecore_exe_kill(Ecore_Exe *exe)
|
|
{
|
|
struct _ecore_exe_dead_exe *dead;
|
|
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_kill");
|
|
return;
|
|
}
|
|
printf("Sending KILL signal to %s (%d).\n", exe->cmd, exe->pid);
|
|
kill(exe->pid, SIGKILL);
|
|
|
|
dead = calloc(1, sizeof(struct _ecore_exe_dead_exe));
|
|
if (dead)
|
|
{
|
|
dead->pid = exe->pid;
|
|
dead->cmd = strdup(exe->cmd);
|
|
ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead, dead);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends a @c SIGUSR signal to the given spawned process.
|
|
* @param exe Process handle to the given process.
|
|
* @param num The number user signal to send. Must be either 1 or 2, or
|
|
* the signal will be ignored.
|
|
* @ingroup Ecore_Exe_Signal_Group
|
|
*/
|
|
void
|
|
ecore_exe_signal(Ecore_Exe *exe, int num)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_signal");
|
|
return;
|
|
}
|
|
if (num == 1)
|
|
kill(exe->pid, SIGUSR1);
|
|
else if (num == 2)
|
|
kill(exe->pid, SIGUSR2);
|
|
}
|
|
|
|
/**
|
|
* Sends a @c SIGHUP signal to the given spawned process.
|
|
* @param exe Process handle to the given process.
|
|
* @ingroup Ecore_Exe_Signal_Group
|
|
*/
|
|
void
|
|
ecore_exe_hup(Ecore_Exe *exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
{
|
|
ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
|
|
"ecore_exe_hup");
|
|
return;
|
|
}
|
|
kill(exe->pid, SIGHUP);
|
|
}
|
|
|
|
static int
|
|
_ecore_exe_is_it_alive(pid_t pid)
|
|
{
|
|
Ecore_Exe *exe;
|
|
|
|
/* FIXME: There is no nice, safe, OS independant way to tell if a
|
|
* particular PID is still alive. I have written code to do so
|
|
* for my urunlevel busybox applet (http://urunlevel.sourceforge.net/),
|
|
* but it's for linux only, and still not guaranteed.
|
|
*
|
|
* So for now, we just check that a valid Ecore_Exe structure
|
|
* exists for it. Even that is not a guarantee, as the structure
|
|
* can be freed without killing the process.
|
|
*
|
|
* I think we can safely put exe's into two categories, those users
|
|
* that care about the life of the exe, and the run and forget type.
|
|
* The run and forget type starts up the exe, then free's the
|
|
* Ecore_Exe structure straight away. They can never call any of
|
|
* the functions that can call this, so we don't worry about them.
|
|
*
|
|
* Those user's that care about the life of exe's will keep the
|
|
* Ecore_Exe structure around, terminate them eventually, or
|
|
* register for exit events. For these ones the assumption
|
|
* that valid Ecore_Exe struct == live exe is almost valid.
|
|
*
|
|
* I will probably copy my urunlevel code into here someday.
|
|
*/
|
|
exe = _ecore_exe_find(pid);
|
|
if (exe)
|
|
{
|
|
if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
|
|
return (pid_t) 1;
|
|
}
|
|
|
|
return (pid_t) 0;
|
|
}
|
|
|
|
static int
|
|
_ecore_exe_make_sure_its_dead(void *data)
|
|
{
|
|
struct _ecore_exe_dead_exe *dead;
|
|
|
|
dead = data;
|
|
if (dead)
|
|
{
|
|
if (_ecore_exe_is_it_alive(dead->pid))
|
|
{
|
|
if (dead->cmd)
|
|
printf("Sending KILL signal to alledgedly dead %s (%d).\n", dead->cmd, dead->pid);
|
|
else
|
|
printf("Sending KILL signal to alledgedly dead PID %d.\n", dead->pid);
|
|
kill(dead->pid, SIGKILL);
|
|
ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead, dead);
|
|
}
|
|
else
|
|
{
|
|
IF_FREE(dead->cmd);
|
|
free(dead);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_ecore_exe_make_sure_its_really_dead(void *data)
|
|
{
|
|
struct _ecore_exe_dead_exe *dead;
|
|
|
|
dead = data;
|
|
if (dead)
|
|
{
|
|
if (_ecore_exe_is_it_alive(dead->pid))
|
|
{
|
|
if (dead->cmd)
|
|
printf("%s (%d) is not really dead.\n", dead->cmd, dead->pid);
|
|
else
|
|
printf("PID %d is not really dead.\n", dead->pid);
|
|
}
|
|
IF_FREE(dead->cmd);
|
|
free(dead);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
_ecore_exe_shutdown(void)
|
|
{
|
|
while (exes) _ecore_exe_free(exes);
|
|
}
|
|
|
|
Ecore_Exe *
|
|
_ecore_exe_find(pid_t pid)
|
|
{
|
|
Ecore_List2 *l;
|
|
|
|
for (l = (Ecore_List2 *)exes; l; l = l->next)
|
|
{
|
|
Ecore_Exe *exe;
|
|
|
|
exe = (Ecore_Exe *)l;
|
|
if (exe->pid == pid) return exe;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_ecore_exe_exec_it(const char *exe_cmd)
|
|
{
|
|
char use_sh = 1;
|
|
char* buf = NULL;
|
|
char** args = NULL;
|
|
int save_errno = 0;
|
|
|
|
if (! strpbrk(exe_cmd, "|&;<>()$`\\\"'*?#"))
|
|
{
|
|
char* token;
|
|
char pre_command = 1;
|
|
int num_tokens = 0;
|
|
|
|
if (! (buf = strdup(exe_cmd)))
|
|
return;
|
|
|
|
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_FREE(buf);
|
|
if (! token && num_tokens)
|
|
{
|
|
int i = 0;
|
|
char* token;
|
|
|
|
if (! (buf = strdup(exe_cmd)))
|
|
return;
|
|
|
|
token = strtok(buf, " \t\n\v");
|
|
use_sh = 0;
|
|
if (! (args = (char**) calloc(num_tokens + 1, sizeof(char*))))
|
|
{
|
|
IF_FREE(buf);
|
|
return;
|
|
}
|
|
for (i = 0; i < num_tokens; i ++)
|
|
{
|
|
if (token)
|
|
args[i] = token;
|
|
token = strtok(NULL, " \t\n\v");
|
|
}
|
|
args[num_tokens] = NULL;
|
|
}
|
|
}
|
|
|
|
setsid();
|
|
if (use_sh)
|
|
{ /* We have to use a shell to run this. */
|
|
if (shell == NULL)
|
|
{ /* Find users preferred shell. */
|
|
shell = getenv("SHELL");
|
|
if (shell == 0)
|
|
shell = "/bin/sh";
|
|
}
|
|
errno = 0;
|
|
execl(shell, shell, "-c", exe_cmd, (char *)NULL);
|
|
}
|
|
else
|
|
{ /* We can run this directly. */
|
|
errno = 0;
|
|
execvp(args[0], args);
|
|
}
|
|
|
|
save_errno = errno;
|
|
IF_FREE(buf);
|
|
IF_FREE(args);
|
|
errno = save_errno;
|
|
return;
|
|
}
|
|
|
|
void *
|
|
_ecore_exe_free(Ecore_Exe *exe)
|
|
{
|
|
void *data;
|
|
int ok = 0;
|
|
int result;
|
|
|
|
data = exe->data;
|
|
|
|
IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler);
|
|
IF_FN_DEL(ecore_main_fd_handler_del, exe->read_fd_handler);
|
|
if (exe->child_fd_read) E_NO_ERRNO(result, close(exe->child_fd_read), ok);
|
|
if (exe->child_fd_write) E_NO_ERRNO(result, close(exe->child_fd_write), ok);
|
|
IF_FREE(exe->write_data_buf);
|
|
IF_FREE(exe->read_data_buf);
|
|
IF_FREE(exe->cmd);
|
|
|
|
exes = _ecore_list2_remove(exes, exe);
|
|
ECORE_MAGIC_SET(exe, ECORE_MAGIC_NONE);
|
|
IF_FREE(exe->tag);
|
|
free(exe);
|
|
return data;
|
|
}
|
|
|
|
|
|
static int
|
|
_ecore_exe_data_read_handler(void *data, Ecore_Fd_Handler *fd_handler)
|
|
{
|
|
Ecore_Exe *exe;
|
|
|
|
exe = data;
|
|
if ((exe->read_fd_handler) && (ecore_main_fd_handler_active_get(exe->read_fd_handler, ECORE_FD_READ)))
|
|
{
|
|
unsigned char *inbuf;
|
|
int inbuf_num;
|
|
|
|
/* Get any left over data from last time. */
|
|
inbuf = exe->read_data_buf;
|
|
inbuf_num = exe->read_data_size;
|
|
exe->read_data_buf = NULL;
|
|
exe->read_data_size = 0;
|
|
|
|
for (;;)
|
|
{
|
|
int num, lost_exe;
|
|
char buf[READBUFSIZ];
|
|
|
|
lost_exe = 0;
|
|
errno = 0;
|
|
if ((num = read(exe->child_fd_read, buf, READBUFSIZ)) < 1) /* FIXME: SPEED/SIZE TRADE OFF - add a smaller READBUFSIZE (currently 64k) to inbuf, use that instead of buf, and save ourselves a memcpy(). */
|
|
{
|
|
lost_exe = ((errno == EIO) ||
|
|
(errno == EBADF) ||
|
|
(errno == EPIPE) ||
|
|
(errno == EINVAL) ||
|
|
(errno == ENOSPC));
|
|
if ((errno != EAGAIN) && (errno != EINTR))
|
|
perror("_ecore_exe_data_handler() read problem ");
|
|
}
|
|
if (num > 0)
|
|
{ /* Data got read. */
|
|
inbuf = realloc(inbuf, inbuf_num + num);
|
|
memcpy(inbuf + inbuf_num, buf, num);
|
|
inbuf_num += num;
|
|
}
|
|
else
|
|
{ /* No more data to read. */
|
|
if (inbuf)
|
|
{
|
|
Ecore_Event_Exe_Data *e;
|
|
|
|
e = calloc(1, sizeof(Ecore_Event_Exe_Data));
|
|
if (e)
|
|
{
|
|
e->exe = exe;
|
|
e->data = inbuf;
|
|
e->size = inbuf_num;
|
|
|
|
if (exe->flags & ECORE_EXE_PIPE_READ_LINE_BUFFERED)
|
|
{
|
|
int max = 0;
|
|
int count = 0;
|
|
int i;
|
|
int last = 0;
|
|
char *c;
|
|
|
|
c = inbuf;
|
|
for (i = 0; i < inbuf_num; i++) /* Find the lines. */
|
|
{
|
|
if (inbuf[i] == '\n')
|
|
{
|
|
if (count >= max)
|
|
{
|
|
max += 10; /* FIXME: Maybe keep track of the largest number of lines ever sent, and add half that many instead of 10. */
|
|
e->lines = realloc(e->lines, sizeof(Ecore_Event_Exe_Data_Line) * (max + 1)); /* Allow room for the NULL termination. */
|
|
}
|
|
/* raster said to leave the line endings as line endings, however -
|
|
* This is line buffered mode, we are not dealing with binary here, but lines.
|
|
* If we are not dealing with binary, we must be dealing with ASCII, unicode, or some other text format.
|
|
* Thus the user is most likely gonna deal with this text as strings.
|
|
* Thus the user is most likely gonna pass this data to str functions.
|
|
* rasters way - the endings are always gonna be '\n'; onefangs way - they will always be '\0'
|
|
* We are handing them the string length as a convenience.
|
|
* Thus if they really want it in raw format, they can e->lines[i].line[e->lines[i].size - 1] = '\n'; easily enough.
|
|
* In the default case, we can do this conversion quicker than the user can, as we already have the index and pointer.
|
|
* Let's make it easy on them to use these as standard C strings.
|
|
*
|
|
* onefang is proud to announce that he has just set a new personal record for the
|
|
* most over documentation of a simple assignment statement. B-)
|
|
*/
|
|
inbuf[i] = '\0';
|
|
e->lines[count].line = c;
|
|
e->lines[count].size = i - last;
|
|
last = i + 1;
|
|
c = &inbuf[last];
|
|
count++;
|
|
}
|
|
}
|
|
if (count == 0) /* No lines to send, cancel the event. */
|
|
{
|
|
_ecore_exe_event_exe_data_free(NULL, e);
|
|
e = NULL;
|
|
}
|
|
else /* NULL terminate the array, so that people know where the end is. */
|
|
{
|
|
e->lines[count].line = NULL;
|
|
e->lines[count].size = 0;
|
|
}
|
|
if (i > last) /* Partial line left over, save it for next time. */
|
|
{
|
|
e->size = last;
|
|
exe->read_data_size = i - last;
|
|
exe->read_data_buf = malloc(exe->read_data_size);
|
|
memcpy(exe->read_data_buf, c, exe->read_data_size);
|
|
}
|
|
}
|
|
|
|
if (e) /* Send the event. */
|
|
ecore_event_add(ECORE_EVENT_EXE_DATA, e,
|
|
_ecore_exe_event_exe_data_free, NULL);
|
|
}
|
|
}
|
|
if (lost_exe)
|
|
{
|
|
if (exe->read_data_size)
|
|
printf("There are %d bytes left unsent from the dead exe %s.\n", exe->read_data_size, exe->cmd);
|
|
/* Thought about this a bit. If the exe has actually
|
|
* died, this won't do any harm as it must have died
|
|
* recently and the pid has not had a chance to recycle.
|
|
* It is also a paranoid catchall, coz the usual ecore_signal
|
|
* mechenism should kick in. But let's give it a good
|
|
* kick anyway.
|
|
*/
|
|
ecore_exe_terminate(exe);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_ecore_exe_data_write_handler(void *data, Ecore_Fd_Handler *fd_handler)
|
|
{
|
|
Ecore_Exe *exe;
|
|
|
|
exe = data;
|
|
if ((exe->write_fd_handler) && (ecore_main_fd_handler_active_get(exe->write_fd_handler, ECORE_FD_WRITE)))
|
|
_ecore_exe_flush(exe);
|
|
|
|
/* If we have sent all there is to send, and we need to close the pipe, then close it. */
|
|
if ((exe->close_stdin == 1) && (exe->write_data_size == exe->write_data_offset))
|
|
{
|
|
int ok = 0;
|
|
int result;
|
|
|
|
printf("Closing stdin for %s\n", exe->cmd);
|
|
/* if (exe->child_fd_write) E_NO_ERRNO(result, fsync(exe->child_fd_write), ok); This a) doesn't work, and b) isn't needed. */
|
|
IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler);
|
|
if (exe->child_fd_write) E_NO_ERRNO(result, close(exe->child_fd_write), ok);
|
|
exe->child_fd_write = 0;
|
|
IF_FREE(exe->write_data_buf);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
_ecore_exe_flush(Ecore_Exe *exe)
|
|
{
|
|
int count;
|
|
|
|
/* check whether we need to write anything at all. */
|
|
if ((!exe->child_fd_write) && (!exe->write_data_buf)) return;
|
|
if (exe->write_data_size == exe->write_data_offset) return;
|
|
|
|
count = write(exe->child_fd_write,
|
|
exe->write_data_buf + exe->write_data_offset,
|
|
exe->write_data_size - exe->write_data_offset);
|
|
if (count < 1)
|
|
{
|
|
if (errno == EIO || errno == EBADF ||
|
|
errno == EPIPE || errno == EINVAL ||
|
|
errno == ENOSPC) /* we lost our exe! */
|
|
{
|
|
ecore_exe_terminate(exe);
|
|
if (exe->write_fd_handler)
|
|
ecore_main_fd_handler_active_set(exe->write_fd_handler, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exe->write_data_offset += count;
|
|
if (exe->write_data_offset >= exe->write_data_size)
|
|
{ /* Nothing left to write, clean up. */
|
|
exe->write_data_size = 0;
|
|
exe->write_data_offset = 0;
|
|
IF_FREE(exe->write_data_buf);
|
|
if (exe->write_fd_handler)
|
|
ecore_main_fd_handler_active_set(exe->write_fd_handler, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_exe_event_exe_data_free(void *data __UNUSED__, void *ev)
|
|
{
|
|
Ecore_Event_Exe_Data *e;
|
|
|
|
e = ev;
|
|
|
|
IF_FREE(e->lines);
|
|
IF_FREE(e->data);
|
|
free(e);
|
|
}
|
|
#endif
|