blob: e41d88e1ae1c84fe184314a67bcdb13740d39607 [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 Rossum643f8f61997-12-25 04:48:51 +0000114#ifdef SO_REUSEADDR
115 i = 1;
116 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i);
117#endif
118
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000119 memset((char *)&addr, '\0', sizeof addr);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000120 addr.sin_family = AF_INET;
121 addr.sin_port = htons(port);
122 addr.sin_addr.s_addr = 0L;
123 if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) {
124 oprogname();
125 perror("can't bind socket to address");
126 exit(1);
127 }
128
129 if (listen(sock, 5) < 0) {
130 oprogname();
131 perror("can't listen on socket");
132 exit(1);
133 }
134
135 fprintf(stderr, "Listening on port %d...\n", port);
136
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000137 for (i = 0; ; i++) {
Guido van Rossume0c69011997-07-19 21:33:10 +0000138 size = sizeof clientaddr;
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000139 memset((char *) &clientaddr, '\0', size);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000140 conn = accept(sock, (struct sockaddr *) &clientaddr, &size);
141 if (conn < 0) {
142 oprogname();
143 perror("can't accept connection from socket");
144 exit(1);
145 }
146
Guido van Rossume0c69011997-07-19 21:33:10 +0000147 size = sizeof addr;
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000148 memset((char *) &addr, '\0', size);
Guido van Rossume0c69011997-07-19 21:33:10 +0000149 if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) {
150 oprogname();
151 perror("can't get socket name of connection");
152 exit(1);
153 }
154 if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) {
155 oprogname();
156 perror("connection from non-local host refused");
157 fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n",
158 ntohl(addr.sin_addr.s_addr),
159 ntohl(clientaddr.sin_addr.s_addr));
160 close(conn);
161 continue;
162 }
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000163 if (i == 4) {
164 close(conn);
165 break;
166 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000167 create_thread(conn, &clientaddr);
168 }
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000169
170 close(sock);
171
172 if (gtstate) {
173 PyEval_AcquireThread(gtstate);
174 gtstate = NULL;
175 Py_Finalize();
Guido van Rossumaa615051997-08-20 22:40:18 +0000176 Py_Finalize();
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000177 }
178 exit(0);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000179}
180
181static void
182create_thread(int conn, struct sockaddr_in *addr)
183{
184 struct workorder *work;
185 pthread_t tdata;
186
187 work = malloc(sizeof(struct workorder));
188 if (work == NULL) {
189 oprogname();
190 fprintf(stderr, "out of memory for thread.\n");
191 close(conn);
192 return;
193 }
194 work->conn = conn;
195 work->addr = *addr;
196
197 init_python();
198
199 if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) {
200 oprogname();
201 perror("can't create new thread");
202 close(conn);
203 return;
204 }
205
206 if (pthread_detach(tdata) < 0) {
207 oprogname();
208 perror("can't detach from thread");
209 }
210}
211
212static PyThreadState *the_tstate;
213static PyInterpreterState *the_interp;
214static PyObject *the_builtins;
215
216static void
217init_python()
218{
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000219 if (gtstate)
220 return;
221 Py_Initialize(); /* Initialize the interpreter */
222 PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */
223 gtstate = PyEval_SaveThread(); /* Release the thread state */
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000224}
225
226static void *
227service_thread(struct workorder *work)
228{
229 FILE *input, *output;
230
231 fprintf(stderr, "Start thread for connection %d.\n", work->conn);
232
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000233 ps();
234
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000235 input = fdopen(work->conn, "r");
236 if (input == NULL) {
237 oprogname();
238 perror("can't create input stream");
239 goto done;
240 }
241
242 output = fdopen(work->conn, "w");
243 if (output == NULL) {
244 oprogname();
245 perror("can't create output stream");
246 fclose(input);
247 goto done;
248 }
249
250 setvbuf(input, NULL, _IONBF, 0);
251 setvbuf(output, NULL, _IONBF, 0);
252
253 run_interpreter(input, output);
254
255 fclose(input);
256 fclose(output);
257
258 done:
259 fprintf(stderr, "End thread for connection %d.\n", work->conn);
260 close(work->conn);
261 free(work);
262}
263
264static void
265oprogname()
266{
267 int save = errno;
268 fprintf(stderr, "%s: ", progname);
269 errno = save;
270}
271
272static void
273run_interpreter(FILE *input, FILE *output)
274{
275 PyThreadState *tstate;
276 PyObject *new_stdin, *new_stdout;
Guido van Rossum630924f1997-07-25 20:59:55 +0000277 PyObject *mainmod, *globals;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000278 char buffer[1000];
279 char *p, *q;
280 int n, end;
281
Guido van Rossum630924f1997-07-25 20:59:55 +0000282 PyEval_AcquireLock();
283 tstate = Py_NewInterpreter();
284 if (tstate == NULL) {
285 fprintf(output, "Sorry -- can't create an interpreter\n");
286 return;
287 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000288
Guido van Rossum630924f1997-07-25 20:59:55 +0000289 mainmod = PyImport_AddModule("__main__");
290 globals = PyModule_GetDict(mainmod);
291 Py_INCREF(globals);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000292
293 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
294 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
295
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000296 PySys_SetObject("stdin", new_stdin);
297 PySys_SetObject("stdout", new_stdout);
298 PySys_SetObject("stderr", new_stdout);
299
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000300 for (n = 1; !PyErr_Occurred(); n++) {
301 Py_BEGIN_ALLOW_THREADS
302 fprintf(output, "%d> ", n);
303 p = fgets(buffer, sizeof buffer, input);
304 Py_END_ALLOW_THREADS
305
306 if (p == NULL)
307 break;
308 if (p[0] == '\377' && p[1] == '\354')
309 break;
310
311 q = strrchr(p, '\r');
312 if (q && q[1] == '\n' && q[2] == '\0') {
313 *q++ = '\n';
314 *q++ = '\0';
315 }
316
317 while (*p && isspace(*p))
318 p++;
319 if (p[0] == '#' || p[0] == '\0')
320 continue;
321
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000322 end = run_command(buffer, globals);
323 if (end < 0)
324 PyErr_Print();
325
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000326 if (end)
327 break;
328 }
329
330 Py_XDECREF(globals);
331 Py_XDECREF(new_stdin);
332 Py_XDECREF(new_stdout);
333
Guido van Rossum630924f1997-07-25 20:59:55 +0000334 Py_EndInterpreter(tstate);
335 PyEval_ReleaseLock();
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000336
337 fprintf(output, "Goodbye!\n");
338}
339
340static int
341run_command(char *buffer, PyObject *globals)
342{
343 PyObject *m, *d, *v;
Guido van Rossum630924f1997-07-25 20:59:55 +0000344 fprintf(stderr, "run_command: %s", buffer);
345 if (strchr(buffer, '\n') == NULL)
346 fprintf(stderr, "\n");
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000347 v = PyRun_String(buffer, Py_single_input, globals, globals);
348 if (v == NULL) {
349 if (PyErr_Occurred() == PyExc_SystemExit) {
350 PyErr_Clear();
351 return 1;
352 }
353 PyErr_Print();
354 return 0;
355 }
356 Py_DECREF(v);
357 return 0;
358}
Guido van Rossumc46d22e1997-08-02 02:02:22 +0000359
360static void
361ps()
362{
363 char buffer[100];
364 sprintf(buffer, "ps -l -p %d </dev/null | tail +2l\n", getpid());
365 system(buffer);
366}