| /* A multi-threaded telnet-like server that gives a Python prompt. | |
| Usage: pysvr [port] | |
| For security reasons, it only accepts requests from the current host. | |
| This can still be insecure, but restricts violations from people who | |
| can log in on your machine. Use with caution! | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include <errno.h> | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <pthread.h> | |
| #include <getopt.h> | |
| /* XXX Umpfh. | |
| Python.h defines a typedef destructor, which conflicts with pthread.h. | |
| So Python.h must be included after pthread.h. */ | |
| #include "Python.h" | |
| extern int Py_VerboseFlag; | |
| #ifndef PORT | |
| #define PORT 4000 | |
| #endif | |
| struct workorder { | |
| int conn; | |
| struct sockaddr_in addr; | |
| }; | |
| /* Forward */ | |
| static void init_python(void); | |
| static void usage(void); | |
| static void oprogname(void); | |
| static void main_thread(int); | |
| static void create_thread(int, struct sockaddr_in *); | |
| static void *service_thread(struct workorder *); | |
| static void run_interpreter(FILE *, FILE *); | |
| static int run_command(char *, PyObject *); | |
| static void ps(void); | |
| static char *progname = "pysvr"; | |
| static PyThreadState *gtstate; | |
| main(int argc, char **argv) | |
| { | |
| int port = PORT; | |
| int c; | |
| if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0') | |
| progname = argv[0]; | |
| while ((c = getopt(argc, argv, "v")) != EOF) { | |
| switch (c) { | |
| case 'v': | |
| Py_VerboseFlag++; | |
| break; | |
| default: | |
| usage(); | |
| } | |
| } | |
| if (optind < argc) { | |
| if (optind+1 < argc) { | |
| oprogname(); | |
| fprintf(stderr, "too many arguments\n"); | |
| usage(); | |
| } | |
| port = atoi(argv[optind]); | |
| if (port <= 0) { | |
| fprintf(stderr, "bad port (%s)\n", argv[optind]); | |
| usage(); | |
| } | |
| } | |
| main_thread(port); | |
| fprintf(stderr, "Bye.\n"); | |
| exit(0); | |
| } | |
| static char usage_line[] = "usage: %s [port]\n"; | |
| static void | |
| usage(void) | |
| { | |
| fprintf(stderr, usage_line, progname); | |
| exit(2); | |
| } | |
| static void | |
| main_thread(int port) | |
| { | |
| int sock, conn, size, i; | |
| struct sockaddr_in addr, clientaddr; | |
| sock = socket(PF_INET, SOCK_STREAM, 0); | |
| if (sock < 0) { | |
| oprogname(); | |
| perror("can't create socket"); | |
| exit(1); | |
| } | |
| #ifdef SO_REUSEADDR | |
| i = 1; | |
| setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i); | |
| #endif | |
| memset((char *)&addr, '\0', sizeof addr); | |
| addr.sin_family = AF_INET; | |
| addr.sin_port = htons(port); | |
| addr.sin_addr.s_addr = 0L; | |
| if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) { | |
| oprogname(); | |
| perror("can't bind socket to address"); | |
| exit(1); | |
| } | |
| if (listen(sock, 5) < 0) { | |
| oprogname(); | |
| perror("can't listen on socket"); | |
| exit(1); | |
| } | |
| fprintf(stderr, "Listening on port %d...\n", port); | |
| for (i = 0; ; i++) { | |
| size = sizeof clientaddr; | |
| memset((char *) &clientaddr, '\0', size); | |
| conn = accept(sock, (struct sockaddr *) &clientaddr, &size); | |
| if (conn < 0) { | |
| oprogname(); | |
| perror("can't accept connection from socket"); | |
| exit(1); | |
| } | |
| size = sizeof addr; | |
| memset((char *) &addr, '\0', size); | |
| if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) { | |
| oprogname(); | |
| perror("can't get socket name of connection"); | |
| exit(1); | |
| } | |
| if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) { | |
| oprogname(); | |
| perror("connection from non-local host refused"); | |
| fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n", | |
| ntohl(addr.sin_addr.s_addr), | |
| ntohl(clientaddr.sin_addr.s_addr)); | |
| close(conn); | |
| continue; | |
| } | |
| if (i == 4) { | |
| close(conn); | |
| break; | |
| } | |
| create_thread(conn, &clientaddr); | |
| } | |
| close(sock); | |
| if (gtstate) { | |
| PyEval_AcquireThread(gtstate); | |
| gtstate = NULL; | |
| Py_Finalize(); | |
| /* And a second time, just because we can. */ | |
| Py_Finalize(); /* This should be harmless. */ | |
| } | |
| exit(0); | |
| } | |
| static void | |
| create_thread(int conn, struct sockaddr_in *addr) | |
| { | |
| struct workorder *work; | |
| pthread_t tdata; | |
| work = malloc(sizeof(struct workorder)); | |
| if (work == NULL) { | |
| oprogname(); | |
| fprintf(stderr, "out of memory for thread.\n"); | |
| close(conn); | |
| return; | |
| } | |
| work->conn = conn; | |
| work->addr = *addr; | |
| init_python(); | |
| if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) { | |
| oprogname(); | |
| perror("can't create new thread"); | |
| close(conn); | |
| return; | |
| } | |
| if (pthread_detach(tdata) < 0) { | |
| oprogname(); | |
| perror("can't detach from thread"); | |
| } | |
| } | |
| static PyThreadState *the_tstate; | |
| static PyInterpreterState *the_interp; | |
| static PyObject *the_builtins; | |
| static void | |
| init_python(void) | |
| { | |
| if (gtstate) | |
| return; | |
| Py_Initialize(); /* Initialize the interpreter */ | |
| PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ | |
| gtstate = PyEval_SaveThread(); /* Release the thread state */ | |
| } | |
| static void * | |
| service_thread(struct workorder *work) | |
| { | |
| FILE *input, *output; | |
| fprintf(stderr, "Start thread for connection %d.\n", work->conn); | |
| ps(); | |
| input = fdopen(work->conn, "r"); | |
| if (input == NULL) { | |
| oprogname(); | |
| perror("can't create input stream"); | |
| goto done; | |
| } | |
| output = fdopen(work->conn, "w"); | |
| if (output == NULL) { | |
| oprogname(); | |
| perror("can't create output stream"); | |
| fclose(input); | |
| goto done; | |
| } | |
| setvbuf(input, NULL, _IONBF, 0); | |
| setvbuf(output, NULL, _IONBF, 0); | |
| run_interpreter(input, output); | |
| fclose(input); | |
| fclose(output); | |
| done: | |
| fprintf(stderr, "End thread for connection %d.\n", work->conn); | |
| close(work->conn); | |
| free(work); | |
| } | |
| static void | |
| oprogname(void) | |
| { | |
| int save = errno; | |
| fprintf(stderr, "%s: ", progname); | |
| errno = save; | |
| } | |
| static void | |
| run_interpreter(FILE *input, FILE *output) | |
| { | |
| PyThreadState *tstate; | |
| PyObject *new_stdin, *new_stdout; | |
| PyObject *mainmod, *globals; | |
| char buffer[1000]; | |
| char *p, *q; | |
| int n, end; | |
| PyEval_AcquireLock(); | |
| tstate = Py_NewInterpreter(); | |
| if (tstate == NULL) { | |
| fprintf(output, "Sorry -- can't create an interpreter\n"); | |
| return; | |
| } | |
| mainmod = PyImport_AddModule("__main__"); | |
| globals = PyModule_GetDict(mainmod); | |
| Py_INCREF(globals); | |
| new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL); | |
| new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL); | |
| PySys_SetObject("stdin", new_stdin); | |
| PySys_SetObject("stdout", new_stdout); | |
| PySys_SetObject("stderr", new_stdout); | |
| for (n = 1; !PyErr_Occurred(); n++) { | |
| Py_BEGIN_ALLOW_THREADS | |
| fprintf(output, "%d> ", n); | |
| p = fgets(buffer, sizeof buffer, input); | |
| Py_END_ALLOW_THREADS | |
| if (p == NULL) | |
| break; | |
| if (p[0] == '\377' && p[1] == '\354') | |
| break; | |
| q = strrchr(p, '\r'); | |
| if (q && q[1] == '\n' && q[2] == '\0') { | |
| *q++ = '\n'; | |
| *q++ = '\0'; | |
| } | |
| while (*p && isspace(*p)) | |
| p++; | |
| if (p[0] == '#' || p[0] == '\0') | |
| continue; | |
| end = run_command(buffer, globals); | |
| if (end < 0) | |
| PyErr_Print(); | |
| if (end) | |
| break; | |
| } | |
| Py_XDECREF(globals); | |
| Py_XDECREF(new_stdin); | |
| Py_XDECREF(new_stdout); | |
| Py_EndInterpreter(tstate); | |
| PyEval_ReleaseLock(); | |
| fprintf(output, "Goodbye!\n"); | |
| } | |
| static int | |
| run_command(char *buffer, PyObject *globals) | |
| { | |
| PyObject *m, *d, *v; | |
| fprintf(stderr, "run_command: %s", buffer); | |
| if (strchr(buffer, '\n') == NULL) | |
| fprintf(stderr, "\n"); | |
| v = PyRun_String(buffer, Py_single_input, globals, globals); | |
| if (v == NULL) { | |
| if (PyErr_Occurred() == PyExc_SystemExit) { | |
| PyErr_Clear(); | |
| return 1; | |
| } | |
| PyErr_Print(); | |
| return 0; | |
| } | |
| Py_DECREF(v); | |
| return 0; | |
| } | |
| static void | |
| ps(void) | |
| { | |
| char buffer[100]; | |
| PyOS_snprintf(buffer, sizeof(buffer), | |
| "ps -l -p %d </dev/null | sed 1d\n", getpid()); | |
| system(buffer); | |
| } |