int lp_check_file(lua_State *ls) { FILE *f; char *aux; unsigned long int size=0L; unsigned long int size2=0L; aux=(char *)malloc(sizeof(char)*512); if (aux==NULL) { ph_log_memory_error((char *)"luaphant.c",(char *)"lp_check_file",(char *)"aux",sizeof(char)*512); } memset(aux,0,sizeof(char)*512); strcpy(aux,lua_tostring(ls,1)); f=fopen(aux,"rb"); if (f==NULL) { ph_log(_("Fatal error: Can't find the file %s."),aux); exit(-1); } fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET); size2=(unsigned long int)lua_tonumber(ls,2); if (size!=size2) { ph_log(_("Fatal error: Size doesn't match.")); exit(-1); } fclose(f); free(aux); return 0; }
char * ph_get_path(CFG *cfg, char *path, char *buffer) { FILE *f,*f1; memset(path,0,sizeof(char)*MAXPATHSIZE); strcpy(path,buffer); f=fopen(path,"r"); if (f==NULL) { /* Ok, no está en la ruta normal, mirar a ver si está en el directorio actual */ /* En sistemas Unix cuya shell ponga el valor de la variable PWD */ /* TODO Mirar a ver si hay alguna manera portable */ #ifndef WIN32 #ifndef DOS #ifndef CUSTOM_PATH memset(path,0,sizeof(char)*MAXPATHSIZE); strcpy(path,getenv("PWD")); path[strlen(path)]='/'; strcat(path,buffer); f=fopen(path,"r"); if (f==NULL) { ph_log(_("- Error: According to function ph_get_path the file %s doesn't exist.\n"),path); return NULL; } else { fclose(f); return path; } #endif #endif #endif memset(path,0,sizeof(char)*MAXPATHSIZE); #ifdef CUSTOM_PATH strcat(path,"/usr/share/phantomaspc/"); strcat(path,buffer); #else strcpy(path,buffer); #endif f1=fopen(path,"rb"); if (f1==NULL) { ph_log(_("- Error: According to function ph_get_path the file %s doesn't exist.\n"),path); return NULL; } fclose(f1); return path; } fclose(f); return path; }
static void read_remote(ph_sock_t *sock, ph_iomask_t why, void *data) { ph_buf_t *buf; ph_unused_parameter(data); if (why & (PH_IOMASK_TIME|PH_IOMASK_ERR)) { ph_log(PH_LOG_ERR, "disconnecting `P{sockaddr:%p}", (void*)&sock->peername); ph_sock_shutdown(sock, PH_SOCK_SHUT_RDWR); ph_sock_free(sock); remote_sock = NULL; ph_sched_stop(); return; } while (1) { buf = ph_sock_read_line(sock); if (!buf) { // Not available yet, we'll try again later return; } ph_ignore_result(write(STDOUT_FILENO, ph_buf_mem(buf), ph_buf_len(buf))); ph_buf_delref(buf); } }
static void read_stdin(ph_job_t *job, ph_iomask_t why, void *data) { char buf[128]; int x, i; ph_unused_parameter(why); ph_unused_parameter(data); x = read(job->fd, buf, sizeof(buf)); if (x <= 0) { if (x == -1) { ph_log(PH_LOG_ERR, "read(stdin): `Pe%d", errno); } ph_sched_stop(); return; } // Writing to the other job is safe here because we have the // same affinity: we know that it is not executing and mutating // its state for (i = 0; i < x; i++) { if (buf[i] == '\n') { ph_stm_write(remote_sock->stream, "\r\n", 2, NULL); } else { ph_stm_write(remote_sock->stream, buf + i, 1, NULL); } } // Force the sock to wakeup and send the buffer. // FIXME: Need something nicer than this hack ph_sock_enable(remote_sock, false); ph_sock_enable(remote_sock, true); ph_job_set_nbio_timeout_in(job, PH_IOMASK_READ, timeout); }
static void connected(ph_sock_t *sock, int overall_status, int errcode, const ph_sockaddr_t *addr, struct timeval *elapsed, void *arg) { SSL_CTX *ctx; SSL *ssl; ph_unused_parameter(arg); ph_unused_parameter(elapsed); switch (overall_status) { case PH_SOCK_CONNECT_GAI_ERR: ph_log(PH_LOG_ERR, "resolve %s:%d failed %s", addrstring, portno, gai_strerror(errcode)); ph_sched_stop(); return; case PH_SOCK_CONNECT_ERRNO: ph_log(PH_LOG_ERR, "connect %s:%d (`P{sockaddr:%p}) failed: `Pe%d", addrstring, portno, (void*)addr, errcode); ph_sched_stop(); return; } sock->callback = read_remote; remote_sock = sock; // Now set up stdin to feed into this new sock ph_job_init(&stdin_job); stdin_job.fd = STDIN_FILENO; stdin_job.callback = read_stdin; // Ensure that we have the same affinity as the other job stdin_job.emitter_affinity = sock->job.emitter_affinity; ph_socket_set_nonblock(STDIN_FILENO, true); ph_job_set_nbio_timeout_in(&stdin_job, PH_IOMASK_READ, timeout); ctx = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_cipher_list(ctx, "ALL"); SSL_CTX_set_options(ctx, SSL_OP_ALL); SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); ssl = SSL_new(ctx); ph_sock_openssl_enable(sock, ssl, true, done_handshake); ph_sock_enable(sock, true); }
lua_State * lp_init_lua(void) { lua_State *ls; ph_log(_("- Initiating Lua: ")); ls=luaL_newstate(); if (ls==NULL) { ph_log(_("Fatal error: Can't init Lua.\n")); exit(13); } else { ph_log(_("Ok.\n")); } ls=lp_register_luaphant_functions(ls); luaL_openlibs(ls); return ls; }
void ph_panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); ph_logv(PH_LOG_PANIC, fmt, ap); va_end(ap); ph_log(PH_LOG_PANIC, "Fatal error detected at:"); ph_log_stacktrace(PH_LOG_PANIC); abort(); }
// Called each time the session wakes up. // The `why` parameter indicates why we were woken up static void echo_processor(ph_sock_t *sock, ph_iomask_t why, void *arg) { struct echo_state *state = arg; ph_buf_t *buf; // If the socket encountered an error, or if the timeout was reached // (there's a default timeout, even if we didn't override it), then // we tear down the session if (why & (PH_IOMASK_ERR|PH_IOMASK_TIME)) { ph_log(PH_LOG_ERR, "disconnecting `P{sockaddr:%p}", (void*)&sock->peername); ph_sock_shutdown(sock, PH_SOCK_SHUT_RDWR); ph_mem_free(mt_state, state); ph_sock_free(sock); return; } // We loop because echo_processor is only triggered by newly arriving // data or events from the kernel. If we have data buffered and only // partially consume it, we won't get woken up until the next data // arrives, if ever. while (1) { // Try to read a line of text. // This returns a slice over the underlying buffer (if the line was // smaller than a buffer) or a freshly made contiguous buffer (if the // line was larger than our buffer segment size). Either way, we // own a reference to the returned buffer and should treat it as // a read-only slice. buf = ph_sock_read_line(sock); if (!buf) { // Not available yet, we'll try again later return; } // We got a line; update our state state->num_lines++; // Send our response. The data is buffered and automatically sent // to the client as it becomes writable, so we don't need to handle // partial writes or EAGAIN here. // If this was a "real" server, we would still check the return value // from the writes and proceed to tear down the session if things failed. // Note that buf includes the trailing CRLF, so our response // will implicitly end with CRLF too. ph_stm_printf(sock->stream, "You said [%d]: ", state->num_lines); ph_stm_write(sock->stream, ph_buf_mem(buf), ph_buf_len(buf), NULL); // We're done with buf, so we must release it ph_buf_delref(buf); } }
// Called each time the listener has accepted a client connection static void acceptor(ph_listener_t *lstn, ph_sock_t *sock) { ph_unused_parameter(lstn); // Allocate an echo_state instance and stash it. // This is set to be zero'd on creation and will show up as the // `arg` parameter in `echo_processor` sock->job.data = ph_mem_alloc(mt_state); // Tell it how to dispatch sock->callback = echo_processor; ph_log(PH_LOG_ERR, "accepted `P{sockaddr:%p}", (void*)&sock->peername); ph_sock_enable(sock, true); }
void ph_log_stacktrace(uint8_t level) { #if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) void *array[24]; size_t size; char **strings; size_t i; size = backtrace(array, sizeof(array)/sizeof(array[0])); strings = backtrace_symbols(array, size); for (i = 0; i < size; i++) { ph_log(level, "%s", strings[i]); } free(strings); #endif }
/* { * "base": 0, // base core number; is added to "selector" * "selector": "tid", // use tid * "selector": "wid", // use thr->is_worker id * "selector": [1,2,3], // use 1+base, 2+base, 3+base * "selector": 1, // use 1+base * "selector": "none" // don't specify affinity * } */ bool ph_thread_set_affinity_policy(ph_thread_t *me, ph_variant_t *policy) { ph_cpu_set_t set; uint32_t cores = ph_num_cores(); CPU_ZERO(&set); if (!policy) { ph_cpu_set(me->tid % cores, &set); } else { int base = 0; ph_var_err_t err; ph_variant_t *sel = NULL; ph_var_unpack(policy, &err, 0, "{si, so}", "base", &base, "selector", &sel); if (sel && ph_var_is_array(sel)) { uint32_t i; for (i = 0; i < ph_var_array_size(sel); i++) { int cpu = ph_var_int_val(ph_var_array_get(sel, i)); ph_cpu_set((base + cpu) % cores, &set); } } else if (sel && ph_var_is_string(sel)) { ph_string_t *s = ph_var_string_val(sel); if (ph_string_equal_cstr(s, "tid")) { ph_cpu_set((base + me->tid) % cores, &set); } else if (ph_string_equal_cstr(s, "wid")) { ph_cpu_set((base + me->is_worker - 1) % cores, &set); } else if (ph_string_equal_cstr(s, "none")) { return true; } else { ph_log(PH_LOG_ERR, "Unknown thread affinity selector `Ps%p", (void*)s); } } else if (sel && ph_var_is_int(sel)) { ph_cpu_set((base + ph_var_int_val(sel)) % cores, &set); } else { ph_cpu_set((base + me->tid) % cores, &set); } } return apply_affinity(&set, me); }
int main(int argc, char **argv) { int c; uint16_t portno = 8080; char *addrstring = NULL; ph_sockaddr_t addr; ph_listener_t *lstn; bool use_v4 = false; // Must be called prior to calling any other phenom functions ph_library_init(); while ((c = getopt(argc, argv, "p:l:4")) != -1) { switch (c) { case '4': use_v4 = true; break; case 'l': addrstring = optarg; break; case 'p': portno = atoi(optarg); break; default: ph_fdprintf(STDERR_FILENO, "Invalid parameters\n" " -4 - interpret address as an IPv4 address\n" " -l ADDRESS - which address to listen on\n" " -p PORTNO - which port to listen on\n" ); exit(EX_USAGE); } } // Set up the address that we're going to listen on if ((use_v4 && ph_sockaddr_set_v4(&addr, addrstring, portno) != PH_OK) || (!use_v4 && ph_sockaddr_set_v6(&addr, addrstring, portno) != PH_OK)) { ph_fdprintf(STDERR_FILENO, "Invalid address [%s]:%d", addrstring ? addrstring : "*", portno ); exit(EX_USAGE); } // Register our memtype mt_state = ph_memtype_register(&mt_state_def); // Optional config file for tuning internals ph_config_load_config_file("/path/to/my/config.json"); // Enable the non-blocking IO manager ph_nbio_init(0); ph_log(PH_LOG_ERR, "will listen on `P{sockaddr:%p}", (void*)&addr); // This enables a very simple request/response console // that allows you to run diagnostic commands: // `echo memory | nc -UC /tmp/phenom-debug-console` // (on BSD systems, use `nc -Uc`!) // The code behind this is in // https://github.com/facebook/libphenom/blob/master/corelib/debug_console.c ph_debug_console_start("/tmp/phenom-debug-console"); lstn = ph_listener_new("echo-server", acceptor); ph_listener_bind(lstn, &addr); ph_listener_enable(lstn, true); // Run ph_sched_run(); return 0; }
ph_result_t ph_nbio_emitter_apply_io_mask( struct ph_nbio_emitter *emitter, ph_job_t *job, ph_iomask_t mask) { struct epoll_event evt; int res; int want_mask; if (job->fd == -1) { return PH_OK; } switch (mask & (PH_IOMASK_READ|PH_IOMASK_WRITE)) { case PH_IOMASK_READ|PH_IOMASK_WRITE: want_mask = EPOLLIN|EPOLLOUT|DEFAULT_POLL_MASK; break; case PH_IOMASK_READ: want_mask = EPOLLIN|DEFAULT_POLL_MASK; break; case PH_IOMASK_WRITE: want_mask = EPOLLOUT|DEFAULT_POLL_MASK; break; case 0: default: want_mask = 0; break; } if (want_mask == 0) { job->mask = 0; job->kmask = 0; res = epoll_ctl(emitter->io_fd, EPOLL_CTL_DEL, job->fd, &evt); if (res < 0 && errno == ENOENT) { res = 0; } } else { int op = job->kmask ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; evt.events = want_mask; evt.data.ptr = job; // Set the masks on the job before we epoll_ctl as it // may arrive at another thread *before* epoll_ctl returns job->kmask = want_mask; job->mask = mask; res = epoll_ctl(emitter->io_fd, op, job->fd, &evt); if (res == -1 && errno == EEXIST && op == EPOLL_CTL_ADD) { // This can happen when we're transitioning between distinct job // pointers, for instance, when we're moving from an async connect // to setting up the sock job res = epoll_ctl(emitter->io_fd, EPOLL_CTL_MOD, job->fd, &evt); } else if (res == -1 && errno == ENOENT && op == EPOLL_CTL_MOD) { res = epoll_ctl(emitter->io_fd, EPOLL_CTL_ADD, job->fd, &evt); } if (res == -1 && errno == EEXIST) { res = 0; } } if (res == -1) { ph_log(PH_LOG_ERR, "fd=%d (callback=%p) epoll_ctl: setting mask to %02x -> %d `Pe%d", job->fd, (void*)(uintptr_t)job->callback, mask, errno, errno); ph_log_stacktrace(PH_LOG_ERR); return PH_ERR; } return PH_OK; }
void ph_nbio_emitter_run(struct ph_nbio_emitter *emitter, ph_thread_t *thread) { struct epoll_event *event; int n, i; int max_chunk, max_sleep; max_chunk = ph_config_query_int("$.nbio.max_per_wakeup", 1024); max_sleep = ph_config_query_int("$.nbio.max_sleep", 5000); event = malloc(max_chunk * sizeof(struct epoll_event)); while (ck_pr_load_int(&_ph_run_loop)) { n = epoll_wait(emitter->io_fd, event, max_chunk, max_sleep); thread->refresh_time = true; if (n < 0) { if (errno != EINTR) { ph_log(PH_LOG_ERR, "epoll_wait: `Pe%d", errno); } ph_job_collector_emitter_call(emitter); ph_thread_epoch_poll(); continue; } if (n == 0) { continue; } ph_thread_epoch_begin(); for (i = 0; i < n; i++) { ph_iomask_t mask = 0; ph_job_t *job = event[i].data.ptr; if (job->mask == 0) { // Ignore: disabled for now continue; } switch (event[i].events & (EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) { case EPOLLIN: mask = PH_IOMASK_READ; break; case EPOLLOUT: mask = PH_IOMASK_WRITE; break; case EPOLLIN|EPOLLOUT: mask = PH_IOMASK_READ|PH_IOMASK_WRITE; break; default: mask = PH_IOMASK_ERR; } // We can't just clear kmask completely because ONESHOT retains // the existence of the item; we need to know it is there so that // we can MOD it instead of ADD it later. job->kmask = DEFAULT_POLL_MASK; ph_nbio_emitter_dispatch_immediate(emitter, job, mask); if (ph_job_have_deferred_items(thread)) { ph_job_pool_apply_deferred_items(thread); } } ph_thread_epoch_end(); ph_job_collector_emitter_call(emitter); ph_thread_epoch_poll(); } free(event); }
static void done_handshake(ph_sock_t *sock, int res) { ph_unused_parameter(sock); ph_log(PH_LOG_ERR, "handshake completed with res=%d", res); }