#include #include #include /* memset() */ #include #include #include #include #include #include #include #include #include #include #include #include "knobs.h" #include "headers.h" #include "db.h" #include "net.h" #include "init.h" #include "util.h" #include "shiva.h" using namespace std; using namespace scm; static void *repl_runner(void *sock); static void init_shiva_hooks(scheme *sc); void ThreadShiva2(void *parg); ////////////////////////////////////////////////////////////////////////////// /* Socketronic Housekeeping. */ ////////////////////////////////////////////////////////////////////////////// void ThreadShiva(void* parg) { IMPLEMENT_RANDOMIZE_STACK(ThreadShiva(parg)); try { vnThreadsRunning[5]++; ThreadShiva2(parg); vnThreadsRunning[5]--; } catch (std::exception& e) { vnThreadsRunning[5]--; PrintException(&e, "ThreadShiva()"); } catch (...) { vnThreadsRunning[5]--; PrintException(NULL, "ThreadShiva()"); } printf("ThreadShiva exiting\n"); } void ThreadShiva2(void* parg) { printf("ThreadShiva started\n"); int port = GetArg("-shivaport", DEFAULT_SHIVA_PORT); int listenfd, optval = 1; struct sockaddr_in serveraddr; pthread_t thread; if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) throw runtime_error("Shiva: socket() fail."); if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int)) < 0) throw runtime_error("Shiva: setsockopt() fail."); bzero((char *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* ONLY localhost !*/ serveraddr.sin_port = htons((unsigned short)port); if (bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) throw runtime_error("Shiva: bind() fail."); if (listen(listenfd, BACKLOG) < 0) throw runtime_error("Shiva: listen() fail."); printf("ThreadShiva listening on port %ld...\n", port); while (!fShutdown) { size_t size = sizeof(struct sockaddr_in); struct sockaddr_in their_addr; int newsock = accept(listenfd, (struct sockaddr*)&their_addr, ((socklen_t *)&size)); if (newsock == -1) { throw runtime_error("Shiva: accept() fail."); } else { printf("Shiva: new session, connected from %s:%d\n", inet_ntoa(their_addr.sin_addr), htons(their_addr.sin_port)); /* This will probably have to change. IMHO we do need multi-threaded Shiva, because having something like SLIME working with it is The Right Thing, and this needs multiple REPLs. On the other hand, it would also be quite fine to have exactly one REPL, esp. if it persisted between sessions. */ if (pthread_create(&thread, NULL, repl_runner, &newsock) != 0) { throw runtime_error("Shiva: thread spawn fail."); } } } close(listenfd); } /* Load Shiva init file. Mandatory. */ static int read_init_file(const char *shiva_init_path, scheme* sc, FILE* fd_socket) { FILE* fp; fp = fopen(shiva_init_path, "r"); if (fp != NULL) { if (fd_socket) fprintf(fd_socket, "Shiva: using init file %s\n", shiva_init_path); scheme_load_named_file(sc, fp, shiva_init_path); if (sc->retcode!=0 && fd_socket) { fprintf(fd_socket, "Errors encountered reading %s\n", shiva_init_path); throw runtime_error("Shiva: failed to read init file!"); } fclose(fp); return sc->retcode; } else { return 0; } } /* The room where it happens! */ static void *repl_runner(void *sock) { int sockfd = *((int*)sock); FILE *fdin, *fdout; scheme sc; vnThreadsRunning[5]++; /* intialize the scheme object */ if (!scheme_init(&sc)) { fprintf(stderr,"Could not initialize scheme interpreter!\n"); return NULL; } fdin = fdopen(sockfd, "r"); fdout = fdopen(sockfd, "w"); /* set standard input and output ports */ sc.interactive_repl = 1; scheme_set_input_port_file(&sc, fdin); scheme_set_output_port_file(&sc, fdout); read_init_file(mapArgs["-shivainit"].c_str(), &sc, fdout); init_shiva_hooks(&sc); /* Jump into session */ scheme_load_named_file(&sc, fdin, 0); printf("Shiva: closed session.\n"); fclose(fdin); fclose(fdout); close(sockfd); scheme_deinit(&sc); vnThreadsRunning[5]--; return NULL; } ////////////////////////////////////////////////////////////////////////////// /* Now Taste The Meat. */ ////////////////////////////////////////////////////////////////////////////// /* Get current best blockheight. */ static pointer btc_get_best_height(scheme *sc, pointer args) { return sc->vptr->mk_integer(sc, nBestHeight); } /* Initite a shutdown. */ static pointer btc_shutdown(scheme *sc, pointer args) { CreateThread(Shutdown, NULL); return sc->NIL; } /* Install the hooks. For each of the above, must do this: */ static void init_shiva_hooks(scheme *sc) { scheme_define(sc, sc->global_env, mk_symbol(sc, "btc-get-best-height" ), mk_foreign_func(sc, btc_get_best_height)); scheme_define(sc, sc->global_env, mk_symbol(sc, "btc-shutdown" ), mk_foreign_func(sc, btc_shutdown)); }