mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			370 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* A multi-threaded telnet-like server that gives a Python prompt.
 | 
						|
 | 
						|
Usage: pysvr [port]
 | 
						|
 | 
						|
For security reasons, it only accepts requests from the current host.
 | 
						|
This can still be insecure, but restricts violations from people who
 | 
						|
can log in on your machine.  Use with caution!
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
#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>
 | 
						|
#include <getopt.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"
 | 
						|
 | 
						|
extern int Py_VerboseFlag;
 | 
						|
 | 
						|
#ifndef PORT
 | 
						|
#define PORT 4000
 | 
						|
#endif
 | 
						|
 | 
						|
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 void ps(void);
 | 
						|
 | 
						|
static char *progname = "pysvr";
 | 
						|
 | 
						|
static PyThreadState *gtstate;
 | 
						|
 | 
						|
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, "v")) != EOF) {
 | 
						|
		switch (c) {
 | 
						|
		case 'v':
 | 
						|
			Py_VerboseFlag++;
 | 
						|
			break;
 | 
						|
		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(void)
 | 
						|
{
 | 
						|
	fprintf(stderr, usage_line, progname);
 | 
						|
	exit(2);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
main_thread(int port)
 | 
						|
{
 | 
						|
	int sock, conn, size, i;
 | 
						|
	struct sockaddr_in addr, clientaddr;
 | 
						|
 | 
						|
	sock = socket(PF_INET, SOCK_STREAM, 0);
 | 
						|
	if (sock < 0) {
 | 
						|
		oprogname();
 | 
						|
		perror("can't create socket");
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef SO_REUSEADDR
 | 
						|
	i = 1;
 | 
						|
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i);
 | 
						|
#endif
 | 
						|
 | 
						|
	memset((char *)&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 (i = 0; ; i++) {
 | 
						|
		size = sizeof clientaddr;
 | 
						|
		memset((char *) &clientaddr, '\0', size);
 | 
						|
		conn = accept(sock, (struct sockaddr *) &clientaddr, &size);
 | 
						|
		if (conn < 0) {
 | 
						|
			oprogname();
 | 
						|
			perror("can't accept connection from socket");
 | 
						|
			exit(1);
 | 
						|
		}
 | 
						|
 | 
						|
		size = sizeof addr;
 | 
						|
		memset((char *) &addr, '\0', size);
 | 
						|
		if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) {
 | 
						|
			oprogname();
 | 
						|
			perror("can't get socket name of connection");
 | 
						|
			exit(1);
 | 
						|
		}
 | 
						|
		if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) {
 | 
						|
			oprogname();
 | 
						|
			perror("connection from non-local host refused");
 | 
						|
			fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n",
 | 
						|
				ntohl(addr.sin_addr.s_addr),
 | 
						|
				ntohl(clientaddr.sin_addr.s_addr));
 | 
						|
			close(conn);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (i == 4) {
 | 
						|
			close(conn);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		create_thread(conn, &clientaddr);
 | 
						|
	}
 | 
						|
 | 
						|
	close(sock);
 | 
						|
 | 
						|
	if (gtstate) {
 | 
						|
		PyEval_AcquireThread(gtstate);
 | 
						|
		gtstate = NULL;
 | 
						|
		Py_Finalize();
 | 
						|
		/* And a second time, just because we can. */
 | 
						|
		Py_Finalize(); /* This should be harmless. */
 | 
						|
	}
 | 
						|
	exit(0);
 | 
						|
}
 | 
						|
 | 
						|
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(void)
 | 
						|
{
 | 
						|
	if (gtstate)
 | 
						|
		return;
 | 
						|
	Py_Initialize(); /* Initialize the interpreter */
 | 
						|
	PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */
 | 
						|
	gtstate = PyEval_SaveThread(); /* Release the thread state */
 | 
						|
}
 | 
						|
 | 
						|
static void *
 | 
						|
service_thread(struct workorder *work)
 | 
						|
{
 | 
						|
	FILE *input, *output;
 | 
						|
 | 
						|
	fprintf(stderr, "Start thread for connection %d.\n", work->conn);
 | 
						|
 | 
						|
	ps();
 | 
						|
 | 
						|
	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(void)
 | 
						|
{
 | 
						|
	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 *mainmod, *globals;
 | 
						|
	char buffer[1000];
 | 
						|
	char *p, *q;
 | 
						|
	int n, end;
 | 
						|
 | 
						|
	PyEval_AcquireLock();
 | 
						|
	tstate = Py_NewInterpreter();
 | 
						|
	if (tstate == NULL) {
 | 
						|
		fprintf(output, "Sorry -- can't create an interpreter\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	mainmod = PyImport_AddModule("__main__");
 | 
						|
	globals = PyModule_GetDict(mainmod);
 | 
						|
	Py_INCREF(globals);
 | 
						|
 | 
						|
	new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
 | 
						|
	new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
 | 
						|
 | 
						|
	PySys_SetObject("stdin", new_stdin);
 | 
						|
	PySys_SetObject("stdout", new_stdout);
 | 
						|
	PySys_SetObject("stderr", new_stdout);
 | 
						|
 | 
						|
	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;
 | 
						|
 | 
						|
		end = run_command(buffer, globals);
 | 
						|
		if (end < 0)
 | 
						|
			PyErr_Print();
 | 
						|
 | 
						|
		if (end)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	Py_XDECREF(globals);
 | 
						|
	Py_XDECREF(new_stdin);
 | 
						|
	Py_XDECREF(new_stdout);
 | 
						|
 | 
						|
	Py_EndInterpreter(tstate);
 | 
						|
	PyEval_ReleaseLock();
 | 
						|
 | 
						|
	fprintf(output, "Goodbye!\n");
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
run_command(char *buffer, PyObject *globals)
 | 
						|
{
 | 
						|
	PyObject *m, *d, *v;
 | 
						|
	fprintf(stderr, "run_command: %s", buffer);
 | 
						|
	if (strchr(buffer, '\n') == NULL)
 | 
						|
		fprintf(stderr, "\n");
 | 
						|
	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;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ps(void)
 | 
						|
{
 | 
						|
	char buffer[100];
 | 
						|
	PyOS_snprintf(buffer, sizeof(buffer),
 | 
						|
		      "ps -l -p %d </dev/null | sed 1d\n", getpid());
 | 
						|
	system(buffer);
 | 
						|
}
 |