1/* 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
11#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#include <getopt.h>
23
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
28#include "Python.h"
29
30extern int Py_VerboseFlag;
31
32#ifndef PORT
33#define PORT 4000
34#endif
35
36struct 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 *);
50static void ps(void);
51
52static char *progname = "pysvr";
53
54static PyThreadState *gtstate;
55
56main(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
64    while ((c = getopt(argc, argv, "v")) != EOF) {
65        switch (c) {
66        case 'v':
67            Py_VerboseFlag++;
68            break;
69        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
97usage(void)
98{
99    fprintf(stderr, usage_line, progname);
100    exit(2);
101}
102
103static void
104main_thread(int port)
105{
106    int sock, conn, size, i;
107    struct sockaddr_in addr, clientaddr;
108
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
116#ifdef SO_REUSEADDR
117    i = 1;
118    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i);
119#endif
120
121    memset((char *)&addr, '\0', sizeof addr);
122    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
139    for (i = 0; ; i++) {
140        size = sizeof clientaddr;
141        memset((char *) &clientaddr, '\0', size);
142        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
149        size = sizeof addr;
150        memset((char *) &addr, '\0', size);
151        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        }
165        if (i == 4) {
166            close(conn);
167            break;
168        }
169        create_thread(conn, &clientaddr);
170    }
171
172    close(sock);
173
174    if (gtstate) {
175        PyEval_AcquireThread(gtstate);
176        gtstate = NULL;
177        Py_Finalize();
178        /* And a second time, just because we can. */
179        Py_Finalize(); /* This should be harmless. */
180    }
181    exit(0);
182}
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
220init_python(void)
221{
222    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 */
227}
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
236    ps();
237
238    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
268oprogname(void)
269{
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;
280    PyObject *mainmod, *globals;
281    char buffer[1000];
282    char *p, *q;
283    int n, end;
284
285    PyEval_AcquireLock();
286    tstate = Py_NewInterpreter();
287    if (tstate == NULL) {
288        fprintf(output, "Sorry -- can't create an interpreter\n");
289        return;
290    }
291
292    mainmod = PyImport_AddModule("__main__");
293    globals = PyModule_GetDict(mainmod);
294    Py_INCREF(globals);
295
296    new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
297    new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
298
299    PySys_SetObject("stdin", new_stdin);
300    PySys_SetObject("stdout", new_stdout);
301    PySys_SetObject("stderr", new_stdout);
302
303    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
325        end = run_command(buffer, globals);
326        if (end < 0)
327            PyErr_Print();
328
329        if (end)
330            break;
331    }
332
333    Py_XDECREF(globals);
334    Py_XDECREF(new_stdin);
335    Py_XDECREF(new_stdout);
336
337    Py_EndInterpreter(tstate);
338    PyEval_ReleaseLock();
339
340    fprintf(output, "Goodbye!\n");
341}
342
343static int
344run_command(char *buffer, PyObject *globals)
345{
346    PyObject *m, *d, *v;
347    fprintf(stderr, "run_command: %s", buffer);
348    if (strchr(buffer, '\n') == NULL)
349        fprintf(stderr, "\n");
350    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}
362
363static void
364ps(void)
365{
366    char buffer[100];
367    PyOS_snprintf(buffer, sizeof(buffer),
368                  "ps -l -p %d </dev/null | sed 1d\n", getpid());
369    system(buffer);
370}
371