Home | History | Annotate | Download | only in pysvr
      1 /* A multi-threaded telnet-like server that gives a Python prompt.
      2 
      3 Usage: pysvr [port]
      4 
      5 For security reasons, it only accepts requests from the current host.
      6 This can still be insecure, but restricts violations from people who
      7 can 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 
     30 extern int Py_VerboseFlag;
     31 
     32 #ifndef PORT
     33 #define PORT 4000
     34 #endif
     35 
     36 struct workorder {
     37     int conn;
     38     struct sockaddr_in addr;
     39 };
     40 
     41 /* Forward */
     42 static void init_python(void);
     43 static void usage(void);
     44 static void oprogname(void);
     45 static void main_thread(int);
     46 static void create_thread(int, struct sockaddr_in *);
     47 static void *service_thread(struct workorder *);
     48 static void run_interpreter(FILE *, FILE *);
     49 static int run_command(char *, PyObject *);
     50 static void ps(void);
     51 
     52 static char *progname = "pysvr";
     53 
     54 static PyThreadState *gtstate;
     55 
     56 main(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 
     94 static char usage_line[] = "usage: %s [port]\n";
     95 
     96 static void
     97 usage(void)
     98 {
     99     fprintf(stderr, usage_line, progname);
    100     exit(2);
    101 }
    102 
    103 static void
    104 main_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 
    184 static void
    185 create_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 
    215 static PyThreadState *the_tstate;
    216 static PyInterpreterState *the_interp;
    217 static PyObject *the_builtins;
    218 
    219 static void
    220 init_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 
    229 static void *
    230 service_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 
    267 static void
    268 oprogname(void)
    269 {
    270     int save = errno;
    271     fprintf(stderr, "%s: ", progname);
    272     errno = save;
    273 }
    274 
    275 static void
    276 run_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 
    343 static int
    344 run_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 
    363 static void
    364 ps(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