blob: c70863f65092770bca19680746a562acd0c8a26b [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{
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000195 PyEval_InitThreads(); /* Create and acquire the interpreter lock */
Guido van Rossum630924f1997-07-25 20:59:55 +0000196 PyEval_ReleaseLock(); /* Release the lock */
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000197}
198
199static void *
200service_thread(struct workorder *work)
201{
202 FILE *input, *output;
203
204 fprintf(stderr, "Start thread for connection %d.\n", work->conn);
205
206 input = fdopen(work->conn, "r");
207 if (input == NULL) {
208 oprogname();
209 perror("can't create input stream");
210 goto done;
211 }
212
213 output = fdopen(work->conn, "w");
214 if (output == NULL) {
215 oprogname();
216 perror("can't create output stream");
217 fclose(input);
218 goto done;
219 }
220
221 setvbuf(input, NULL, _IONBF, 0);
222 setvbuf(output, NULL, _IONBF, 0);
223
224 run_interpreter(input, output);
225
226 fclose(input);
227 fclose(output);
228
229 done:
230 fprintf(stderr, "End thread for connection %d.\n", work->conn);
231 close(work->conn);
232 free(work);
233}
234
235static void
236oprogname()
237{
238 int save = errno;
239 fprintf(stderr, "%s: ", progname);
240 errno = save;
241}
242
243static void
244run_interpreter(FILE *input, FILE *output)
245{
246 PyThreadState *tstate;
247 PyObject *new_stdin, *new_stdout;
Guido van Rossum630924f1997-07-25 20:59:55 +0000248 PyObject *mainmod, *globals;
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000249 char buffer[1000];
250 char *p, *q;
251 int n, end;
252
Guido van Rossum630924f1997-07-25 20:59:55 +0000253 PyEval_AcquireLock();
254 tstate = Py_NewInterpreter();
255 if (tstate == NULL) {
256 fprintf(output, "Sorry -- can't create an interpreter\n");
257 return;
258 }
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000259
Guido van Rossum630924f1997-07-25 20:59:55 +0000260 mainmod = PyImport_AddModule("__main__");
261 globals = PyModule_GetDict(mainmod);
262 Py_INCREF(globals);
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000263
264 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
265 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
266
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000267 for (n = 1; !PyErr_Occurred(); n++) {
268 Py_BEGIN_ALLOW_THREADS
269 fprintf(output, "%d> ", n);
270 p = fgets(buffer, sizeof buffer, input);
271 Py_END_ALLOW_THREADS
272
273 if (p == NULL)
274 break;
275 if (p[0] == '\377' && p[1] == '\354')
276 break;
277
278 q = strrchr(p, '\r');
279 if (q && q[1] == '\n' && q[2] == '\0') {
280 *q++ = '\n';
281 *q++ = '\0';
282 }
283
284 while (*p && isspace(*p))
285 p++;
286 if (p[0] == '#' || p[0] == '\0')
287 continue;
288
289 PySys_SetObject("stdin", new_stdin);
290 PySys_SetObject("stdout", new_stdout);
291 PySys_SetObject("stderr", new_stdout);
292
293 end = run_command(buffer, globals);
294 if (end < 0)
295 PyErr_Print();
296
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000297 if (end)
298 break;
299 }
300
301 Py_XDECREF(globals);
302 Py_XDECREF(new_stdin);
303 Py_XDECREF(new_stdout);
304
Guido van Rossum630924f1997-07-25 20:59:55 +0000305 Py_EndInterpreter(tstate);
306 PyEval_ReleaseLock();
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000307
308 fprintf(output, "Goodbye!\n");
309}
310
311static int
312run_command(char *buffer, PyObject *globals)
313{
314 PyObject *m, *d, *v;
Guido van Rossum630924f1997-07-25 20:59:55 +0000315 fprintf(stderr, "run_command: %s", buffer);
316 if (strchr(buffer, '\n') == NULL)
317 fprintf(stderr, "\n");
Guido van Rossum5c8b9911997-07-19 21:00:47 +0000318 v = PyRun_String(buffer, Py_single_input, globals, globals);
319 if (v == NULL) {
320 if (PyErr_Occurred() == PyExc_SystemExit) {
321 PyErr_Clear();
322 return 1;
323 }
324 PyErr_Print();
325 return 0;
326 }
327 Py_DECREF(v);
328 return 0;
329}