Add asynchronous subprocess support.

This adds kore.proc to the python runtime allowing async processing
handling:

The kore.proc method takes the command to run and an optional timeout
parameter in milliseconds. If the process did not exit normally after
that amount of time a TimeoutError exception is raised.

For instance:

async def run(cmd):
	proc = kore.proc(cmd, 1000)

	try:
		await proc.send("hello")
		proc.close_stdin()
	except TimeoutError:
		proc.kill()

	retcode = await proc.reap()

	return retcode
This commit is contained in:
Joris Vink 2018-10-26 19:19:47 +02:00
parent ea7ea48840
commit e2651889e0
6 changed files with 578 additions and 79 deletions

View File

@ -26,6 +26,7 @@
void kore_python_init(void);
void kore_python_cleanup(void);
void kore_python_coro_run(void);
void kore_python_proc_reap(void);
void kore_python_path(const char *);
void kore_python_coro_delete(void *);
void kore_python_log_error(const char *);

View File

@ -20,14 +20,17 @@
struct python_coro {
u_int64_t id;
int state;
int error;
PyObject *obj;
struct pysocket_op *sockop;
struct http_request *request;
PyObject *exception;
char *exception_msg;
TAILQ_ENTRY(python_coro) list;
};
static PyObject *python_kore_log(PyObject *, PyObject *);
static PyObject *python_kore_lock(PyObject *, PyObject *);
static PyObject *python_kore_proc(PyObject *, PyObject *);
static PyObject *python_kore_bind(PyObject *, PyObject *);
static PyObject *python_kore_timer(PyObject *, PyObject *);
static PyObject *python_kore_fatal(PyObject *, PyObject *);
@ -53,6 +56,7 @@ static PyObject *python_websocket_broadcast(PyObject *, PyObject *);
static struct PyMethodDef pykore_methods[] = {
METHOD("log", python_kore_log, METH_VARARGS),
METHOD("lock", python_kore_lock, METH_NOARGS),
METHOD("proc", python_kore_proc, METH_VARARGS),
METHOD("bind", python_kore_bind, METH_VARARGS),
METHOD("timer", python_kore_timer, METH_VARARGS),
METHOD("queue", python_kore_queue, METH_VARARGS),
@ -182,6 +186,7 @@ static PyTypeObject pysocket_type = {
struct pysocket_data {
struct kore_event evt;
int fd;
int eof;
int type;
void *self;
struct python_coro *coro;
@ -344,6 +349,72 @@ static PyTypeObject pylock_op_type = {
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};
struct pyproc {
PyObject_HEAD
pid_t pid;
int reaped;
int status;
struct pysocket *in;
struct pysocket *out;
struct python_coro *coro;
struct kore_timer *timer;
TAILQ_ENTRY(pyproc) list;
};
static void pyproc_dealloc(struct pyproc *);
static PyObject *pyproc_kill(struct pyproc *, PyObject *);
static PyObject *pyproc_reap(struct pyproc *, PyObject *);
static PyObject *pyproc_recv(struct pyproc *, PyObject *);
static PyObject *pyproc_send(struct pyproc *, PyObject *);
static PyObject *pyproc_close_stdin(struct pyproc *, PyObject *);
static PyMethodDef pyproc_methods[] = {
METHOD("kill", pyproc_kill, METH_NOARGS),
METHOD("reap", pyproc_reap, METH_NOARGS),
METHOD("recv", pyproc_recv, METH_VARARGS),
METHOD("send", pyproc_send, METH_VARARGS),
METHOD("close_stdin", pyproc_close_stdin, METH_NOARGS),
METHOD(NULL, NULL, -1),
};
static PyTypeObject pyproc_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "kore.proc",
.tp_doc = "async process",
.tp_methods = pyproc_methods,
.tp_basicsize = sizeof(struct pyproc),
.tp_dealloc = (destructor)pyproc_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};
struct pyproc_op {
PyObject_HEAD
struct pyproc *proc;
};
static void pyproc_op_dealloc(struct pyproc_op *);
static PyObject *pyproc_op_await(PyObject *);
static PyObject *pyproc_op_iternext(struct pyproc_op *);
static PyAsyncMethods pyproc_op_async = {
(unaryfunc)pyproc_op_await,
NULL,
NULL
};
static PyTypeObject pyproc_op_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "kore.proc_op",
.tp_doc = "proc reaper awaitable",
.tp_as_async = &pyproc_op_async,
.tp_iternext = (iternextfunc)pyproc_op_iternext,
.tp_basicsize = sizeof(struct pyproc_op),
.tp_dealloc = (destructor)pyproc_op_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};
struct pyconnection {
PyObject_HEAD
struct connection *c;

View File

@ -135,6 +135,12 @@ kore_connection_accept(struct listener *listener, struct connection **out)
return (KORE_RESULT_ERROR);
}
if (fcntl(c->fd, F_SETFD, FD_CLOEXEC) == -1) {
close(c->fd);
kore_pool_put(&connection_pool, c);
return (KORE_RESULT_ERROR);
}
c->handle = kore_connection_handle;
TAILQ_INSERT_TAIL(&connections, c, list);

View File

@ -21,6 +21,7 @@
#include <sys/socket.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <stdio.h>
#include <netdb.h>
#include <signal.h>
@ -417,6 +418,12 @@ kore_listener_alloc(int family, const char *ccb)
return (NULL);
}
if (fcntl(l->fd, F_SETFD, FD_CLOEXEC) == -1) {
kore_listener_free(l);
kore_log(LOG_ERR, "fcntl(): %s", errno_s);
return (NULL);
}
if (!kore_connection_nonblock(l->fd, family != AF_UNIX)) {
kore_listener_free(l);
kore_log(LOG_ERR, "kore_connection_nonblock(): %s", errno_s);
@ -519,6 +526,8 @@ kore_signal_setup(void)
fatal("sigaction: %s", errno_s);
if (sigaction(SIGUSR1, &sa, NULL) == -1)
fatal("sigaction: %s", errno_s);
if (sigaction(SIGCHLD, &sa, NULL) == -1)
fatal("sigaction: %s", errno_s);
if (foreground) {
if (sigaction(SIGINT, &sa, NULL) == -1)
@ -644,6 +653,9 @@ kore_server_start(int argc, char *argv[])
case SIGUSR1:
kore_worker_dispatch_signal(sig_recv);
break;
case SIGCHLD:
kore_worker_wait(0);
break;
default:
break;
}
@ -651,7 +663,6 @@ kore_server_start(int argc, char *argv[])
sig_recv = 0;
}
kore_worker_wait(0);
kore_platform_event_wait(100);
kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT);
}

View File

@ -17,8 +17,10 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <libgen.h>
#include <signal.h>
#include "kore.h"
#include "http.h"
@ -47,6 +49,7 @@ static void pysocket_evt_handle(void *, int);
static PyObject *pysocket_op_create(struct pysocket *,
int, const void *, size_t);
static struct pysocket *pysocket_alloc(void);
static PyObject *pysocket_async_recv(struct pysocket_op *);
static PyObject *pysocket_async_send(struct pysocket_op *);
static PyObject *pysocket_async_accept(struct pysocket_op *);
@ -55,6 +58,7 @@ static PyObject *pysocket_async_connect(struct pysocket_op *);
static void pylock_do_release(struct pylock *);
static void pytimer_run(void *, u_int64_t);
static void pyproc_timeout(void *, u_int64_t);
static void pysuspend_wakeup(void *, u_int64_t);
#if defined(KORE_USE_PGSQL)
@ -145,6 +149,8 @@ static PyMemAllocatorEx allocator = {
.free = python_free
};
static TAILQ_HEAD(, pyproc) procs;
static struct kore_pool coro_pool;
static struct kore_pool queue_wait_pool;
static struct kore_pool queue_object_pool;
@ -166,6 +172,7 @@ kore_python_init(void)
coro_id = 0;
coro_count = 0;
TAILQ_INIT(&procs);
TAILQ_INIT(&coro_runnable);
TAILQ_INIT(&coro_suspended);
@ -216,9 +223,11 @@ kore_python_coro_run(void)
}
/*
* If something was woken up, let Kore do HTTP processing
* so they run ASAP without having to wait for a tick from
* the event loop.
* Let Kore do HTTP processing so awoken coroutines run asap without
* having to wait for a tick from the event loop.
*
* Maybe it is more beneficial that we track if something related
* to HTTP requests was awoken and only run if true?
*/
http_process();
}
@ -243,11 +252,11 @@ kore_python_coro_delete(void *obj)
kore_pool_put(&coro_pool, coro);
}
/* XXX - Fix this (show error type + traceback). */
void
kore_python_log_error(const char *function)
{
PyObject *type, *value, *traceback;
const char *sval;
PyObject *repr, *type, *value, *traceback;
if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_StopIteration))
return;
@ -259,17 +268,56 @@ kore_python_log_error(const char *function)
return;
}
kore_log(LOG_ERR,
"python exception in '%s' - type:%s - value:%s",
function,
PyUnicode_AsUTF8AndSize(type, NULL),
PyUnicode_AsUTF8AndSize(value, NULL));
if ((repr = PyObject_Repr(value)) == NULL)
sval = "unknown";
else
sval = PyUnicode_AsUTF8(repr);
kore_log(LOG_ERR, "uncaught exception %s in '%s'", sval, function);
Py_XDECREF(repr);
Py_DECREF(type);
Py_DECREF(value);
Py_XDECREF(traceback);
}
void
kore_python_proc_reap(void)
{
struct pyproc *proc;
pid_t child;
int status;
if ((child = waitpid(-1, &status, 0)) == -1) {
if (errno == ECHILD)
return;
kore_log(LOG_NOTICE, "waitpid: %s", errno_s);
return;
}
proc = NULL;
TAILQ_FOREACH(proc, &procs, list) {
if (proc->pid == child)
break;
}
if (proc == NULL) {
kore_log(LOG_NOTICE, "SIGCHLD for unknown proc (%d)", child);
return;
}
proc->pid = -1;
proc->reaped = 1;
proc->status = status;
if (proc->coro->request != NULL)
http_request_wakeup(proc->coro->request);
else
python_coro_wakeup(proc->coro);
}
static void *
python_malloc(void *ctx, size_t len)
{
@ -342,8 +390,11 @@ python_coro_create(PyObject *obj, struct http_request *req)
coro = kore_pool_get(&coro_pool);
coro_count++;
coro->sockop = NULL;
coro->exception = NULL;
coro->exception_msg = NULL;
coro->obj = obj;
coro->error = 0;
coro->request = req;
coro->id = coro_id++;
coro->state = CORO_STATE_RUNNABLE;
@ -369,9 +420,6 @@ python_coro_run(struct python_coro *coro)
for (;;) {
PyErr_Clear();
if (coro->error)
PyErr_SetString(PyExc_RuntimeError, "i/o error");
item = _PyGen_Send((PyGenObject *)coro->obj, NULL);
if (item == NULL) {
kore_python_log_error("coroutine");
@ -721,6 +769,7 @@ python_module_init(void)
if ((pykore = PyModule_Create(&pykore_module)) == NULL)
fatal("python_module_init: failed to setup pykore module");
python_push_type("pyproc", pykore, &pyproc_type);
python_push_type("pylock", pykore, &pylock_type);
python_push_type("pytimer", pykore, &pytimer_type);
python_push_type("pyqueue", pykore, &pyqueue_type);
@ -875,7 +924,7 @@ python_kore_socket_wrap(PyObject *self, PyObject *args)
if ((pyproto = PyObject_GetAttrString(pysock, "proto")) == NULL)
goto out;
if ((sock = PyObject_New(struct pysocket, &pysocket_type)) == NULL)
if ((sock = pysocket_alloc()) == NULL)
goto out;
sock->socket = pysock;
@ -965,22 +1014,22 @@ python_kore_fatalx(PyObject *self, PyObject *args)
static PyObject *
python_kore_suspend(PyObject *self, PyObject *args)
{
struct pysuspend_op *sop;
struct pysuspend_op *op;
int delay;
if (!PyArg_ParseTuple(args, "i", &delay))
return (NULL);
sop = PyObject_New(struct pysuspend_op, &pysuspend_op_type);
if (sop == NULL)
op = PyObject_New(struct pysuspend_op, &pysuspend_op_type);
if (op == NULL)
return (NULL);
sop->timer = NULL;
sop->delay = delay;
sop->coro = coro_running;
sop->state = PYSUSPEND_OP_INIT;
op->timer = NULL;
op->delay = delay;
op->coro = coro_running;
op->state = PYSUSPEND_OP_INIT;
return ((PyObject *)sop);
return ((PyObject *)op);
}
static PyObject *
@ -1020,6 +1069,108 @@ python_kore_timer(PyObject *self, PyObject *args)
return ((PyObject *)timer);
}
static PyObject *
python_kore_proc(PyObject *self, PyObject *args)
{
const char *cmd;
struct pyproc *proc;
char *copy, *argv[30];
int timeo, in_pipe[2], out_pipe[2];
timeo = -1;
if (coro_running == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"kore.proc only available in coroutines");
return (NULL);
}
if (!PyArg_ParseTuple(args, "s|i", &cmd, &timeo))
return (NULL);
if (pipe(in_pipe) == -1) {
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
}
if (pipe(out_pipe) == -1) {
close(in_pipe[0]);
close(in_pipe[1]);
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
}
if ((proc = PyObject_New(struct pyproc, &pyproc_type)) == NULL) {
close(in_pipe[0]);
close(in_pipe[1]);
close(out_pipe[0]);
close(out_pipe[1]);
return (NULL);
}
proc->pid = -1;
proc->reaped = 0;
proc->status = 0;
proc->timer = NULL;
proc->coro = coro_running;
proc->in = pysocket_alloc();
proc->out = pysocket_alloc();
if (proc->in == NULL || proc->out == NULL) {
Py_DECREF((PyObject *)proc);
return (NULL);
}
TAILQ_INSERT_TAIL(&procs, proc, list);
proc->pid = fork();
if (proc->pid == -1) {
if (errno == ENOSYS) {
Py_DECREF((PyObject *)proc);
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
}
fatal("python_kore_proc: fork(): %s", errno_s);
}
if (proc->pid == 0) {
close(in_pipe[1]);
close(out_pipe[0]);
if (dup2(out_pipe[1], STDOUT_FILENO) == -1 ||
dup2(out_pipe[1], STDERR_FILENO) == -1 ||
dup2(in_pipe[0], STDIN_FILENO) == -1)
fatal("dup2: %s", errno_s);
copy = kore_strdup(cmd);
kore_split_string(copy, " ", argv, 30);
(void)execve(argv[0], argv, NULL);
printf("kore.proc failed to execute %s (%s)\n",
argv[0], errno_s);
exit(1);
}
close(in_pipe[0]);
close(out_pipe[1]);
if (!kore_connection_nonblock(in_pipe[1], 0) ||
!kore_connection_nonblock(out_pipe[0], 0)) {
Py_DECREF((PyObject *)proc);
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
}
proc->in->fd = in_pipe[1];
proc->out->fd = out_pipe[0];
if (timeo != -1) {
proc->timer = kore_timer_add(pyproc_timeout,
timeo, proc, KORE_TIMER_ONESHOT);
}
return ((PyObject *)proc);
}
static PyObject *
python_import(const char *path)
{
@ -1175,14 +1326,14 @@ pytimer_close(struct pytimer *timer, PyObject *args)
}
static void
pysuspend_op_dealloc(struct pysuspend_op *sop)
pysuspend_op_dealloc(struct pysuspend_op *op)
{
if (sop->timer != NULL) {
kore_timer_remove(sop->timer);
sop->timer = NULL;
if (op->timer != NULL) {
kore_timer_remove(op->timer);
op->timer = NULL;
}
PyObject_Del((PyObject *)sop);
PyObject_Del((PyObject *)op);
}
static PyObject *
@ -1193,13 +1344,13 @@ pysuspend_op_await(PyObject *sop)
}
static PyObject *
pysuspend_op_iternext(struct pysuspend_op *sop)
pysuspend_op_iternext(struct pysuspend_op *op)
{
switch (sop->state) {
switch (op->state) {
case PYSUSPEND_OP_INIT:
sop->timer = kore_timer_add(pysuspend_wakeup, sop->delay,
sop, KORE_TIMER_ONESHOT);
sop->state = PYSUSPEND_OP_WAIT;
op->timer = kore_timer_add(pysuspend_wakeup, op->delay,
op, KORE_TIMER_ONESHOT);
op->state = PYSUSPEND_OP_WAIT;
break;
case PYSUSPEND_OP_WAIT:
break;
@ -1207,7 +1358,7 @@ pysuspend_op_iternext(struct pysuspend_op *sop)
PyErr_SetNone(PyExc_StopIteration);
return (NULL);
default:
fatal("unknown state %d for pysuspend_op", sop->state);
fatal("unknown state %d for pysuspend_op", op->state);
}
Py_RETURN_NONE;
@ -1216,20 +1367,43 @@ pysuspend_op_iternext(struct pysuspend_op *sop)
static void
pysuspend_wakeup(void *arg, u_int64_t now)
{
struct pysuspend_op *sop = arg;
struct pysuspend_op *op = arg;
sop->timer = NULL;
sop->state = PYSUSPEND_OP_CONTINUE;
op->timer = NULL;
op->state = PYSUSPEND_OP_CONTINUE;
if (sop->coro->request != NULL)
http_request_wakeup(sop->coro->request);
if (op->coro->request != NULL)
http_request_wakeup(op->coro->request);
else
python_coro_wakeup(sop->coro);
python_coro_wakeup(op->coro);
}
static struct pysocket *
pysocket_alloc(void)
{
struct pysocket *sock;
if ((sock = PyObject_New(struct pysocket, &pysocket_type)) == NULL)
return (NULL);
sock->fd = -1;
sock->family = -1;
sock->protocol = -1;
sock->socket = NULL;
return (sock);
}
static void
pysocket_dealloc(struct pysocket *sock)
{
if (sock->socket != NULL) {
(void)PyObject_CallMethod(sock->socket, "close", NULL);
Py_DECREF(sock->socket);
} else if (sock->fd != -1) {
(void)close(sock->fd);
}
PyObject_Del((PyObject *)sock);
}
@ -1317,8 +1491,10 @@ pysocket_close(struct pysocket *sock, PyObject *args)
if (sock->socket != NULL) {
(void)PyObject_CallMethod(sock->socket, "close", NULL);
Py_DECREF(sock->socket);
sock->socket = NULL;
} else if (sock->fd != -1) {
(void)close(sock->fd);
sock->fd = -1;
}
Py_RETURN_TRUE;
@ -1349,6 +1525,7 @@ pysocket_op_dealloc(struct pysocket_op *op)
op->data.type == PYSOCKET_TYPE_SEND)
kore_buf_cleanup(&op->data.buffer);
op->data.coro->sockop = NULL;
Py_DECREF(op->data.socket);
PyObject_Del((PyObject *)op);
@ -1359,6 +1536,9 @@ pysocket_op_create(struct pysocket *sock, int type, const void *ptr, size_t len)
{
struct pysocket_op *op;
if (coro_running->sockop != NULL)
fatal("pysocket_op_create: coro has active socketop");
op = PyObject_New(struct pysocket_op, &pysocket_op_type);
if (op == NULL)
return (NULL);
@ -1376,6 +1556,7 @@ pysocket_op_create(struct pysocket *sock, int type, const void *ptr, size_t len)
op->data.fd = sock->fd;
#endif
op->data.eof = 0;
op->data.self = op;
op->data.type = type;
op->data.socket = sock;
@ -1384,6 +1565,7 @@ pysocket_op_create(struct pysocket *sock, int type, const void *ptr, size_t len)
op->data.evt.type = KORE_TYPE_PYSOCKET;
op->data.evt.handle = pysocket_evt_handle;
coro_running->sockop = op;
Py_INCREF(op->data.socket);
switch (type) {
@ -1426,6 +1608,24 @@ pysocket_op_iternext(struct pysocket_op *op)
{
PyObject *ret;
if (op->data.eof) {
if (op->data.coro->exception != NULL) {
PyErr_SetString(op->data.coro->exception,
op->data.coro->exception_msg);
op->data.coro->exception = NULL;
return (NULL);
}
if (op->data.type != PYSOCKET_TYPE_RECV) {
PyErr_SetString(PyExc_RuntimeError, "socket EOF");
return (NULL);
}
PyErr_SetNone(PyExc_StopIteration);
return (NULL);
}
switch (op->data.type) {
case PYSOCKET_TYPE_CONNECT:
ret = pysocket_async_connect(op);
@ -1473,7 +1673,7 @@ pysocket_async_accept(struct pysocket_op *op)
int fd;
struct pysocket *sock;
if ((sock = PyObject_New(struct pysocket, &pysocket_type)) == NULL)
if ((sock = pysocket_alloc() ) == NULL)
return (NULL);
sock->addr_len = sizeof(sock->addr);
@ -1516,14 +1716,21 @@ pysocket_async_recv(struct pysocket_op *op)
Py_RETURN_NONE;
}
ret = read(op->data.fd, op->data.buffer.data, op->data.buffer.length);
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
op->data.evt.flags &= ~KORE_EVENT_READ;
Py_RETURN_NONE;
for (;;) {
ret = read(op->data.fd, op->data.buffer.data,
op->data.buffer.length);
if (ret == -1) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
op->data.evt.flags &= ~KORE_EVENT_READ;
Py_RETURN_NONE;
}
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
}
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
break;
}
if (ret == 0) {
@ -1551,15 +1758,21 @@ pysocket_async_send(struct pysocket_op *op)
Py_RETURN_NONE;
}
ret = write(op->data.fd, op->data.buffer.data + op->data.buffer.offset,
op->data.buffer.length - op->data.buffer.offset);
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
op->data.evt.flags &= ~KORE_EVENT_WRITE;
Py_RETURN_NONE;
for (;;) {
ret = write(op->data.fd,
op->data.buffer.data + op->data.buffer.offset,
op->data.buffer.length - op->data.buffer.offset);
if (ret == -1) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
op->data.evt.flags &= ~KORE_EVENT_WRITE;
Py_RETURN_NONE;
}
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
}
PyErr_SetString(PyExc_RuntimeError, errno_s);
return (NULL);
break;
}
op->data.buffer.offset += (size_t)ret;
@ -1573,11 +1786,14 @@ pysocket_async_send(struct pysocket_op *op)
}
static void
pysocket_evt_handle(void *arg, int error)
pysocket_evt_handle(void *arg, int eof)
{
struct pysocket_data *data = arg;
struct python_coro *coro = data->coro;
if (coro->sockop == NULL)
fatal("pysocket_evt_handle: sockop == NULL");
/*
* If we are a coroutine tied to an HTTP request wake-up the
* HTTP request instead. That in turn will wakeup the coro and
@ -1590,7 +1806,7 @@ pysocket_evt_handle(void *arg, int error)
else
python_coro_wakeup(coro);
coro->error = error;
coro->sockop->data.eof = eof;
}
static void
@ -1880,6 +2096,195 @@ pylock_op_iternext(struct pylock_op *op)
return (NULL);
}
static void
pyproc_timeout(void *arg, u_int64_t now)
{
struct pyproc *proc = arg;
proc->timer = NULL;
if (proc->coro->sockop != NULL)
proc->coro->sockop->data.eof = 1;
proc->coro->exception = PyExc_TimeoutError;
proc->coro->exception_msg = "timeout before process exited";
if (proc->coro->request != NULL)
http_request_wakeup(proc->coro->request);
else
python_coro_wakeup(proc->coro);
}
static void
pyproc_dealloc(struct pyproc *proc)
{
int status;
TAILQ_REMOVE(&procs, proc, list);
if (proc->timer != NULL) {
kore_timer_remove(proc->timer);
proc->timer = NULL;
}
if (proc->pid != -1) {
if (kill(proc->pid, SIGKILL) == -1) {
kore_log(LOG_NOTICE,
"kore.proc failed to send SIGKILL %d (%s)",
proc->pid, errno_s);
}
for (;;) {
if (waitpid(proc->pid, &status, 0) == -1) {
if (errno == EINTR)
continue;
kore_log(LOG_NOTICE,
"kore.proc failed to wait for %d (%s)",
proc->pid, errno_s);
}
break;
}
}
if (proc->in != NULL) {
Py_DECREF((PyObject *)proc->in);
proc->in = NULL;
}
if (proc->out != NULL) {
Py_DECREF((PyObject *)proc->out);
proc->out = NULL;
}
PyObject_Del((PyObject *)proc);
}
static PyObject *
pyproc_kill(struct pyproc *proc, PyObject *args)
{
if (proc->pid != -1 && kill(proc->pid, SIGKILL) == -1)
kore_log(LOG_NOTICE, "kill(%d): %s", proc->pid, errno_s);
Py_RETURN_TRUE;
}
static PyObject *
pyproc_reap(struct pyproc *proc, PyObject *args)
{
struct pyproc_op *op;
if (proc->timer != NULL) {
kore_timer_remove(proc->timer);
proc->timer = NULL;
}
if ((op = PyObject_New(struct pyproc_op, &pyproc_op_type)) == NULL)
return (NULL);
op->proc = proc;
Py_INCREF((PyObject *)proc);
return ((PyObject *)op);
}
static PyObject *
pyproc_recv(struct pyproc *proc, PyObject *args)
{
Py_ssize_t len;
if (proc->out == NULL) {
PyErr_SetString(PyExc_RuntimeError, "stdout closed");
return (NULL);
}
if (!PyArg_ParseTuple(args, "n", &len))
return (NULL);
return (pysocket_op_create(proc->out, PYSOCKET_TYPE_RECV, NULL, len));
}
static PyObject *
pyproc_send(struct pyproc *proc, PyObject *args)
{
Py_buffer buf;
PyObject *ret;
if (proc->in == NULL) {
PyErr_SetString(PyExc_RuntimeError, "stdin closed");
return (NULL);
}
if (!PyArg_ParseTuple(args, "y*", &buf))
return (NULL);
ret = pysocket_op_create(proc->in,
PYSOCKET_TYPE_SEND, buf.buf, buf.len);
return (ret);
}
static PyObject *
pyproc_close_stdin(struct pyproc *proc, PyObject *args)
{
if (proc->in != NULL) {
Py_DECREF((PyObject *)proc->in);
proc->in = NULL;
}
Py_RETURN_TRUE;
}
static void
pyproc_op_dealloc(struct pyproc_op *op)
{
Py_DECREF((PyObject *)op->proc);
PyObject_Del((PyObject *)op);
}
static PyObject *
pyproc_op_await(PyObject *sop)
{
Py_INCREF(sop);
return (sop);
}
static PyObject *
pyproc_op_iternext(struct pyproc_op *op)
{
int ret;
PyObject *res;
if (op->proc->coro->exception != NULL) {
PyErr_SetString(op->proc->coro->exception,
op->proc->coro->exception_msg);
op->proc->coro->exception = NULL;
return (NULL);
}
if (op->proc->reaped == 0)
Py_RETURN_NONE;
if (WIFSTOPPED(op->proc->status)) {
op->proc->reaped = 0;
Py_RETURN_NONE;
}
if (WIFEXITED(op->proc->status)) {
ret = WEXITSTATUS(op->proc->status);
} else {
ret = op->proc->status;
}
if ((res = PyLong_FromLong(ret)) == NULL)
return (NULL);
PyErr_SetObject(PyExc_StopIteration, res);
Py_DECREF(res);
return (NULL);
}
static PyObject *
pyhttp_request_alloc(const struct http_request *req)
{

View File

@ -90,8 +90,8 @@ extern volatile sig_atomic_t sig_recv;
struct kore_worker *worker = NULL;
u_int8_t worker_set_affinity = 1;
u_int32_t worker_accept_threshold = 0;
u_int32_t worker_rlimit_nofiles = 1024;
u_int32_t worker_max_connections = 250;
u_int32_t worker_rlimit_nofiles = 768;
u_int32_t worker_max_connections = 512;
u_int32_t worker_active_connections = 0;
void
@ -379,23 +379,6 @@ kore_worker_entry(struct kore_worker *kw)
worker->restarted = 0;
for (;;) {
if (sig_recv != 0) {
switch (sig_recv) {
case SIGHUP:
kore_module_reload(1);
break;
case SIGQUIT:
case SIGINT:
case SIGTERM:
quit = 1;
break;
default:
break;
}
sig_recv = 0;
}
netwait = 100;
now = kore_time_ms();
@ -446,6 +429,28 @@ kore_worker_entry(struct kore_worker *kw)
}
}
if (sig_recv != 0) {
switch (sig_recv) {
case SIGHUP:
kore_module_reload(1);
break;
case SIGQUIT:
case SIGINT:
case SIGTERM:
quit = 1;
break;
case SIGCHLD:
#if defined(KORE_USE_PYTHON)
kore_python_proc_reap();
#endif
break;
default:
break;
}
sig_recv = 0;
}
#if !defined(KORE_NO_HTTP)
http_process();
#endif