blob: 133b7850b5f89fc49cfa3d0f17f95ae84efd433d [file] [log] [blame]
Guido van Rossume0c69011997-07-19 21:33:10 +00001/* A multi-threaded telnet-like server that gives a Python prompt.
2
3Usage: pysvr [port]
4
5For security reasons, it only accepts requests from the current host.
6This can still be insecure, but restricts violations from people who
7can log in on your machine. Use with caution!
8
9*/
10
Guido van Rossum5c8b9911997-07-19 21:00:47 +000011#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <ctype.h>
15#include <errno.h>
16
17#include <sys/types.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20
21#include <pthread.h>
22
23/* XXX Umpfh.
24 Python.h defines a typedef destructor, which conflicts with pthread.h.
25 So Python.h must be included after pthread.h. */
26
27#include <Python.h>
28
29#ifndef PORT
30#define PORT 4000
31#endif
32
33extern int optind;
34extern char *optarg;
35extern int getopt();
36
37struct workorder {
38 int conn;
39 struct sockaddr_in addr;
40};
41
42/* Forward */
43static void init_python(void);
44static void usage(void);
45static void oprogname(void);
46static void main_thread(int);
47static void create_thread(int, struct sockaddr_in *);
48static void *service_thread(struct workorder *);
49static void run_interpreter(FILE *, FILE *);
50static int run_command(char *, PyObject *);
51
52static char *progname = "pysvr";
53
54main(int argc, char **argv)
55{
56 int port = PORT;
57 int c;
58
59 if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0')
60 progname = argv[0];
61
62 while ((c = getopt(argc, argv, "")) != EOF) {
63 switch (c) {
64 default:
65 usage();
66 }
67 }
68
69 if (optind < argc) {
70 if (optind+1 < argc) {
71 oprogname();
72 fprintf(stderr, "too many arguments\n");
73 usage();
74 }
75 port = atoi(argv[optind]);
76 if (port <= 0) {
77 fprintf(stderr, "bad port (%s)\n", argv[optind]);
78 usage();
79 }
80 }
81
82 main_thread(port);
83
84 fprintf(stderr, "Bye.\n");
85
86 exit(0);
87}
88
89static char usage_line[] = "usage: %s [port]\n";
90
91static void
92usage()
93{
94 fprintf(stderr, usage_line, progname);
95 exit(2);
96}
97
98static void
99main_thread(int port)
100{
Guido van Rossume0c69011997-07-19 21:33:10 +0000101 int sock, conn, size;
102 struct sockaddr_in addr, clientaddr;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000103
104 sock = socket(PF_INET, SOCK_STREAM, 0);
105 if (sock < 0) {
106 oprogname();
107 perror("can't create socket");
108 exit(1);
109 }
110
111 memset(&addr, '\0', sizeof addr);
112 addr.sin_family = AF_INET;
113 addr.sin_port = htons(port);
114 addr.sin_addr.s_addr = 0L;
115 if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) {
116 oprogname();
117 perror("can't bind socket to address");
118 exit(1);
119 }
120
121 if (listen(sock, 5) < 0) {
122 oprogname();
123 perror("can't listen on socket");
124 exit(1);
125 }
126
127 fprintf(stderr, "Listening on port %d...\n", port);
128
129 for (;;) {
Guido van Rossume0c69011997-07-19 21:33:10 +0000130 size = sizeof clientaddr;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000131 conn = accept(sock, (struct sockaddr *) &clientaddr, &size);
132 if (conn < 0) {
133 oprogname();
134 perror("can't accept connection from socket");
135 exit(1);
136 }
137
Guido van Rossume0c69011997-07-19 21:33:10 +0000138 size = sizeof addr;
139 if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) {
140 oprogname();
141 perror("can't get socket name of connection");
142 exit(1);
143 }
144 if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) {
145 oprogname();
146 perror("connection from non-local host refused");
147 fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n",
148 ntohl(addr.sin_addr.s_addr),
149 ntohl(clientaddr.sin_addr.s_addr));
150 close(conn);
151 continue;
152 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000153 create_thread(conn, &clientaddr);
154 }
155}
156
157static void
158create_thread(int conn, struct sockaddr_in *addr)
159{
160 struct workorder *work;
161 pthread_t tdata;
162
163 work = malloc(sizeof(struct workorder));
164 if (work == NULL) {
165 oprogname();
166 fprintf(stderr, "out of memory for thread.\n");
167 close(conn);
168 return;
169 }
170 work->conn = conn;
171 work->addr = *addr;
172
173 init_python();
174
175 if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) {
176 oprogname();
177 perror("can't create new thread");
178 close(conn);
179 return;
180 }
181
182 if (pthread_detach(tdata) < 0) {
183 oprogname();
184 perror("can't detach from thread");
185 }
186}
187
188static PyThreadState *the_tstate;
189static PyInterpreterState *the_interp;
190static PyObject *the_builtins;
191
192static void
193init_python()
194{
195 if (the_interp)
196 return;
197 Py_Initialize(); /* Initialize the interpreter */
198 the_builtins = PyEval_GetBuiltins(); /* Get __builtins__ */
199 PyEval_InitThreads(); /* Create and acquire the interpreter lock */
200 the_tstate = PyEval_SaveThread(); /* Release lock & get thread state */
201 the_interp = the_tstate->interpreter_state; /* Get interp state */
202}
203
204static void *
205service_thread(struct workorder *work)
206{
207 FILE *input, *output;
208
209 fprintf(stderr, "Start thread for connection %d.\n", work->conn);
210
211 input = fdopen(work->conn, "r");
212 if (input == NULL) {
213 oprogname();
214 perror("can't create input stream");
215 goto done;
216 }
217
218 output = fdopen(work->conn, "w");
219 if (output == NULL) {
220 oprogname();
221 perror("can't create output stream");
222 fclose(input);
223 goto done;
224 }
225
226 setvbuf(input, NULL, _IONBF, 0);
227 setvbuf(output, NULL, _IONBF, 0);
228
229 run_interpreter(input, output);
230
231 fclose(input);
232 fclose(output);
233
234 done:
235 fprintf(stderr, "End thread for connection %d.\n", work->conn);
236 close(work->conn);
237 free(work);
238}
239
240static void
241oprogname()
242{
243 int save = errno;
244 fprintf(stderr, "%s: ", progname);
245 errno = save;
246}
247
248static void
249run_interpreter(FILE *input, FILE *output)
250{
251 PyThreadState *tstate;
252 PyObject *new_stdin, *new_stdout;
253 PyObject *old_stdin, *old_stdout, *old_stderr;
254 PyObject *globals;
255 char buffer[1000];
256 char *p, *q;
257 int n, end;
258
259 tstate = PyThreadState_New(the_interp);
260 PyEval_AcquireThread(tstate);
261
262 globals = PyDict_New();
263 PyDict_SetItemString(globals, "__builtins__", the_builtins);
264
265 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
266 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
267
268 old_stdin = PySys_GetObject("stdin");
269 old_stdout = PySys_GetObject("stdout");
270 old_stderr = PySys_GetObject("stderr");
271
272 for (n = 1; !PyErr_Occurred(); n++) {
273 Py_BEGIN_ALLOW_THREADS
274 fprintf(output, "%d> ", n);
275 p = fgets(buffer, sizeof buffer, input);
276 Py_END_ALLOW_THREADS
277
278 if (p == NULL)
279 break;
280 if (p[0] == '\377' && p[1] == '\354')
281 break;
282
283 q = strrchr(p, '\r');
284 if (q && q[1] == '\n' && q[2] == '\0') {
285 *q++ = '\n';
286 *q++ = '\0';
287 }
288
289 while (*p && isspace(*p))
290 p++;
291 if (p[0] == '#' || p[0] == '\0')
292 continue;
293
294 PySys_SetObject("stdin", new_stdin);
295 PySys_SetObject("stdout", new_stdout);
296 PySys_SetObject("stderr", new_stdout);
297
298 end = run_command(buffer, globals);
299 if (end < 0)
300 PyErr_Print();
301
302 PySys_SetObject("stdin", old_stdin);
303 PySys_SetObject("stdout", old_stdout);
304 PySys_SetObject("stderr", old_stderr);
305
306 if (end)
307 break;
308 }
309
310 Py_XDECREF(globals);
311 Py_XDECREF(new_stdin);
312 Py_XDECREF(new_stdout);
313
314 PyEval_ReleaseThread(tstate);
315 PyThreadState_Delete(tstate);
316
317 fprintf(output, "Goodbye!\n");
318}
319
320static int
321run_command(char *buffer, PyObject *globals)
322{
323 PyObject *m, *d, *v;
324 v = PyRun_String(buffer, Py_single_input, globals, globals);
325 if (v == NULL) {
326 if (PyErr_Occurred() == PyExc_SystemExit) {
327 PyErr_Clear();
328 return 1;
329 }
330 PyErr_Print();
331 return 0;
332 }
333 Py_DECREF(v);
334 return 0;
335}