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