blob: cced6da4bfb0ad018471207d809ea03e25d05318 [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>
Thomas Wouters2cffc7d2000-11-03 08:18:37 +000022#include <getopt.h>
Guido van Rossum5c8b9911997-07-19 21:00:47 +000023
24/* XXX Umpfh.
25 Python.h defines a typedef destructor, which conflicts with pthread.h.
26 So Python.h must be included after pthread.h. */
27
Tim Peters885d4572001-11-28 20:27:42 +000028#include "Python.h"
Guido van Rossum5c8b9911997-07-19 21:00:47 +000029
Guido van Rossumcaf2f8e1999-10-05 22:16:07 +000030extern int Py_VerboseFlag;
31
Guido van Rossum5c8b9911997-07-19 21:00:47 +000032#ifndef PORT
33#define PORT 4000
34#endif
35
Guido van Rossum5c8b9911997-07-19 21:00:47 +000036struct workorder {
37 int conn;
38 struct sockaddr_in addr;
39};
40
41/* Forward */
42static void init_python(void);
43static void usage(void);
44static void oprogname(void);
45static void main_thread(int);
46static void create_thread(int, struct sockaddr_in *);
47static void *service_thread(struct workorder *);
48static void run_interpreter(FILE *, FILE *);
49static int run_command(char *, PyObject *);
Guido van Rossumc46d22e1997-08-02 02:02:22 +000050static void ps(void);
Guido van Rossum5c8b9911997-07-19 21:00:47 +000051
52static char *progname = "pysvr";
53
Guido van Rossumc46d22e1997-08-02 02:02:22 +000054static PyThreadState *gtstate;
55
Guido van Rossum5c8b9911997-07-19 21:00:47 +000056main(int argc, char **argv)
57{
58 int port = PORT;
59 int c;
60
61 if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0')
62 progname = argv[0];
63
Guido van Rossumcaf2f8e1999-10-05 22:16:07 +000064 while ((c = getopt(argc, argv, "v")) != EOF) {
Guido van Rossum5c8b9911997-07-19 21:00:47 +000065 switch (c) {
Guido van Rossumcaf2f8e1999-10-05 22:16:07 +000066 case 'v':
67 Py_VerboseFlag++;
68 break;
Guido van Rossum5c8b9911997-07-19 21:00:47 +000069 default:
70 usage();
71 }
72 }
73
74 if (optind < argc) {
75 if (optind+1 < argc) {
76 oprogname();
77 fprintf(stderr, "too many arguments\n");
78 usage();
79 }
80 port = atoi(argv[optind]);
81 if (port <= 0) {
82 fprintf(stderr, "bad port (%s)\n", argv[optind]);
83 usage();
84 }
85 }
86
87 main_thread(port);
88
89 fprintf(stderr, "Bye.\n");
90
91 exit(0);
92}
93
94static char usage_line[] = "usage: %s [port]\n";
95
96static void
Thomas Wouters78890102000-07-22 19:25:51 +000097usage(void)
Guido van Rossum5c8b9911997-07-19 21:00:47 +000098{
99 fprintf(stderr, usage_line, progname);
100 exit(2);
101}
102
103static void
104main_thread(int port)
105{
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000106 int sock, conn, size, i;
Guido van Rossume0c69011997-07-19 21:33:10 +0000107 struct sockaddr_in addr, clientaddr;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000108
109 sock = socket(PF_INET, SOCK_STREAM, 0);
110 if (sock < 0) {
111 oprogname();
112 perror("can't create socket");
113 exit(1);
114 }
115
Guido van Rossum643f8f61997-12-25 04:48:51 +0000116#ifdef SO_REUSEADDR
117 i = 1;
118 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i);
119#endif
120
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000121 memset((char *)&addr, '\0', sizeof addr);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000122 addr.sin_family = AF_INET;
123 addr.sin_port = htons(port);
124 addr.sin_addr.s_addr = 0L;
125 if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) {
126 oprogname();
127 perror("can't bind socket to address");
128 exit(1);
129 }
130
131 if (listen(sock, 5) < 0) {
132 oprogname();
133 perror("can't listen on socket");
134 exit(1);
135 }
136
137 fprintf(stderr, "Listening on port %d...\n", port);
138
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000139 for (i = 0; ; i++) {
Guido van Rossume0c69011997-07-19 21:33:10 +0000140 size = sizeof clientaddr;
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000141 memset((char *) &clientaddr, '\0', size);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000142 conn = accept(sock, (struct sockaddr *) &clientaddr, &size);
143 if (conn < 0) {
144 oprogname();
145 perror("can't accept connection from socket");
146 exit(1);
147 }
148
Guido van Rossume0c69011997-07-19 21:33:10 +0000149 size = sizeof addr;
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000150 memset((char *) &addr, '\0', size);
Guido van Rossume0c69011997-07-19 21:33:10 +0000151 if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) {
152 oprogname();
153 perror("can't get socket name of connection");
154 exit(1);
155 }
156 if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) {
157 oprogname();
158 perror("connection from non-local host refused");
159 fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n",
160 ntohl(addr.sin_addr.s_addr),
161 ntohl(clientaddr.sin_addr.s_addr));
162 close(conn);
163 continue;
164 }
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000165 if (i == 4) {
166 close(conn);
167 break;
168 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000169 create_thread(conn, &clientaddr);
170 }
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000171
172 close(sock);
173
174 if (gtstate) {
175 PyEval_AcquireThread(gtstate);
176 gtstate = NULL;
177 Py_Finalize();
Guido van Rossumcaf2f8e1999-10-05 22:16:07 +0000178 /* And a second time, just because we can. */
179 Py_Finalize(); /* This should be harmless. */
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000180 }
181 exit(0);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000182}
183
184static void
185create_thread(int conn, struct sockaddr_in *addr)
186{
187 struct workorder *work;
188 pthread_t tdata;
189
190 work = malloc(sizeof(struct workorder));
191 if (work == NULL) {
192 oprogname();
193 fprintf(stderr, "out of memory for thread.\n");
194 close(conn);
195 return;
196 }
197 work->conn = conn;
198 work->addr = *addr;
199
200 init_python();
201
202 if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) {
203 oprogname();
204 perror("can't create new thread");
205 close(conn);
206 return;
207 }
208
209 if (pthread_detach(tdata) < 0) {
210 oprogname();
211 perror("can't detach from thread");
212 }
213}
214
215static PyThreadState *the_tstate;
216static PyInterpreterState *the_interp;
217static PyObject *the_builtins;
218
219static void
Thomas Wouters78890102000-07-22 19:25:51 +0000220init_python(void)
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000221{
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000222 if (gtstate)
223 return;
224 Py_Initialize(); /* Initialize the interpreter */
225 PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */
226 gtstate = PyEval_SaveThread(); /* Release the thread state */
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000227}
228
229static void *
230service_thread(struct workorder *work)
231{
232 FILE *input, *output;
233
234 fprintf(stderr, "Start thread for connection %d.\n", work->conn);
235
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000236 ps();
237
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000238 input = fdopen(work->conn, "r");
239 if (input == NULL) {
240 oprogname();
241 perror("can't create input stream");
242 goto done;
243 }
244
245 output = fdopen(work->conn, "w");
246 if (output == NULL) {
247 oprogname();
248 perror("can't create output stream");
249 fclose(input);
250 goto done;
251 }
252
253 setvbuf(input, NULL, _IONBF, 0);
254 setvbuf(output, NULL, _IONBF, 0);
255
256 run_interpreter(input, output);
257
258 fclose(input);
259 fclose(output);
260
261 done:
262 fprintf(stderr, "End thread for connection %d.\n", work->conn);
263 close(work->conn);
264 free(work);
265}
266
267static void
Thomas Wouters78890102000-07-22 19:25:51 +0000268oprogname(void)
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000269{
270 int save = errno;
271 fprintf(stderr, "%s: ", progname);
272 errno = save;
273}
274
275static void
276run_interpreter(FILE *input, FILE *output)
277{
278 PyThreadState *tstate;
279 PyObject *new_stdin, *new_stdout;
Guido van Rossum630924f1997-07-25 20:59:55 +0000280 PyObject *mainmod, *globals;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000281 char buffer[1000];
282 char *p, *q;
283 int n, end;
284
Guido van Rossum630924f1997-07-25 20:59:55 +0000285 PyEval_AcquireLock();
286 tstate = Py_NewInterpreter();
287 if (tstate == NULL) {
288 fprintf(output, "Sorry -- can't create an interpreter\n");
289 return;
290 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000291
Guido van Rossum630924f1997-07-25 20:59:55 +0000292 mainmod = PyImport_AddModule("__main__");
293 globals = PyModule_GetDict(mainmod);
294 Py_INCREF(globals);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000295
296 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
297 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
298
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000299 PySys_SetObject("stdin", new_stdin);
300 PySys_SetObject("stdout", new_stdout);
301 PySys_SetObject("stderr", new_stdout);
302
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000303 for (n = 1; !PyErr_Occurred(); n++) {
304 Py_BEGIN_ALLOW_THREADS
305 fprintf(output, "%d> ", n);
306 p = fgets(buffer, sizeof buffer, input);
307 Py_END_ALLOW_THREADS
308
309 if (p == NULL)
310 break;
311 if (p[0] == '\377' && p[1] == '\354')
312 break;
313
314 q = strrchr(p, '\r');
315 if (q && q[1] == '\n' && q[2] == '\0') {
316 *q++ = '\n';
317 *q++ = '\0';
318 }
319
320 while (*p && isspace(*p))
321 p++;
322 if (p[0] == '#' || p[0] == '\0')
323 continue;
324
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000325 end = run_command(buffer, globals);
326 if (end < 0)
327 PyErr_Print();
328
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000329 if (end)
330 break;
331 }
332
333 Py_XDECREF(globals);
334 Py_XDECREF(new_stdin);
335 Py_XDECREF(new_stdout);
336
Guido van Rossum630924f1997-07-25 20:59:55 +0000337 Py_EndInterpreter(tstate);
338 PyEval_ReleaseLock();
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000339
340 fprintf(output, "Goodbye!\n");
341}
342
343static int
344run_command(char *buffer, PyObject *globals)
345{
346 PyObject *m, *d, *v;
Guido van Rossum630924f1997-07-25 20:59:55 +0000347 fprintf(stderr, "run_command: %s", buffer);
348 if (strchr(buffer, '\n') == NULL)
349 fprintf(stderr, "\n");
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000350 v = PyRun_String(buffer, Py_single_input, globals, globals);
351 if (v == NULL) {
352 if (PyErr_Occurred() == PyExc_SystemExit) {
353 PyErr_Clear();
354 return 1;
355 }
356 PyErr_Print();
357 return 0;
358 }
359 Py_DECREF(v);
360 return 0;
361}
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000362
363static void
Thomas Wouters78890102000-07-22 19:25:51 +0000364ps(void)
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000365{
366 char buffer[100];
Tim Peters885d4572001-11-28 20:27:42 +0000367 PyOS_snprintf(buffer, sizeof(buffer),
Martin v. Löwisdcea3702002-03-01 13:15:30 +0000368 "ps -l -p %d </dev/null | sed 1d\n", getpid());
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000369 system(buffer);
370}