542 lines
13 KiB
C
542 lines
13 KiB
C
|
/*
|
||
|
* mooapp/mooappinput.c
|
||
|
*
|
||
|
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
||
|
*
|
||
|
* 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 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* See COPYING file that comes with this distribution.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include <Python.h>
|
||
|
|
||
|
#ifdef __WIN32__
|
||
|
#include <windows.h>
|
||
|
#include <io.h>
|
||
|
#endif /* __WIN32__ */
|
||
|
|
||
|
#ifdef HAVE_FCNTL_H
|
||
|
#include <fcntl.h>
|
||
|
#endif /* HAVE_FCNTL_H */
|
||
|
#ifdef HAVE_ERRNO_H
|
||
|
#include <errno.h>
|
||
|
#endif /* HAVE_ERRNO_H */
|
||
|
#ifdef HAVE_SYS_TYPES_H
|
||
|
#include <sys/types.h>
|
||
|
#endif /* HAVE_SYS_TYPES_H */
|
||
|
#ifdef HAVE_SYS_STAT_H
|
||
|
#include <sys/stat.h>
|
||
|
#endif /* HAVE_SYS_STAT_H */
|
||
|
#ifdef HAVE_UNISTD_H
|
||
|
#include <unistd.h>
|
||
|
#endif /* HAVE_UNISTD_H */
|
||
|
#ifdef HAVE_STDLIB_H
|
||
|
#include <stdlib.h>
|
||
|
#endif /* HAVE_STDLIB_H */
|
||
|
#ifdef HAVE_SYS_POLL_H
|
||
|
#include <sys/poll.h>
|
||
|
#endif /* HAVE_SYS_POLL_H */
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include "mooapp/mooappinput.h"
|
||
|
|
||
|
|
||
|
#define MAX_BUFFER_SIZE 4096
|
||
|
|
||
|
|
||
|
/*********************************************************************/
|
||
|
/*********************************************************************/
|
||
|
|
||
|
static gboolean read_input (GIOChannel *source,
|
||
|
GIOCondition condition,
|
||
|
MooAppInput *ch);
|
||
|
static void commit (MooAppInput *ch);
|
||
|
|
||
|
|
||
|
MooAppInput *moo_app_input_new (MooPython *python,
|
||
|
const char *pipe_basename)
|
||
|
{
|
||
|
MooAppInput *ch;
|
||
|
|
||
|
g_return_val_if_fail (python != NULL && pipe_basename != NULL, NULL);
|
||
|
|
||
|
ch = g_new0 (MooAppInput, 1);
|
||
|
|
||
|
ch->python = python;
|
||
|
ch->pipe_basename = g_strdup (pipe_basename);
|
||
|
|
||
|
#ifdef __WIN32__
|
||
|
ch->listener = NULL;
|
||
|
#else /* ! __WIN32__ */
|
||
|
ch->pipe = -1;
|
||
|
#endif /* ! __WIN32__ */
|
||
|
ch->pipe_name = NULL;
|
||
|
ch->io = NULL;
|
||
|
ch->io_watch = 0;
|
||
|
ch->python = NULL;
|
||
|
ch->ready = FALSE;
|
||
|
ch->buffer = g_byte_array_new ();
|
||
|
|
||
|
return ch;
|
||
|
}
|
||
|
|
||
|
|
||
|
MooAppInput *moo_app_input_ref (MooAppInput *ch)
|
||
|
{
|
||
|
g_return_val_if_fail (ch != NULL, NULL);
|
||
|
++ch->ref_count;
|
||
|
return ch;
|
||
|
}
|
||
|
|
||
|
|
||
|
void moo_app_input_unref (MooAppInput *ch)
|
||
|
{
|
||
|
g_return_if_fail (ch != NULL);
|
||
|
|
||
|
if (--ch->ref_count)
|
||
|
return;
|
||
|
|
||
|
moo_app_input_shutdown (ch);
|
||
|
|
||
|
g_byte_array_free (ch->buffer, TRUE);
|
||
|
g_free (ch->pipe_basename);
|
||
|
g_free (ch);
|
||
|
}
|
||
|
|
||
|
|
||
|
void moo_app_input_shutdown (MooAppInput *ch)
|
||
|
{
|
||
|
g_return_if_fail (ch != NULL);
|
||
|
|
||
|
#ifdef __WIN32__
|
||
|
if (ch->listener) {
|
||
|
CloseHandle (ch->listener);
|
||
|
ch->listener = NULL;
|
||
|
}
|
||
|
#endif /* __WIN32__ */
|
||
|
if (ch->io) {
|
||
|
g_io_channel_shutdown (ch->io, TRUE, NULL);
|
||
|
g_io_channel_unref (ch->io);
|
||
|
ch->io = NULL;
|
||
|
}
|
||
|
if (ch->pipe_name) {
|
||
|
#ifndef __WIN32__
|
||
|
ch->pipe = -1;
|
||
|
unlink (ch->pipe_name);
|
||
|
#endif /* ! __WIN32__ */
|
||
|
g_free (ch->pipe_name);
|
||
|
ch->pipe_name = NULL;
|
||
|
}
|
||
|
if (ch->io_watch) {
|
||
|
g_source_remove (ch->io_watch);
|
||
|
ch->io_watch = 0;
|
||
|
}
|
||
|
ch->ready = FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void commit (MooAppInput *ch)
|
||
|
{
|
||
|
PyObject *res;
|
||
|
|
||
|
g_byte_array_append (ch->buffer, "0", 1);
|
||
|
res = moo_python_run_string (ch->python,
|
||
|
ch->buffer->data,
|
||
|
strlen (ch->buffer->data));
|
||
|
|
||
|
if (res)
|
||
|
Py_DECREF (res);
|
||
|
else
|
||
|
PyErr_Print ();
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************/
|
||
|
/* WIN32
|
||
|
*/
|
||
|
#ifdef __WIN32__
|
||
|
|
||
|
|
||
|
typedef struct {
|
||
|
char *in;
|
||
|
int out;
|
||
|
} ListenerInfo;
|
||
|
|
||
|
static ListenerInfo *listener_info_new (const char *input, int output)
|
||
|
{
|
||
|
ListenerInfo *info = g_new (ListenerInfo, 1);
|
||
|
info->in = g_strdup (input);
|
||
|
info->out = output;
|
||
|
return info;
|
||
|
}
|
||
|
|
||
|
static void listener_info_free (ListenerInfo *info)
|
||
|
{
|
||
|
if (!info) return;
|
||
|
g_free (info->in);
|
||
|
g_free (info);
|
||
|
}
|
||
|
|
||
|
static DWORD WINAPI listener_main (ListenerInfo *info);
|
||
|
|
||
|
|
||
|
gboolean moo_app_input_start (MooAppInput *ch)
|
||
|
{
|
||
|
int listener_pipe[2] = {-1, -1};
|
||
|
DWORD id;
|
||
|
ListenerInfo *info;
|
||
|
|
||
|
g_return_val_if_fail (ch != NULL && !ch->ready, FALSE);
|
||
|
|
||
|
g_free (ch->pipe_name);
|
||
|
ch->pipe_name =
|
||
|
g_strdup_printf ("\\\\.\\pipe\\%s_in_%ld",
|
||
|
ch->pipe_basename,
|
||
|
GetCurrentProcessId());
|
||
|
|
||
|
if (_pipe (listener_pipe, 4096, _O_BINARY | _O_NOINHERIT))
|
||
|
{
|
||
|
g_critical ("%s: could not create listener pipe", G_STRLOC);
|
||
|
g_free (ch->pipe_name);
|
||
|
ch->pipe_name = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
info = listener_info_new (ch->pipe_name, listener_pipe[1]);
|
||
|
ch->listener = CreateThread (NULL, 0,
|
||
|
(LPTHREAD_START_ROUTINE) listener_main,
|
||
|
info, 0, &id);
|
||
|
if (!ch->listener)
|
||
|
{
|
||
|
g_critical ("%s: could not start listener thread", G_STRLOC);
|
||
|
listener_info_free (info);
|
||
|
g_free (ch->pipe_name);
|
||
|
ch->pipe_name = NULL;
|
||
|
close (listener_pipe[0]);
|
||
|
close (listener_pipe[1]);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ch->io = g_io_channel_win32_new_fd (listener_pipe[0]);
|
||
|
g_io_channel_set_encoding (ch->io, NULL, NULL);
|
||
|
g_io_channel_set_buffered (ch->io, FALSE);
|
||
|
ch->io_watch = g_io_add_watch (ch->io, G_IO_IN | G_IO_PRI,
|
||
|
(GIOFunc) read_input, ch);
|
||
|
|
||
|
ch->ready = TRUE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static gboolean read_input (GIOChannel *source,
|
||
|
GIOCondition condition,
|
||
|
MooAppInput *self)
|
||
|
{
|
||
|
gboolean error_occured = FALSE;
|
||
|
GError *err = NULL;
|
||
|
gboolean again = TRUE;
|
||
|
gboolean endline = FALSE;
|
||
|
char c;
|
||
|
guint bytes_read;
|
||
|
|
||
|
if (condition & (G_IO_ERR | G_IO_HUP))
|
||
|
if (errno != EAGAIN)
|
||
|
error_occured = TRUE;
|
||
|
|
||
|
g_io_channel_read_chars (source, &c, 1, &bytes_read, &err);
|
||
|
if (bytes_read == 1)
|
||
|
{
|
||
|
if (c != '\r')
|
||
|
g_byte_array_append (self->buffer, &c, 1);
|
||
|
|
||
|
if (c == '\n')
|
||
|
{
|
||
|
endline = TRUE;
|
||
|
again = FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
again = FALSE;
|
||
|
}
|
||
|
|
||
|
while (again && !error_occured && !err)
|
||
|
{
|
||
|
if (g_io_channel_get_buffer_condition (source) & G_IO_IN) {
|
||
|
g_io_channel_read_chars (source, &c, 1, &bytes_read, &err);
|
||
|
if (bytes_read == 1)
|
||
|
{
|
||
|
if (c != '\r')
|
||
|
g_byte_array_append (self->buffer, &c, 1);
|
||
|
|
||
|
if (c == '\n') {
|
||
|
endline = TRUE;
|
||
|
again = FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
again = FALSE;
|
||
|
}
|
||
|
else
|
||
|
again = FALSE;
|
||
|
}
|
||
|
|
||
|
if (error_occured || err)
|
||
|
{
|
||
|
g_critical ("%s: error", G_STRLOC);
|
||
|
if (err) {
|
||
|
g_critical ("%s: %s", G_STRLOC, err->message);
|
||
|
g_error_free (err);
|
||
|
}
|
||
|
moo_app_input_shutdown (self);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (endline)
|
||
|
{
|
||
|
commit (self);
|
||
|
|
||
|
if (self->buffer->len > MAX_BUFFER_SIZE) {
|
||
|
g_byte_array_free (self->buffer, TRUE);
|
||
|
self->buffer = g_byte_array_new ();
|
||
|
}
|
||
|
else
|
||
|
g_byte_array_set_size (self->buffer, 0);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static DWORD WINAPI listener_main (ListenerInfo *info)
|
||
|
{
|
||
|
char *pipe_name;
|
||
|
int output;
|
||
|
HANDLE input;
|
||
|
|
||
|
pipe_name = g_strdup (info->in);
|
||
|
output = info->out;
|
||
|
listener_info_free (info);
|
||
|
|
||
|
g_message ("%s: hi there", G_STRLOC);
|
||
|
|
||
|
input = CreateNamedPipe (pipe_name,
|
||
|
PIPE_ACCESS_DUPLEX,
|
||
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
||
|
PIPE_UNLIMITED_INSTANCES,
|
||
|
0, 0, 200, NULL);
|
||
|
|
||
|
if (input == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
g_critical ("%s: could not create input pipe", G_STRLOC);
|
||
|
g_free (pipe_name);
|
||
|
return 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_message ("%s: opened pipe %s", G_STRLOC, pipe_name);
|
||
|
}
|
||
|
|
||
|
while (TRUE)
|
||
|
{
|
||
|
DWORD bytes_read;
|
||
|
char c;
|
||
|
|
||
|
DisconnectNamedPipe (input);
|
||
|
g_message ("%s: opening connection", G_STRLOC);
|
||
|
|
||
|
if (!ConnectNamedPipe (input, NULL))
|
||
|
{
|
||
|
DWORD err = GetLastError();
|
||
|
if (err != ERROR_PIPE_CONNECTED)
|
||
|
{
|
||
|
g_critical ("%s: error in ConnectNamedPipe()", G_STRLOC);
|
||
|
CloseHandle (input);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_message ("%s: client connected", G_STRLOC);
|
||
|
|
||
|
while (ReadFile (input, &c, 1, &bytes_read, NULL))
|
||
|
{
|
||
|
if (bytes_read == 1)
|
||
|
{
|
||
|
if (_write (output, &c, 1) != 1)
|
||
|
{
|
||
|
g_message ("%s: parent disconnected", G_STRLOC);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_message ("%s: client disconnected", G_STRLOC);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_message ("%s: goodbye", G_STRLOC);
|
||
|
|
||
|
CloseHandle (input);
|
||
|
g_free (pipe_name);
|
||
|
close (output);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif /* __WIN32__ */
|
||
|
|
||
|
|
||
|
/****************************************************************************/
|
||
|
/* UNIX
|
||
|
*/
|
||
|
#ifndef __WIN32__
|
||
|
|
||
|
gboolean moo_app_input_start (MooAppInput *ch)
|
||
|
{
|
||
|
g_return_val_if_fail (!ch->ready, FALSE);
|
||
|
|
||
|
ch->pipe_name =
|
||
|
g_strdup_printf ("%s/%s_in.%d",
|
||
|
g_get_tmp_dir(),
|
||
|
ch->pipe_basename,
|
||
|
getpid ());
|
||
|
unlink (ch->pipe_name);
|
||
|
|
||
|
if (mkfifo (ch->pipe_name, S_IRUSR | S_IWUSR))
|
||
|
{
|
||
|
int err = errno;
|
||
|
g_critical ("%s: error in mkfifo()", G_STRLOC);
|
||
|
g_critical ("%s: %s", G_STRLOC, g_strerror (err));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ch->pipe = open (ch->pipe_name, O_RDWR | O_NONBLOCK);
|
||
|
if (ch->pipe == -1)
|
||
|
{
|
||
|
int err = errno;
|
||
|
g_critical ("%s: error in open()", G_STRLOC);
|
||
|
g_critical ("%s: %s", G_STRLOC, g_strerror (err));
|
||
|
g_free (ch->pipe_name);
|
||
|
ch->pipe_name = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ch->io = g_io_channel_unix_new (ch->pipe);
|
||
|
g_io_channel_set_encoding (ch->io, NULL, NULL);
|
||
|
ch->io_watch = g_io_add_watch (ch->io,
|
||
|
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
|
||
|
(GIOFunc) read_input,
|
||
|
ch);
|
||
|
|
||
|
ch->ready = TRUE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static gboolean read_input (G_GNUC_UNUSED GIOChannel *source,
|
||
|
GIOCondition condition,
|
||
|
MooAppInput *self)
|
||
|
{
|
||
|
gboolean error_occured = FALSE;
|
||
|
GError *err = NULL;
|
||
|
gboolean again = TRUE;
|
||
|
gboolean endline = FALSE;
|
||
|
|
||
|
if (condition & (G_IO_ERR | G_IO_HUP))
|
||
|
if (errno != EINTR && errno != EAGAIN)
|
||
|
error_occured = TRUE;
|
||
|
|
||
|
while (again && !error_occured && !err)
|
||
|
{
|
||
|
char c;
|
||
|
int bytes_read;
|
||
|
|
||
|
struct pollfd fd = {self->pipe, POLLIN | POLLPRI, 0};
|
||
|
int res = poll (&fd, 1, 0);
|
||
|
|
||
|
switch (res) {
|
||
|
case -1:
|
||
|
if (errno != EINTR && errno != EAGAIN)
|
||
|
error_occured = TRUE;
|
||
|
perror ("poll");
|
||
|
break;
|
||
|
|
||
|
case 0:
|
||
|
again = FALSE;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
if (fd.revents & (POLLERR))
|
||
|
{
|
||
|
if (errno != EINTR && errno != EAGAIN)
|
||
|
error_occured = TRUE;
|
||
|
perror ("poll");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bytes_read = read (self->pipe, &c, 1);
|
||
|
if (bytes_read == 1)
|
||
|
{
|
||
|
g_byte_array_append (self->buffer, &c, 1);
|
||
|
if (c == '\n')
|
||
|
{
|
||
|
endline = TRUE;
|
||
|
again = FALSE;
|
||
|
}
|
||
|
}
|
||
|
else if (bytes_read == -1)
|
||
|
{
|
||
|
perror ("read");
|
||
|
if (errno != EINTR && errno != EAGAIN)
|
||
|
error_occured = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
again = FALSE;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
g_assert_not_reached ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (error_occured || err)
|
||
|
{
|
||
|
g_critical ("%s: error", G_STRLOC);
|
||
|
if (err)
|
||
|
{
|
||
|
g_critical ("%s: %s", G_STRLOC, err->message);
|
||
|
g_error_free (err);
|
||
|
}
|
||
|
moo_app_input_shutdown (self);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (endline)
|
||
|
{
|
||
|
commit (self);
|
||
|
|
||
|
if (self->buffer->len > MAX_BUFFER_SIZE)
|
||
|
{
|
||
|
g_byte_array_free (self->buffer, TRUE);
|
||
|
self->buffer = g_byte_array_new ();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_byte_array_set_size (self->buffer, 0);
|
||
|
}
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#endif /* ! __WIN32__ */
|