]> granicus.if.org Git - python/commitdiff
New example of threaded embedding
authorGuido van Rossum <guido@python.org>
Sat, 19 Jul 1997 21:00:47 +0000 (21:00 +0000)
committerGuido van Rossum <guido@python.org>
Sat, 19 Jul 1997 21:00:47 +0000 (21:00 +0000)
Demo/pysvr/Makefile [new file with mode: 0644]
Demo/pysvr/README [new file with mode: 0644]
Demo/pysvr/pysvr.c [new file with mode: 0644]
Demo/pysvr/pysvr.py [new file with mode: 0755]

diff --git a/Demo/pysvr/Makefile b/Demo/pysvr/Makefile
new file mode 100644 (file)
index 0000000..4adc2ce
--- /dev/null
@@ -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 (file)
index 0000000..5e64e38
--- /dev/null
@@ -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 (file)
index 0000000..f6f9e08
--- /dev/null
@@ -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;
+}
diff --git a/Demo/pysvr/pysvr.py b/Demo/pysvr/pysvr.py
new file mode 100755 (executable)
index 0000000..4488386
--- /dev/null
@@ -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()