blob: b1397c9615c261ecc6afba46be127b89bad0db49 [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 *);
Guido van Rossumc46d22e1997-08-02 02:02:22 +000051static void ps(void);
Guido van Rossum5c8b9911997-07-19 21:00:47 +000052
53static char *progname = "pysvr";
54
Guido van Rossumc46d22e1997-08-02 02:02:22 +000055static PyThreadState *gtstate;
56
Guido van Rossum5c8b9911997-07-19 21:00:47 +000057main(int argc, char **argv)
58{
59 int port = PORT;
60 int c;
61
62 if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0')
63 progname = argv[0];
64
65 while ((c = getopt(argc, argv, "")) != EOF) {
66 switch (c) {
67 default:
68 usage();
69 }
70 }
71
72 if (optind < argc) {
73 if (optind+1 < argc) {
74 oprogname();
75 fprintf(stderr, "too many arguments\n");
76 usage();
77 }
78 port = atoi(argv[optind]);
79 if (port <= 0) {
80 fprintf(stderr, "bad port (%s)\n", argv[optind]);
81 usage();
82 }
83 }
84
85 main_thread(port);
86
87 fprintf(stderr, "Bye.\n");
88
89 exit(0);
90}
91
92static char usage_line[] = "usage: %s [port]\n";
93
94static void
95usage()
96{
97 fprintf(stderr, usage_line, progname);
98 exit(2);
99}
100
101static void
102main_thread(int port)
103{
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000104 int sock, conn, size, i;
Guido van Rossume0c69011997-07-19 21:33:10 +0000105 struct sockaddr_in addr, clientaddr;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000106
107 sock = socket(PF_INET, SOCK_STREAM, 0);
108 if (sock < 0) {
109 oprogname();
110 perror("can't create socket");
111 exit(1);
112 }
113
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000114 memset((char *)&addr, '\0', sizeof addr);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000115 addr.sin_family = AF_INET;
116 addr.sin_port = htons(port);
117 addr.sin_addr.s_addr = 0L;
118 if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) {
119 oprogname();
120 perror("can't bind socket to address");
121 exit(1);
122 }
123
124 if (listen(sock, 5) < 0) {
125 oprogname();
126 perror("can't listen on socket");
127 exit(1);
128 }
129
130 fprintf(stderr, "Listening on port %d...\n", port);
131
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000132 for (i = 0; ; i++) {
Guido van Rossume0c69011997-07-19 21:33:10 +0000133 size = sizeof clientaddr;
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000134 memset((char *) &clientaddr, '\0', size);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000135 conn = accept(sock, (struct sockaddr *) &clientaddr, &size);
136 if (conn < 0) {
137 oprogname();
138 perror("can't accept connection from socket");
139 exit(1);
140 }
141
Guido van Rossume0c69011997-07-19 21:33:10 +0000142 size = sizeof addr;
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000143 memset((char *) &addr, '\0', size);
Guido van Rossume0c69011997-07-19 21:33:10 +0000144 if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) {
145 oprogname();
146 perror("can't get socket name of connection");
147 exit(1);
148 }
149 if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) {
150 oprogname();
151 perror("connection from non-local host refused");
152 fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n",
153 ntohl(addr.sin_addr.s_addr),
154 ntohl(clientaddr.sin_addr.s_addr));
155 close(conn);
156 continue;
157 }
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000158 if (i == 4) {
159 close(conn);
160 break;
161 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000162 create_thread(conn, &clientaddr);
163 }
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000164
165 close(sock);
166
167 if (gtstate) {
168 PyEval_AcquireThread(gtstate);
169 gtstate = NULL;
170 Py_Finalize();
Guido van Rossumaa615051997-08-20 22:40:18 +0000171 Py_Finalize();
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000172 }
173 exit(0);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000174}
175
176static void
177create_thread(int conn, struct sockaddr_in *addr)
178{
179 struct workorder *work;
180 pthread_t tdata;
181
182 work = malloc(sizeof(struct workorder));
183 if (work == NULL) {
184 oprogname();
185 fprintf(stderr, "out of memory for thread.\n");
186 close(conn);
187 return;
188 }
189 work->conn = conn;
190 work->addr = *addr;
191
192 init_python();
193
194 if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) {
195 oprogname();
196 perror("can't create new thread");
197 close(conn);
198 return;
199 }
200
201 if (pthread_detach(tdata) < 0) {
202 oprogname();
203 perror("can't detach from thread");
204 }
205}
206
207static PyThreadState *the_tstate;
208static PyInterpreterState *the_interp;
209static PyObject *the_builtins;
210
211static void
212init_python()
213{
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000214 if (gtstate)
215 return;
216 Py_Initialize(); /* Initialize the interpreter */
Guido van Rossumaa615051997-08-20 22:40:18 +0000217 Py_Initialize(); /* Initialize the interpreter */
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000218 PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */
219 gtstate = PyEval_SaveThread(); /* Release the thread state */
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000220}
221
222static void *
223service_thread(struct workorder *work)
224{
225 FILE *input, *output;
226
227 fprintf(stderr, "Start thread for connection %d.\n", work->conn);
228
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000229 ps();
230
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000231 input = fdopen(work->conn, "r");
232 if (input == NULL) {
233 oprogname();
234 perror("can't create input stream");
235 goto done;
236 }
237
238 output = fdopen(work->conn, "w");
239 if (output == NULL) {
240 oprogname();
241 perror("can't create output stream");
242 fclose(input);
243 goto done;
244 }
245
246 setvbuf(input, NULL, _IONBF, 0);
247 setvbuf(output, NULL, _IONBF, 0);
248
249 run_interpreter(input, output);
250
251 fclose(input);
252 fclose(output);
253
254 done:
255 fprintf(stderr, "End thread for connection %d.\n", work->conn);
256 close(work->conn);
257 free(work);
258}
259
260static void
261oprogname()
262{
263 int save = errno;
264 fprintf(stderr, "%s: ", progname);
265 errno = save;
266}
267
268static void
269run_interpreter(FILE *input, FILE *output)
270{
271 PyThreadState *tstate;
272 PyObject *new_stdin, *new_stdout;
Guido van Rossum630924f1997-07-25 20:59:55 +0000273 PyObject *mainmod, *globals;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000274 char buffer[1000];
275 char *p, *q;
276 int n, end;
277
Guido van Rossum630924f1997-07-25 20:59:55 +0000278 PyEval_AcquireLock();
279 tstate = Py_NewInterpreter();
280 if (tstate == NULL) {
281 fprintf(output, "Sorry -- can't create an interpreter\n");
282 return;
283 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000284
Guido van Rossum630924f1997-07-25 20:59:55 +0000285 mainmod = PyImport_AddModule("__main__");
286 globals = PyModule_GetDict(mainmod);
287 Py_INCREF(globals);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000288
289 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
290 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
291
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000292 PySys_SetObject("stdin", new_stdin);
293 PySys_SetObject("stdout", new_stdout);
294 PySys_SetObject("stderr", new_stdout);
295
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000296 for (n = 1; !PyErr_Occurred(); n++) {
297 Py_BEGIN_ALLOW_THREADS
298 fprintf(output, "%d> ", n);
299 p = fgets(buffer, sizeof buffer, input);
300 Py_END_ALLOW_THREADS
301
302 if (p == NULL)
303 break;
304 if (p[0] == '\377' && p[1] == '\354')
305 break;
306
307 q = strrchr(p, '\r');
308 if (q && q[1] == '\n' && q[2] == '\0') {
309 *q++ = '\n';
310 *q++ = '\0';
311 }
312
313 while (*p && isspace(*p))
314 p++;
315 if (p[0] == '#' || p[0] == '\0')
316 continue;
317
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000318 end = run_command(buffer, globals);
319 if (end < 0)
320 PyErr_Print();
321
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000322 if (end)
323 break;
324 }
325
326 Py_XDECREF(globals);
327 Py_XDECREF(new_stdin);
328 Py_XDECREF(new_stdout);
329
Guido van Rossum630924f1997-07-25 20:59:55 +0000330 Py_EndInterpreter(tstate);
331 PyEval_ReleaseLock();
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000332
333 fprintf(output, "Goodbye!\n");
334}
335
336static int
337run_command(char *buffer, PyObject *globals)
338{
339 PyObject *m, *d, *v;
Guido van Rossum630924f1997-07-25 20:59:55 +0000340 fprintf(stderr, "run_command: %s", buffer);
341 if (strchr(buffer, '\n') == NULL)
342 fprintf(stderr, "\n");
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000343 v = PyRun_String(buffer, Py_single_input, globals, globals);
344 if (v == NULL) {
345 if (PyErr_Occurred() == PyExc_SystemExit) {
346 PyErr_Clear();
347 return 1;
348 }
349 PyErr_Print();
350 return 0;
351 }
352 Py_DECREF(v);
353 return 0;
354}
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000355
356static void
357ps()
358{
359 char buffer[100];
360 sprintf(buffer, "ps -l -p %d </dev/null | tail +2l\n", getpid());
361 system(buffer);
362}