New example of threaded embedding
This commit is contained in:
parent
6e614e3d2a
commit
5c8b991175
|
@ -0,0 +1,49 @@
|
||||||
|
# Makefile for 'pysvr' application embedding Python.
|
||||||
|
# Tailored for Python 1.5a3 or later.
|
||||||
|
# Some details are specific to Solaris or CNRI.
|
||||||
|
|
||||||
|
# Which C compiler (only set because I don't have cc here)
|
||||||
|
CC=gcc
|
||||||
|
|
||||||
|
# Optimization preferences
|
||||||
|
OPT=-g
|
||||||
|
|
||||||
|
# Where Python is installed, and which version
|
||||||
|
INST=/usr/local
|
||||||
|
VER=1.5
|
||||||
|
|
||||||
|
# Expressions using the above definitions -- no need to change
|
||||||
|
PYVER=python$(VER)
|
||||||
|
#PYC=$(INST)/lib/$(PYVER)/config
|
||||||
|
PYC=../src/sparc
|
||||||
|
PYINCL=-I$(INST)/include/$(PYVER) -I$(PYC)
|
||||||
|
PYLIBS=$(PYC)/libpython1.5.a
|
||||||
|
|
||||||
|
# Where GNU readline is installed
|
||||||
|
RLINST=/depot/gnu/plat
|
||||||
|
|
||||||
|
# Libraries to link with -- very installation dependent
|
||||||
|
RLLIBS=-L$(RLINST)/lib -lreadline -ltermcap
|
||||||
|
OTHERLIBS=-lsocket -lnsl -lpthread -ldl -lm
|
||||||
|
|
||||||
|
# Compilation and link flags -- no need to change normally
|
||||||
|
CFLAGS=$(PYINCL) $(OPT)
|
||||||
|
LIBS=$(PYLIBS) $(RLLIBS) $(OTHERLIBS)
|
||||||
|
|
||||||
|
# Default port for the pysvr application
|
||||||
|
PORT=4000
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: pysvr
|
||||||
|
|
||||||
|
# Target to build pysvr
|
||||||
|
pysvr: pysvr.o $(PYOBJS)
|
||||||
|
$(CC) pysvr.o $(LIBS) -o pysvr
|
||||||
|
|
||||||
|
# Target to build and run pysvr
|
||||||
|
run: pysvr
|
||||||
|
pysvr $(PORT)
|
||||||
|
|
||||||
|
# Target to clean up the directory
|
||||||
|
clean:
|
||||||
|
-rm -f pysvr *.o *~ core
|
|
@ -0,0 +1,9 @@
|
||||||
|
This is an example of a multi-threaded C application embedding a
|
||||||
|
Python interpreter.
|
||||||
|
|
||||||
|
The particular application is a multi-threaded telnet-like server that
|
||||||
|
provides you with a Python prompt (instead of a shell prompt).
|
||||||
|
|
||||||
|
The file pysvr.py is a prototype in Python.
|
||||||
|
|
||||||
|
THIS APPLICATION IS NOT SECURE -- ONLY USE IT FOR TESTING!
|
|
@ -0,0 +1,312 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* XXX Umpfh.
|
||||||
|
Python.h defines a typedef destructor, which conflicts with pthread.h.
|
||||||
|
So Python.h must be included after pthread.h. */
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
#ifndef PORT
|
||||||
|
#define PORT 4000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int optind;
|
||||||
|
extern char *optarg;
|
||||||
|
extern int getopt();
|
||||||
|
|
||||||
|
struct workorder {
|
||||||
|
int conn;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Forward */
|
||||||
|
static void init_python(void);
|
||||||
|
static void usage(void);
|
||||||
|
static void oprogname(void);
|
||||||
|
static void main_thread(int);
|
||||||
|
static void create_thread(int, struct sockaddr_in *);
|
||||||
|
static void *service_thread(struct workorder *);
|
||||||
|
static void run_interpreter(FILE *, FILE *);
|
||||||
|
static int run_command(char *, PyObject *);
|
||||||
|
|
||||||
|
static char *progname = "pysvr";
|
||||||
|
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int port = PORT;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0')
|
||||||
|
progname = argv[0];
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "")) != EOF) {
|
||||||
|
switch (c) {
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc) {
|
||||||
|
if (optind+1 < argc) {
|
||||||
|
oprogname();
|
||||||
|
fprintf(stderr, "too many arguments\n");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
port = atoi(argv[optind]);
|
||||||
|
if (port <= 0) {
|
||||||
|
fprintf(stderr, "bad port (%s)\n", argv[optind]);
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main_thread(port);
|
||||||
|
|
||||||
|
fprintf(stderr, "Bye.\n");
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char usage_line[] = "usage: %s [port]\n";
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, usage_line, progname);
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
main_thread(int port)
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
|
sock = socket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock < 0) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't create socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&addr, '\0', sizeof addr);
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
addr.sin_addr.s_addr = 0L;
|
||||||
|
if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't bind socket to address");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(sock, 5) < 0) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't listen on socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Listening on port %d...\n", port);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct sockaddr_in clientaddr;
|
||||||
|
int conn, size;
|
||||||
|
|
||||||
|
conn = accept(sock, (struct sockaddr *) &clientaddr, &size);
|
||||||
|
if (conn < 0) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't accept connection from socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
create_thread(conn, &clientaddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
create_thread(int conn, struct sockaddr_in *addr)
|
||||||
|
{
|
||||||
|
struct workorder *work;
|
||||||
|
pthread_t tdata;
|
||||||
|
|
||||||
|
work = malloc(sizeof(struct workorder));
|
||||||
|
if (work == NULL) {
|
||||||
|
oprogname();
|
||||||
|
fprintf(stderr, "out of memory for thread.\n");
|
||||||
|
close(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
work->conn = conn;
|
||||||
|
work->addr = *addr;
|
||||||
|
|
||||||
|
init_python();
|
||||||
|
|
||||||
|
if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't create new thread");
|
||||||
|
close(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_detach(tdata) < 0) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't detach from thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyThreadState *the_tstate;
|
||||||
|
static PyInterpreterState *the_interp;
|
||||||
|
static PyObject *the_builtins;
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_python()
|
||||||
|
{
|
||||||
|
if (the_interp)
|
||||||
|
return;
|
||||||
|
Py_Initialize(); /* Initialize the interpreter */
|
||||||
|
the_builtins = PyEval_GetBuiltins(); /* Get __builtins__ */
|
||||||
|
PyEval_InitThreads(); /* Create and acquire the interpreter lock */
|
||||||
|
the_tstate = PyEval_SaveThread(); /* Release lock & get thread state */
|
||||||
|
the_interp = the_tstate->interpreter_state; /* Get interp state */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
service_thread(struct workorder *work)
|
||||||
|
{
|
||||||
|
FILE *input, *output;
|
||||||
|
|
||||||
|
fprintf(stderr, "Start thread for connection %d.\n", work->conn);
|
||||||
|
|
||||||
|
input = fdopen(work->conn, "r");
|
||||||
|
if (input == NULL) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't create input stream");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = fdopen(work->conn, "w");
|
||||||
|
if (output == NULL) {
|
||||||
|
oprogname();
|
||||||
|
perror("can't create output stream");
|
||||||
|
fclose(input);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
setvbuf(input, NULL, _IONBF, 0);
|
||||||
|
setvbuf(output, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
run_interpreter(input, output);
|
||||||
|
|
||||||
|
fclose(input);
|
||||||
|
fclose(output);
|
||||||
|
|
||||||
|
done:
|
||||||
|
fprintf(stderr, "End thread for connection %d.\n", work->conn);
|
||||||
|
close(work->conn);
|
||||||
|
free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
oprogname()
|
||||||
|
{
|
||||||
|
int save = errno;
|
||||||
|
fprintf(stderr, "%s: ", progname);
|
||||||
|
errno = save;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_interpreter(FILE *input, FILE *output)
|
||||||
|
{
|
||||||
|
PyThreadState *tstate;
|
||||||
|
PyObject *new_stdin, *new_stdout;
|
||||||
|
PyObject *old_stdin, *old_stdout, *old_stderr;
|
||||||
|
PyObject *globals;
|
||||||
|
char buffer[1000];
|
||||||
|
char *p, *q;
|
||||||
|
int n, end;
|
||||||
|
|
||||||
|
tstate = PyThreadState_New(the_interp);
|
||||||
|
PyEval_AcquireThread(tstate);
|
||||||
|
|
||||||
|
globals = PyDict_New();
|
||||||
|
PyDict_SetItemString(globals, "__builtins__", the_builtins);
|
||||||
|
|
||||||
|
new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
|
||||||
|
new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
|
||||||
|
|
||||||
|
old_stdin = PySys_GetObject("stdin");
|
||||||
|
old_stdout = PySys_GetObject("stdout");
|
||||||
|
old_stderr = PySys_GetObject("stderr");
|
||||||
|
|
||||||
|
for (n = 1; !PyErr_Occurred(); n++) {
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
fprintf(output, "%d> ", n);
|
||||||
|
p = fgets(buffer, sizeof buffer, input);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
if (p == NULL)
|
||||||
|
break;
|
||||||
|
if (p[0] == '\377' && p[1] == '\354')
|
||||||
|
break;
|
||||||
|
|
||||||
|
q = strrchr(p, '\r');
|
||||||
|
if (q && q[1] == '\n' && q[2] == '\0') {
|
||||||
|
*q++ = '\n';
|
||||||
|
*q++ = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*p && isspace(*p))
|
||||||
|
p++;
|
||||||
|
if (p[0] == '#' || p[0] == '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PySys_SetObject("stdin", new_stdin);
|
||||||
|
PySys_SetObject("stdout", new_stdout);
|
||||||
|
PySys_SetObject("stderr", new_stdout);
|
||||||
|
|
||||||
|
end = run_command(buffer, globals);
|
||||||
|
if (end < 0)
|
||||||
|
PyErr_Print();
|
||||||
|
|
||||||
|
PySys_SetObject("stdin", old_stdin);
|
||||||
|
PySys_SetObject("stdout", old_stdout);
|
||||||
|
PySys_SetObject("stderr", old_stderr);
|
||||||
|
|
||||||
|
if (end)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(globals);
|
||||||
|
Py_XDECREF(new_stdin);
|
||||||
|
Py_XDECREF(new_stdout);
|
||||||
|
|
||||||
|
PyEval_ReleaseThread(tstate);
|
||||||
|
PyThreadState_Delete(tstate);
|
||||||
|
|
||||||
|
fprintf(output, "Goodbye!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_command(char *buffer, PyObject *globals)
|
||||||
|
{
|
||||||
|
PyObject *m, *d, *v;
|
||||||
|
v = PyRun_String(buffer, Py_single_input, globals, globals);
|
||||||
|
if (v == NULL) {
|
||||||
|
if (PyErr_Occurred() == PyExc_SystemExit) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
PyErr_Print();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Py_DECREF(v);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
"""A multi-threaded telnet-like server that gives a Python prompt.
|
||||||
|
|
||||||
|
This is really a prototype for the same thing in C.
|
||||||
|
|
||||||
|
Usage: pysvr.py [port]
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys, os, string, getopt, thread, socket, traceback
|
||||||
|
|
||||||
|
OK_DOMAINS = [".cnri.reston.va.us", ".python.org"]
|
||||||
|
|
||||||
|
PORT = 7585892 % 0xFFFF # == 49367
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(sys.argv[1:], "")
|
||||||
|
if len(args) > 1:
|
||||||
|
raise getopt.error, "Too many arguments."
|
||||||
|
except getopt.error, msg:
|
||||||
|
usage(msg)
|
||||||
|
for o, a in opts:
|
||||||
|
pass
|
||||||
|
if args:
|
||||||
|
try:
|
||||||
|
port = string.atoi(args[0])
|
||||||
|
except ValueError, msg:
|
||||||
|
usage(msg)
|
||||||
|
else:
|
||||||
|
port = PORT
|
||||||
|
main_thread(port)
|
||||||
|
|
||||||
|
def usage(msg=None):
|
||||||
|
sys.stdout = sys.stderr
|
||||||
|
if msg:
|
||||||
|
print msg
|
||||||
|
print "\n", __doc__,
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
def main_thread(port):
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.bind(("", port))
|
||||||
|
sock.listen(5)
|
||||||
|
while 1:
|
||||||
|
(conn, addr) = sock.accept()
|
||||||
|
thread.start_new_thread(service_thread, (conn, addr))
|
||||||
|
del conn, addr
|
||||||
|
|
||||||
|
def service_thread(conn, addr):
|
||||||
|
(caddr, cport) = addr
|
||||||
|
try:
|
||||||
|
host, aliases, ipaddrs = socket.gethostbyaddr(caddr)
|
||||||
|
except socket.error:
|
||||||
|
print "Don't know hostname for", caddr
|
||||||
|
return
|
||||||
|
if '.' not in host:
|
||||||
|
for a in aliases:
|
||||||
|
if '.' in a:
|
||||||
|
host = a
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print "Only a local name (%s) for %s" % (host, caddr)
|
||||||
|
return
|
||||||
|
i = string.find(host, '.')
|
||||||
|
domain = string.lower(host[i:])
|
||||||
|
if domain not in OK_DOMAINS:
|
||||||
|
print "Connection from", host, "not accepted"
|
||||||
|
return
|
||||||
|
print "Thread %s has connection from %s.\n" % (str(thread.get_ident()),
|
||||||
|
host),
|
||||||
|
stdin = conn.makefile("r")
|
||||||
|
stdout = conn.makefile("w", 0)
|
||||||
|
run_interpreter(stdin, stdout)
|
||||||
|
print "Thread %s is done.\n" % str(thread.get_ident()),
|
||||||
|
|
||||||
|
def run_interpreter(stdin, stdout):
|
||||||
|
globals = {}
|
||||||
|
try:
|
||||||
|
str(sys.ps1)
|
||||||
|
except:
|
||||||
|
sys.ps1 = ">>> "
|
||||||
|
source = ""
|
||||||
|
while 1:
|
||||||
|
stdout.write(sys.ps1)
|
||||||
|
line = stdin.readline()
|
||||||
|
if line[:2] == '\377\354':
|
||||||
|
line = ""
|
||||||
|
if not line and not source:
|
||||||
|
break
|
||||||
|
if line[-2:] == '\r\n':
|
||||||
|
line = line[:-2] + '\n'
|
||||||
|
source = source + line
|
||||||
|
try:
|
||||||
|
code = compile_command(source)
|
||||||
|
except SyntaxError, err:
|
||||||
|
source = ""
|
||||||
|
traceback.print_exception(SyntaxError, err, None, file=stdout)
|
||||||
|
continue
|
||||||
|
if not code:
|
||||||
|
continue
|
||||||
|
source = ""
|
||||||
|
try:
|
||||||
|
run_command(code, stdin, stdout, globals)
|
||||||
|
except SystemExit, how:
|
||||||
|
if how:
|
||||||
|
try:
|
||||||
|
how = str(how)
|
||||||
|
except:
|
||||||
|
how = ""
|
||||||
|
stdout.write("Exit %s\n" % how)
|
||||||
|
break
|
||||||
|
stdout.write("\nGoodbye.\n")
|
||||||
|
|
||||||
|
def run_command(code, stdin, stdout, globals):
|
||||||
|
save = sys.stdin, sys.stdout, sys.stderr
|
||||||
|
try:
|
||||||
|
sys.stdout = sys.stderr = stdout
|
||||||
|
sys.stdin = stdin
|
||||||
|
try:
|
||||||
|
exec code in globals
|
||||||
|
except SystemExit, how:
|
||||||
|
raise SystemExit, how, sys.exc_info()[2]
|
||||||
|
except:
|
||||||
|
type, value, tb = sys.exc_info()
|
||||||
|
if tb: tb = tb.tb_next
|
||||||
|
traceback.print_exception(type, value, tb)
|
||||||
|
del tb
|
||||||
|
finally:
|
||||||
|
sys.stdin, sys.stdout, sys.stderr = save
|
||||||
|
|
||||||
|
from code import compile_command
|
||||||
|
|
||||||
|
main()
|
Loading…
Reference in New Issue