blob: c651648362cb632f2fedd3920836e006458d662f [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();
171 }
172 exit(0);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000173}
174
175static void
176create_thread(int conn, struct sockaddr_in *addr)
177{
178 struct workorder *work;
179 pthread_t tdata;
180
181 work = malloc(sizeof(struct workorder));
182 if (work == NULL) {
183 oprogname();
184 fprintf(stderr, "out of memory for thread.\n");
185 close(conn);
186 return;
187 }
188 work->conn = conn;
189 work->addr = *addr;
190
191 init_python();
192
193 if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) {
194 oprogname();
195 perror("can't create new thread");
196 close(conn);
197 return;
198 }
199
200 if (pthread_detach(tdata) < 0) {
201 oprogname();
202 perror("can't detach from thread");
203 }
204}
205
206static PyThreadState *the_tstate;
207static PyInterpreterState *the_interp;
208static PyObject *the_builtins;
209
210static void
211init_python()
212{
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000213 if (gtstate)
214 return;
215 Py_Initialize(); /* Initialize the interpreter */
216 PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */
217 gtstate = PyEval_SaveThread(); /* Release the thread state */
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000218}
219
220static void *
221service_thread(struct workorder *work)
222{
223 FILE *input, *output;
224
225 fprintf(stderr, "Start thread for connection %d.\n", work->conn);
226
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000227 ps();
228
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000229 input = fdopen(work->conn, "r");
230 if (input == NULL) {
231 oprogname();
232 perror("can't create input stream");
233 goto done;
234 }
235
236 output = fdopen(work->conn, "w");
237 if (output == NULL) {
238 oprogname();
239 perror("can't create output stream");
240 fclose(input);
241 goto done;
242 }
243
244 setvbuf(input, NULL, _IONBF, 0);
245 setvbuf(output, NULL, _IONBF, 0);
246
247 run_interpreter(input, output);
248
249 fclose(input);
250 fclose(output);
251
252 done:
253 fprintf(stderr, "End thread for connection %d.\n", work->conn);
254 close(work->conn);
255 free(work);
256}
257
258static void
259oprogname()
260{
261 int save = errno;
262 fprintf(stderr, "%s: ", progname);
263 errno = save;
264}
265
266static void
267run_interpreter(FILE *input, FILE *output)
268{
269 PyThreadState *tstate;
270 PyObject *new_stdin, *new_stdout;
Guido van Rossum630924f1997-07-25 20:59:55 +0000271 PyObject *mainmod, *globals;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000272 char buffer[1000];
273 char *p, *q;
274 int n, end;
275
Guido van Rossum630924f1997-07-25 20:59:55 +0000276 PyEval_AcquireLock();
277 tstate = Py_NewInterpreter();
278 if (tstate == NULL) {
279 fprintf(output, "Sorry -- can't create an interpreter\n");
280 return;
281 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000282
Guido van Rossum630924f1997-07-25 20:59:55 +0000283 mainmod = PyImport_AddModule("__main__");
284 globals = PyModule_GetDict(mainmod);
285 Py_INCREF(globals);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000286
287 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
288 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
289
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000290 PySys_SetObject("stdin", new_stdin);
291 PySys_SetObject("stdout", new_stdout);
292 PySys_SetObject("stderr", new_stdout);
293
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000294 for (n = 1; !PyErr_Occurred(); n++) {
295 Py_BEGIN_ALLOW_THREADS
296 fprintf(output, "%d> ", n);
297 p = fgets(buffer, sizeof buffer, input);
298 Py_END_ALLOW_THREADS
299
300 if (p == NULL)
301 break;
302 if (p[0] == '\377' && p[1] == '\354')
303 break;
304
305 q = strrchr(p, '\r');
306 if (q && q[1] == '\n' && q[2] == '\0') {
307 *q++ = '\n';
308 *q++ = '\0';
309 }
310
311 while (*p && isspace(*p))
312 p++;
313 if (p[0] == '#' || p[0] == '\0')
314 continue;
315
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000316 end = run_command(buffer, globals);
317 if (end < 0)
318 PyErr_Print();
319
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000320 if (end)
321 break;
322 }
323
324 Py_XDECREF(globals);
325 Py_XDECREF(new_stdin);
326 Py_XDECREF(new_stdout);
327
Guido van Rossum630924f1997-07-25 20:59:55 +0000328 Py_EndInterpreter(tstate);
329 PyEval_ReleaseLock();
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000330
331 fprintf(output, "Goodbye!\n");
332}
333
334static int
335run_command(char *buffer, PyObject *globals)
336{
337 PyObject *m, *d, *v;
Guido van Rossum630924f1997-07-25 20:59:55 +0000338 fprintf(stderr, "run_command: %s", buffer);
339 if (strchr(buffer, '\n') == NULL)
340 fprintf(stderr, "\n");
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000341 v = PyRun_String(buffer, Py_single_input, globals, globals);
342 if (v == NULL) {
343 if (PyErr_Occurred() == PyExc_SystemExit) {
344 PyErr_Clear();
345 return 1;
346 }
347 PyErr_Print();
348 return 0;
349 }
350 Py_DECREF(v);
351 return 0;
352}
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000353
354static void
355ps()
356{
357 char buffer[100];
358 sprintf(buffer, "ps -l -p %d </dev/null | tail +2l\n", getpid());
359 system(buffer);
360}