gambas-source-code/main/gbx/gbx_c_process.c
Benoît Minisini 5ecb973af4 [DEVELOPMENT ENVIRONMENT]
* BUG: Image editor: Don't load the image twice.
* NEW: Do not open the welcome dialog if a project directory has been 
  specified in program arguments.
* NEW: Farm client: Paint the software names the same way everywhere.
* BUG: If the workspace was filled before
  the main window is visible, then the QT5 backing store starts displaying
  garbage. The workaround was to always show the main window before trying 
  to load any file.
* NEW: Add the environment variables to the system information.

[WIKI CGI SCRIPT]
* BUG: Fix some HTML syntax errors.

[INTERPRETER]
* BUG: Add the Rand() syntax description.

[GB.DB.POSTGRESQL]
* BUG: If PostgreSQL version is greater than 9.0, then force the bytea 
  output format to 'escape', as it was before.

[GB.EVAL.HIGHLIGHT]
* NEW: The TextHighlighter.ToHtml() method now takes the 'Alternate' style
  flag into account, and the generated HTML is less verbose.
* BUG: The WebPage highlighting has been fixed.

[GB.FORM]
* BUG: Completion: Fix a possible crash.
* BUG: TabPanel: Fix a possible crash.

[GB.SDL2]
* NEW: Window.Handle is a new property that returns the window handle.


git-svn-id: svn://localhost/gambas/trunk@7188 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2015-07-27 03:54:57 +00:00

1214 lines
23 KiB
C

/***************************************************************************
gbx_c_process.c
(c) 2000-2013 Benoît Minisini <gambas@users.sourceforge.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#define __GBX_C_PROCESS_C
#include "gbx_info.h"
#ifndef GBX_INFO
#include "gb_common.h"
#include "gb_common_buffer.h"
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifdef OS_OPENBSD
/* granpt(), unlockpt() and ptsname() unavailable under OpenBSD
and are replaced with openpty() implementation because of security
issues */
#include <util.h>
#endif
#include "gb_replace.h"
#include "gb_limit.h"
#include "gb_array.h"
#include "gbx_api.h"
#include "gambas.h"
#include "gbx_stream.h"
#include "gbx_exec.h"
#include "gbx_class.h"
#include "gbx_watch.h"
#include "gbx_project.h"
#include "gbx_c_array.h"
#include "gbx_local.h"
#include "gbx_signal.h"
#include "gbx_c_process.h"
//#define DEBUG_ME
//#define DEBUG_CHILD
char *CPROCESS_shell = NULL;
extern char **environ;
DECLARE_EVENT(EVENT_Read);
DECLARE_EVENT(EVENT_Error);
DECLARE_EVENT(EVENT_Kill);
static CPROCESS *_running_process_list = NULL;
static int _running_process = 0;
static int _ignore_process = 0;
static SIGNAL_CALLBACK *_SIGCHLD_callback;
static bool _init = FALSE;
static int _last_status = 0;
static int _last_child_error = 0;
static int _last_child_error_errno = 0;
static void init_child(void);
static void exit_child(void);
static void wait_child(CPROCESS *process);
enum {
CHILD_NO_ERROR,
CHILD_CANNOT_OPEN_TTY,
CHILD_CANNOT_INIT_TTY,
CHILD_CANNOT_PLUG_INPUT,
CHILD_CANNOT_PLUG_OUTPUT,
CHILD_CANNOT_EXEC,
};
static const char *const _child_error[] = {
NULL,
"cannot open slave pseudo-terminal: ",
"cannot initialize pseudo-terminal: ",
"cannot plug standard input: ",
"cannot plug standard output and standard error: ",
"cannot run executable: "
};
//-------------------------------------------------------------------------
static void close_fd(int *pfd)
{
int fd = *pfd;
if (fd >= 0)
{
#ifdef DEBUG_ME
fprintf(stderr, "unwatch & close: %d\n", fd);
#endif
GB_Watch(fd, GB_WATCH_NONE, NULL, 0);
close(fd);
*pfd = -1;
}
}
static void add_process_to_running_list(CPROCESS *process)
{
if (_running_process_list)
_running_process_list->prev = process;
process->next = _running_process_list;
process->prev = NULL;
_running_process_list = process;
process->running = TRUE;
_running_process++;
}
static void remove_process_from_running_list(CPROCESS *process)
{
if (process->prev)
process->prev->next = process->next;
if (process->next)
process->next->prev = process->prev;
if (process == _running_process_list)
_running_process_list = process->next;
process->running = FALSE;
_running_process--;
if (process->ignore)
_ignore_process--;
}
static void callback_write(int fd, int type, CPROCESS *process)
{
#ifdef DEBUG_ME
fprintf(stderr, "callback_write: %d %p\n", fd, process);
#endif
if (process->to_string)
{
int n;
for(;;)
{
n = read(fd, COMMON_buffer, 256);
if (n >= 0 || errno != EINTR)
break;
}
if (n > 0)
{
process->result = STRING_add(process->result, COMMON_buffer, n);
return;
}
}
if (GB_CanRaise(process, EVENT_Read))
{
if (!STREAM_read_ahead(CSTREAM_stream(process)))
{
GB_Raise(process, EVENT_Read, 0);
return;
}
}
close_fd(&process->out);
}
static void callback_error(int fd, int type, CPROCESS *process)
{
char buffer[256];
int n;
#ifdef DEBUG_ME
fprintf(stderr, "callback_error: %d %p\n", fd, process);
#endif
if (GB_CanRaise(process, EVENT_Error))
{
n = read(fd, buffer, sizeof(buffer));
if (n > 0)
{
GB_Raise(process, EVENT_Error, 1, GB_T_STRING, buffer, n);
return;
}
}
close_fd(&process->err);
}
static void update_stream(CPROCESS *process)
{
STREAM *stream = &process->ob.stream;
stream->type = &STREAM_process;
(*stream->type->open)(stream, NULL, 0, process);
}
static void init_process(CPROCESS *process)
{
process->watch = GB_WATCH_NONE;
process->in = process->out = process->err = -1;
update_stream(process);
}
static void exit_process(CPROCESS *_object)
{
#ifdef DEBUG_ME
fprintf(stderr, "exit_process: %p\n", _object);
#endif
if (THIS->in >= 0)
{
if (THIS->in != THIS->out)
close(THIS->in);
THIS->in = -1;
}
close_fd(&THIS->out);
close_fd(&THIS->err);
STREAM_close(&THIS->ob.stream);
}
static void prepare_child_error(CPROCESS *_object)
{
int fd;
char path[PATH_MAX];
snprintf(path, sizeof(path), FILE_TEMP_DIR "/%d.child", (int)getuid(), (int)getpid(), (int)THIS->pid);
#ifdef DEBUG_ME
fprintf(stderr, "prepare_child_error: %p: %s\n", _object, path);
#endif
if (_last_child_error == 0)
{
fd = open(path, O_RDONLY);
if (fd >= 0)
{
if (read(fd, &_last_child_error, sizeof(int)) != sizeof(int)
|| read(fd, &_last_child_error_errno, sizeof(int)) != sizeof(int))
{
_last_child_error = -1;
_last_child_error_errno = 0;
}
close(fd);
}
#ifdef DEBUG_ME
fprintf(stderr, "prepare_child_error: error = %d errno = %d\n", _last_child_error, _last_child_error_errno);
#endif
}
unlink(path);
}
static void throw_last_child_error()
{
int child_error, child_errno;
if (_last_child_error == 0)
return;
child_error = _last_child_error;
child_errno = _last_child_error_errno;
_last_child_error = 0;
#ifdef DEBUG_ME
fprintf(stderr, "throw_last_child_error: %d %d\n", child_error, child_errno);
#endif
if (child_error < 0)
THROW(E_CHILD, "unknown error", "");
else
THROW(E_CHILD, _child_error[child_error], strerror(child_errno));
}
static void stop_process_after(CPROCESS *_object)
{
STREAM *stream;
bool do_exit_process = FALSE;
#ifdef DEBUG_ME
fprintf(stderr, "stop_process_after: %p out = %d err = %d\n", _object, THIS->out, THIS->err);
#endif
if (WIFEXITED(THIS->status) && WEXITSTATUS(THIS->status) == 255)
{
prepare_child_error(THIS);
exit_process(THIS);
//OBJECT_detach((OBJECT *)THIS);
}
/* Vidage du tampon d'erreur */
while (THIS->err >= 0)
{
callback_error(THIS->err, 0, THIS);
do_exit_process = TRUE;
}
/* Vidage du tampon de sortie */
if (THIS->out >= 0)
{
stream = CSTREAM_stream(THIS);
while (THIS->out >= 0 && !STREAM_is_closed(stream))
callback_write(THIS->out, 0, THIS);
do_exit_process = TRUE;
}
if (do_exit_process)
exit_process(THIS);
/*printf("** stop_process_after\n");*/
//GB_Unref((void **)&_object); /* Ref du post */
}
static void stop_process(CPROCESS *_object)
{
if (!THIS->running)
return;
#ifdef DEBUG_ME
fprintf(stderr, "stop_process: %p\n", THIS);
#endif
/* Remove from running process list */
stop_process_after(THIS);
remove_process_from_running_list(THIS);
#ifdef DEBUG_ME
fprintf(stderr, "Raising Kill event for %p: parent = %p can raise = %d\n", THIS, OBJECT_parent(THIS), GB_CanRaise(THIS, EVENT_Kill));
#endif
GB_Raise(THIS, EVENT_Kill, 0);
OBJECT_detach((OBJECT *)THIS);
OBJECT_UNREF(_object);
//if (!_running_process_list)
if (_running_process <= _ignore_process)
exit_child();
}
static void abort_child(int error)
{
int fd;
int save_errno;
char path[PATH_MAX];
fflush(stdout);
fflush(stderr);
save_errno = errno;
#ifdef DEBUG_ME
fprintf(stderr, "abort_child: %d %d\n", error, save_errno);
#endif
snprintf(path, sizeof(path), FILE_TEMP_DIR "/%d.child", (int)getuid(), (int)getppid(), (int)getpid());
fd = open(path, O_CREAT | O_WRONLY, 0600);
if (fd >= 0)
{
write(fd, &error, sizeof(int)) == sizeof(int)
&& write(fd, &save_errno, sizeof(int)) == sizeof(int);
close(fd);
}
_exit(255);
}
static void init_child_tty(int fd)
{
struct termios terminal = { 0 };
tcgetattr(fd, &terminal);
terminal.c_iflag |= ICRNL | IXON | IXOFF;
#ifdef IUTF8
if (LOCAL_is_UTF8)
terminal.c_iflag |= IUTF8;
#endif
terminal.c_oflag |= OPOST;
terminal.c_oflag &= ~ONLCR;
terminal.c_lflag |= ISIG | ICANON | IEXTEN; // | ECHO;
terminal.c_lflag &= ~ECHO;
#ifdef DEBUG_CHILD
fprintf(stderr, "init_child_tty: %s\n", isatty(fd) ? ttyname(fd) : "not a tty!");
#endif
if (tcsetattr(fd, TCSANOW, &terminal))
{
#ifdef DEBUG_CHILD
int save_errno = errno;
fprintf(stderr, "init_child_tty: errno = %d\n", errno);
errno = save_errno;
#endif
abort_child(CHILD_CANNOT_INIT_TTY);
}
}
const char *CPROCESS_search_program_in_path(char *name)
{
char *search;
char *p, *p2;
int len;
const char *path;
if (strchr(name, '/'))
{
if (access(name, X_OK) == 0)
return name;
else
return NULL;
}
search = getenv("PATH");
if (!search || !*search)
search = "/usr/bin:/bin";
search = STRING_new_zero(search);
path = NULL;
//fprintf(stderr, "search_program_in_path: '%s' in '%s'\n", name, search);
p = search;
for(;;)
{
p2 = strchr(p, ':');
if (p2)
len = p2 - p;
else
len = strlen(p);
if (len > 0)
{
p[len] = 0;
//fprintf(stderr, "trying: %s\n", p);
path = FILE_cat(p, name, NULL);
if (access(path, X_OK) == 0)
break;
path = NULL;
}
if (!p2)
break;
p = p2 + 1;
}
STRING_free(&search);
//fprintf(stderr, "--> %s\n", path);
return path;
}
static void run_process(CPROCESS *process, int mode, void *cmd, CARRAY *env)
{
static const char *shell[] = { NULL, "-c", NULL, NULL };
int fdin[2], fdout[2], fderr[2];
pid_t pid;
char **argv;
CARRAY *array;
int i, n;
sigset_t sig, old;
int save_errno;
// for virtual terminal
int fd_master = -1;
char *slave = NULL;
//struct termios termios_stdin;
//struct termios termios_check;
struct termios termios_master;
const char *exec;
if (mode & PM_SHELL)
{
#ifdef DEBUG_ME
fprintf(stderr, "run_process %p: %s\n", process, (char *)cmd);
#endif
argv = (char **)shell;
if (CPROCESS_shell)
argv[0] = CPROCESS_shell;
else
argv[0] = "/bin/sh";
argv[2] = (char *)cmd;
if (argv[2] == NULL || *argv[2] == 0)
return;
exec = argv[0];
process->process_group = TRUE;
}
else
{
array = (CARRAY *)cmd;
n = ARRAY_count(array->data);
if (n == 0)
return;
ALLOC(&argv, sizeof(*argv) * (n + 1));
memcpy(argv, array->data, sizeof(*argv) * n);
argv[n] = NULL;
exec = CPROCESS_search_program_in_path(argv[0]);
if (!exec)
{
IFREE(argv);
THROW(E_NEXIST);
}
for (i = 0; i < n; i++)
{
if (!argv[i])
argv[i] = "";
}
#ifdef DEBUG_ME
{
int i;
fprintf(stderr, "run_process %p: ", process);
for (i = 0; i < n; i++)
fprintf(stderr, "%s ", argv[i]);
fprintf(stderr, "\n");
}
#endif
}
if (mode & PM_STRING)
{
process->to_string = TRUE;
process->result = NULL;
mode |= PM_READ;
}
if (mode & PM_TERM)
{
#ifdef OS_OPENBSD
int fd_slave;
if (openpty(&fd_master, &fd_slave, NULL, NULL, NULL) < 0)
goto __ABORT_ERRNO;
#else
fd_master = posix_openpt(O_RDWR | O_NOCTTY);
if (fd_master < 0)
goto __ABORT_ERRNO;
grantpt(fd_master);
unlockpt(fd_master);
#endif
slave = ptsname(fd_master);
#ifdef DEBUG_ME
fprintf(stderr, "run_process: slave = %s\n", slave);
#endif
}
else
{
/* Create pipes */
if ((mode & PM_WRITE) && pipe(fdin) != 0)
goto __ABORT_ERRNO;
if ((mode & PM_READ) && (pipe(fdout) != 0 || pipe(fderr) != 0))
goto __ABORT_ERRNO;
}
// Adding to the running process list
add_process_to_running_list(process);
OBJECT_REF(process);
// Start the SIGCHLD callback
init_child();
// Block SIGCHLD and fork
sigemptyset(&sig);
sigaddset(&sig, SIGCHLD);
sigprocmask(SIG_BLOCK, &sig, &old);
if (mode & PM_SHELL || mode & PM_TERM || env)
pid = fork();
else
pid = vfork();
if (pid == (-1))
{
stop_process(process);
sigprocmask(SIG_SETMASK, &old, NULL);
goto __ABORT_ERRNO;
}
// parent process
if (pid)
{
process->pid = pid;
#ifdef DEBUG_ME
fprintf(stderr, "fork: pid = %d\n", pid);
#endif
if (mode & PM_TERM)
{
if (tcgetattr(fd_master, &termios_master))
goto __ABORT_ERRNO;
cfmakeraw(&termios_master);
//termios_master.c_lflag &= ~ECHO;
if (tcsetattr(fd_master, TCSANOW, &termios_master))
goto __ABORT_ERRNO;
}
if (mode & PM_WRITE)
{
if (mode & PM_TERM)
{
process->in = fd_master;
}
else
{
close(fdin[0]);
process->in = fdin[1];
}
}
if (mode & PM_READ)
{
if (mode & PM_TERM)
{
process->out = fd_master;
process->err = -1;
}
else
{
close(fdout[1]);
close(fderr[1]);
process->out = fdout[0];
process->err = fderr[0];
}
#ifdef DEBUG_ME
fprintf(stderr, "watch: out = %d err = %d\n", process->out, process->err);
#endif
GB_Watch(process->out, GB_WATCH_READ, (void *)callback_write, (intptr_t)process);
if (process->err >= 0)
{
fcntl(process->err, F_SETFL, fcntl(process->err, F_GETFL) | O_NONBLOCK);
GB_Watch(process->err, GB_WATCH_READ, (void *)callback_error, (intptr_t)process);
}
}
if ((mode & PM_SHELL) == 0)
{
FREE(&argv);
}
sigprocmask(SIG_SETMASK, &old, NULL);
}
else //------------ child process ----------------------------
{
int fd_slave;
bool pwd;
int ch_i, ch_n;
//bool stdin_isatty = isatty(STDIN_FILENO);
sigprocmask(SIG_SETMASK, &old, NULL);
if (mode & PM_SHELL)
setpgid(0, 0);
if (mode & PM_TERM)
{
close(fd_master);
setsid();
fd_slave = open(slave, O_RDWR);
if (fd_slave < 0)
abort_child(CHILD_CANNOT_OPEN_TTY);
/*#ifdef DEBUG_ME
fprintf(stderr, "run_process (child): slave = %s isatty = %d\n", slave, isatty(fd_slave));
#endif*/
if (mode & PM_WRITE)
{
if (dup2(fd_slave, STDIN_FILENO) == -1)
abort_child(CHILD_CANNOT_PLUG_INPUT);
}
if (mode & PM_READ)
{
if ((dup2(fd_slave, STDOUT_FILENO) == -1)
|| (dup2(fd_slave, STDERR_FILENO) == -1))
abort_child(CHILD_CANNOT_PLUG_OUTPUT);
}
// Strange Linux behaviour ?
// Terminal initialization must be done on STDIN_FILENO after using dup2().
// If it is done on fd_slave, before using dup2(), it sometimes fails with no error.
if (mode & PM_WRITE)
init_child_tty(STDIN_FILENO);
else if (mode & PM_READ)
init_child_tty(STDOUT_FILENO);
/*puts("---------------------------------");
if (stdin_isatty) puts("STDIN is a tty");*/
/*tcgetattr(STDIN_FILENO, &termios_check);
puts(termios_check.c_lflag & ISIG ? "+ISIG" : "-ISIG");
//tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_check);
system("stty icanon");
system("stty -a");
puts("---------------------------------");*/
}
else
{
if (mode & PM_WRITE)
{
close(fdin[1]);
if (dup2(fdin[0], STDIN_FILENO) == -1)
abort_child(CHILD_CANNOT_PLUG_INPUT);
}
if (mode & PM_READ)
{
close(fdout[0]);
close(fderr[0]);
if ((dup2(fdout[1], STDOUT_FILENO) == -1)
|| (dup2(fderr[1], STDERR_FILENO) == -1))
abort_child(CHILD_CANNOT_PLUG_OUTPUT);
}
}
pwd = FALSE;
if (env)
{
char *str;
ch_n = ARRAY_count(env->data);
for (ch_i = 0; ch_i < ch_n; ch_i++)
{
str = ((char **)env->data)[ch_i];
if (putenv(str))
ERROR_warning("cannot set environment string: %s", str);
if (strncmp(str, "PWD=", 4) == 0 && chdir(&str[4]) == 0)
pwd = TRUE;
}
}
// Return to the parent working directory if the PWD environment variable has not been set
if (!pwd)
FILE_chdir(PROJECT_oldcwd);
execv(exec, (char **)argv);
abort_child(CHILD_CANNOT_EXEC);
}
update_stream(process);
#ifdef DEBUG_ME
fprintf(stderr, "run_process: child is OK\n");
#endif
return;
__ABORT_ERRNO:
save_errno = errno;
stop_process(process);
THROW_SYSTEM(save_errno, NULL);
}
void CPROCESS_check(void *_object)
{
#ifdef DEBUG_ME
fprintf(stderr, "CPROCESS_check: %p\n", THIS);
#endif
usleep(100);
wait_child(THIS);
throw_last_child_error();
//fprintf(stderr, "CPROCESS_check: %p END\n", THIS);
}
static void wait_child(CPROCESS *process)
{
int status;
if (wait4(process->pid, &status, WNOHANG, NULL) == process->pid)
{
process->status = status;
_last_status = status;
#ifdef DEBUG_ME
fprintf(stderr, "Process %d has returned %d\n", process->pid, status);
#endif
stop_process(process);
}
}
static void callback_child(int signum, intptr_t data)
{
CPROCESS *process, *next;
//int buffer;
#if 0
for(;;)
{
if (read(fd, (char *)&buffer, 1) == 1)
break;
if (errno != EINTR)
ERROR_panic("Cannot read from SIGCHLD pipe: %s", strerror(errno));
}
#endif
#ifdef DEBUG_ME
fprintf(stderr, ">> callback_child\n");
#endif
for (process = _running_process_list; process; )
{
next = process->next;
wait_child(process);
process = next;
}
throw_last_child_error();
#ifdef DEBUG_ME
fprintf(stderr, "<< callback_child\n");
#endif
}
static void init_child(void)
{
if (_init)
return;
#ifdef DEBUG_ME
fprintf(stderr, "init_child()\n");
#endif
_SIGCHLD_callback = SIGNAL_register(SIGCHLD, callback_child, 0);
_init = TRUE;
}
static void exit_child(void)
{
if (!_init)
return;
#ifdef DEBUG_ME
fprintf(stderr, "exit_child()\n");
#endif
SIGNAL_unregister(SIGCHLD, _SIGCHLD_callback);
_init = FALSE;
}
static CPROCESS *_CPROCESS_create_process;
static void error_CPROCESS_create()
{
OBJECT_UNREF(_CPROCESS_create_process);
}
CPROCESS *CPROCESS_create(int mode, void *cmd, char *name, CARRAY *env)
{
CPROCESS *process;
_CPROCESS_create_process = process = OBJECT_new(CLASS_Process, name, OP ? (OBJECT *)OP : (OBJECT *)CP);
//fprintf(stderr, "CPROCESS_create: %p\n", process);
ON_ERROR(error_CPROCESS_create)
{
init_process(process);
run_process(process, mode, cmd, env);
}
END_ERROR
OBJECT_UNREF_KEEP(process);
if (!name || !*name)
STREAM_blocking(CSTREAM_stream(process), TRUE);
return process;
}
static void error_CPROCESS_wait_for(CPROCESS *process)
{
OBJECT_UNREF(process);
}
void CPROCESS_wait_for(CPROCESS *process, int timeout)
{
int ret;
int sigfd;
#ifdef DEBUG_ME
fprintf(stderr, "Waiting for %d\n", process->pid);
#endif
OBJECT_REF(process);
sigfd = SIGNAL_get_fd();
ON_ERROR_1(error_CPROCESS_wait_for, process)
{
while (process->running)
{
ret = WATCH_process(sigfd, process->out, timeout);
if (ret & WP_OUTPUT)
callback_write(process->out, GB_WATCH_READ, process);
if (ret & WP_END)
SIGNAL_raise_callbacks(sigfd, GB_WATCH_READ, 0);
if (ret & WP_TIMEOUT)
break;
if (ret == 0)
usleep(1000);
}
}
END_ERROR
OBJECT_UNREF(process);
#if 0
{
sigsuspend(&old);
if (!process->running)
break;
#ifdef DEBUG_ME
fprintf(stderr, "Waiting: %d\n", process->running);
#endif
sleep(10);
}
#endif
/*if (have_sigchld)
sigaddset(&old, SIGCHLD);
sigprocmask(SIG_SETMASK, &old, NULL);*/
#ifdef DEBUG_ME
fprintf(stderr, "Waiting for: got it !\n");
#endif
}
//-------------------------------------------------------------------------
BEGIN_METHOD_VOID(Process_exit)
//fprintf(stderr, "Process_exit\n");
while (_running_process_list)
stop_process(_running_process_list);
exit_child();
STRING_free(&CPROCESS_shell);
END_METHOD
BEGIN_METHOD_VOID(Process_new)
init_process(THIS);
END_METHOD
BEGIN_METHOD_VOID(Process_free)
#ifdef DEBUG_ME
fprintf(stderr, "Process_free %p\n", THIS);
#endif
exit_process(THIS);
END_METHOD
BEGIN_PROPERTY(Process_Id)
GB_ReturnInt(THIS->pid);
END_PROPERTY
BEGIN_METHOD_VOID(Process_Kill)
if (!THIS->running)
return;
if (THIS->process_group)
kill(-getpgid(THIS->pid), SIGKILL);
else
kill(THIS->pid, SIGKILL);
//CPROCESS_wait_for(THIS);
END_METHOD
BEGIN_METHOD_VOID(Process_Signal)
if (!THIS->running)
return;
/*
printf("Send SIGUSR1 to process %d\n", THIS->pid);
fflush(NULL);*/
kill(THIS->pid, SIGUSR1);
END_METHOD
#if 0
BEGIN_METHOD(CPROCESS_send, GB_STRING text)
if (!THIS->running || THIS->in < 0)
return;
STREAM_write(&THIS->ob.stream, STRING(text), LENGTH(text));
END_METHOD
#endif
BEGIN_PROPERTY(Process_State)
if (THIS->running)
GB_ReturnInteger(1);
else
{
if (WIFEXITED(THIS->status))
GB_ReturnInteger(0);
else
GB_ReturnInteger(2);
}
END_PROPERTY
BEGIN_PROPERTY(Process_Value)
int status;
if (THIS->running)
{
GB_ReturnInteger(0);
return;
}
status = THIS->status;
if (WIFEXITED(status))
GB_ReturnInteger(WEXITSTATUS(status));
else if (WIFSIGNALED(status))
GB_ReturnInteger(WTERMSIG(status));
else
GB_ReturnInteger(-1);
END_PROPERTY
BEGIN_PROPERTY(Process_LastState)
if (WIFEXITED(_last_status))
GB_ReturnInteger(0);
else
GB_ReturnInteger(2);
END_PROPERTY
BEGIN_PROPERTY(Process_LastValue)
int status;
status = _last_status;
if (WIFEXITED(status))
GB_ReturnInteger(WEXITSTATUS(status));
else if (WIFSIGNALED(status))
GB_ReturnInteger(WTERMSIG(status));
else
GB_ReturnInteger(-1);
END_PROPERTY
BEGIN_METHOD(Process_Wait, GB_FLOAT timeout)
CPROCESS_wait_for(THIS, (int)(VARGOPT(timeout, 0.0) * 1000));
END_METHOD
BEGIN_PROPERTY(Process_Ignore)
if (READ_PROPERTY)
GB_ReturnBoolean(THIS->ignore);
else
{
bool ignore = VPROP(GB_BOOLEAN);
if (THIS->ignore != ignore)
{
THIS->ignore = ignore;
if (ignore)
{
_ignore_process++;
if (_running_process <= _ignore_process)
exit_child();
}
else
{
_ignore_process--;
if (_running_process > _ignore_process)
init_child();
}
}
}
END_METHOD
#endif
GB_DESC NATIVE_Process[] =
{
GB_DECLARE("Process", sizeof(CPROCESS)), GB_NOT_CREATABLE(),
GB_INHERITS("Stream"),
GB_CONSTANT("Stopped", "i", 0),
GB_CONSTANT("Running", "i", 1),
GB_CONSTANT("Crashed", "i", 2),
GB_CONSTANT("Signaled", "i", 2),
GB_STATIC_PROPERTY_READ("LastState", "i", Process_LastState),
GB_STATIC_PROPERTY_READ("LastValue", "i", Process_LastValue),
GB_PROPERTY_READ("Id", "i", Process_Id),
GB_PROPERTY_READ("Handle", "i", Process_Id),
GB_PROPERTY_READ("State", "i", Process_State),
GB_PROPERTY_READ("Value", "i", Process_Value),
GB_STATIC_METHOD("_exit", NULL, Process_exit, NULL),
GB_METHOD("_new", NULL, Process_new, NULL),
GB_METHOD("_free", NULL, Process_free, NULL),
GB_METHOD("Kill", NULL, Process_Kill, NULL),
GB_METHOD("Signal", NULL, Process_Signal, NULL),
GB_METHOD("Wait", NULL, Process_Wait, "[(Timeout)f]"),
GB_PROPERTY("Ignore", "b", Process_Ignore),
GB_EVENT("Read", NULL, NULL, &EVENT_Read),
GB_EVENT("Error", NULL, "(Error)s", &EVENT_Error),
GB_EVENT("Kill", NULL, NULL, &EVENT_Kill),
GB_END_DECLARE
};