From 5c8b9911750565713cba39fd8835cc951ddf2adf Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 19 Jul 1997 21:00:47 +0000 Subject: New example of threaded embedding --- Demo/pysvr/Makefile | 49 +++++++++ Demo/pysvr/README | 9 ++ Demo/pysvr/pysvr.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Demo/pysvr/pysvr.py | 135 +++++++++++++++++++++++ 4 files changed, 505 insertions(+) create mode 100644 Demo/pysvr/Makefile create mode 100644 Demo/pysvr/README create mode 100644 Demo/pysvr/pysvr.c create mode 100755 Demo/pysvr/pysvr.py diff --git a/Demo/pysvr/Makefile b/Demo/pysvr/Makefile new file mode 100644 index 0000000..4adc2ce --- /dev/null +++ b/Demo/pysvr/Makefile @@ -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 diff --git a/Demo/pysvr/README b/Demo/pysvr/README new file mode 100644 index 0000000..5e64e38 --- /dev/null +++ b/Demo/pysvr/README @@ -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! diff --git a/Demo/pysvr/pysvr.c b/Demo/pysvr/pysvr.c new file mode 100644 index 0000000..f6f9e08 --- /dev/null +++ b/Demo/pysvr/pysvr.c @@ -0,0 +1,312 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* XXX Umpfh. + Python.h defines a typedef destructor, which conflicts with pthread.h. + So Python.h must be included after pthread.h. */ + +#include + +#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, "", "r", NULL); + new_stdout = PyFile_FromFile(output, "", "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; +} diff --git a/Demo/pysvr/pysvr.py b/Demo/pysvr/pysvr.py new file mode 100755 index 0000000..4488386 --- /dev/null +++ b/Demo/pysvr/pysvr.py @@ -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() -- cgit v0.12