src/worker/wkr_main.c in webroar-0.2.2 vs src/worker/wkr_main.c in webroar-0.2.3

- old
+ new

@@ -1,7 +1,7 @@ /* WebROaR - Ruby Application Server - http://webroar.in/ - * Copyright (C) 2009 WebROaR + * Copyright (C) 2009 Goonj LLC * * This file is part of WebROaR. * * WebROaR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,14 +19,20 @@ #include <assert.h> #include <string.h> #include <worker.h> #include <ruby.h> +#include <rubysig.h> +#include <execinfo.h> +#ifdef __linux__ +#include <sys/prctl.h> +#endif -//static wkr_t *worker = NULL; +static wkr_t *worker = NULL; struct ev_loop *loop; // Event loop +struct ev_idle idle_watcher; int is_alive = 1; static int drop_privileges(wkr_t *w) { change_log_file_owner(w->tmp->uid, w->tmp->gid); //setting read, effective, saved group and user id @@ -40,10 +46,17 @@ } LOG_DEBUG(DEBUG,"Passed userid=%d and groupid=%d", w->tmp->uid, w->tmp->gid); LOG_DEBUG(DEBUG,"effective userid=%d and groupid=%d",geteuid(),getegid()); +#ifdef __linux__ + int rv = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + LOG_DEBUG(DEBUG,"prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) = %d", rv); + if (rv < 0) { + LOG_ERROR(SEVERE,"error setting prctl(PR_SET_DUMPABLE, 1, 0, 0, 0), errno = %d, desc = %s", errno, strerror(errno)); + } +#endif return 0; } /** Usage */ static inline void print_usage(char *appname) { @@ -66,10 +79,71 @@ // file_log("/tmp/too_many_worker.log","Webroar-Worker of %s getting close\n", worker->tmp->name.str); LOG_DEBUG(4,"**************Caught Interrupt Signal************"); is_alive = 0; } +void cleanup() { + LOG_FUNCTION + stop_idle_watcher(); + LOG_DEBUG(DEBUG,"stoping event loop"); + ev_unloop(loop,EVUNLOOP_ALL); + //TODO: send worker stopping signal + worker_free(&worker); + LOG_INFO("Worker stopped and exiting gracefully."); + close_logger(); + exit(0); +} + +/** Handle segmentation fault */ +void crash_handler(int sig) { + void *array[WR_STACKTRACE_SIZE]; + size_t size; + char **bt_symbols; + char bt_string[WR_STACKTRACE_SIZE * WR_LONG_STR_LEN * 2]; + int i; + + LOG_ERROR(FATAL, "Got %d signal, trying to create core file.", sig); + signal(sig, SIG_DFL); + //kill(getpid(), sig); + if(fork() == 0) { // child + char cmd[64], core_file_name[48], timestamp[24]; + int rv; + signal(SIGCHLD, SIG_DFL); + //TODO: add application name + sprintf(core_file_name, "/tmp/webroar-worker"); + if ( get_timestamp(timestamp) == 0 ) { + strcat(core_file_name, "-"); + strcat(core_file_name, timestamp); + } +#ifdef __APPLE__ + sprintf(cmd, "gcore -c %s %ld", core_file_name, (long) getppid() ); +#else + sprintf(cmd, "gcore -o %s %ld", core_file_name, (long) getppid() ); +#endif + rv = system(cmd); + if ( rv < 0 ) { + LOG_ERROR(FATAL, "Core file creation failed, gcore might be missing... rv = %d, errno = %d, error = %s", rv, errno, strerror(errno)); + } else { + LOG_INFO("Core file - %s created", core_file_name); + } + exit(0); + } + sleep(5); + // get void*'s for all entries on the stack + size = backtrace(array, WR_STACKTRACE_SIZE); + bt_symbols = backtrace_symbols(array, size); + strcpy(bt_string, "\n"); + for(i = 0; i < size; i++) { + strcat(bt_string, bt_symbols[i]); + strcat(bt_string, "\n"); + } + LOG_ERROR(FATAL, "Obtained %zd stack frames.%s", size, bt_string); + free(bt_symbols); + //TODO: carefully dump worker state, current request + cleanup(); +} + /** Parse command line arguments */ static inline wkr_tmp_t* parse_args(int argc, char **argv) { int option; extern char *optarg; size_t len; @@ -156,35 +230,81 @@ if (invalid_arg_flag>0 || app_path_flag == 0) { print_usage(argv[0]); wkr_tmp_free(&tmp); return NULL; } + if(strcmp(tmp->name.str, WR_STATIC_FILE_SERVER_NAME) == 0){ + tmp->is_static = 1; + } return tmp; } +void start_idle_watcher() { + LOG_FUNCTION + if(!ev_is_active(&idle_watcher)) { + ev_idle_start (loop, &idle_watcher); + } +} + +void stop_idle_watcher() { + LOG_FUNCTION + ev_idle_stop(loop, &idle_watcher); +} + +void idle_cb (struct ev_loop *loop, struct ev_idle *w, int revents) { + LOG_FUNCTION + /* Calling libev's blocking call ev_loop() between TRAP_* macros were working on Ruby 1.8, + * but didn't worked on Ruby 1.9, looks we need to schedule ruby threads on our own + */ + if(rb_thread_alone()) { + /* Stop scheduling ruby threads, there is only one! */ + stop_idle_watcher(); + } else { + /* TODO: Found following three api to schedule ruby threads + * rb_thread_schedule() was getting called infinitely and eating most of the CPU. + * rb_thread_polling() was getting called, for approximately 16 times in a second on 1.8 + * and 10 times on 1.9 + * rb_thread_select() takes delay(in struct timeval) as last argument, useful to control + * number of call in a second, but need to checkout best value for that argument + * Ruby 1.9 have rb_thread_blocking_region() to execute C blocking call, need to explore it + * Checkout the best strategy to combine ruby thread scheduling with libev. + * Currently rb_thread_polling looks suitable to use */ + rb_thread_polling(); + //rb_thread_schedule(); + } +} + int main(int argc, char **argv) { int port, retval = 0; wkr_t* w = NULL; if(argc==1) { print_usage(argv[0]); return -1; } + + signal(SIGSEGV, crash_handler); /* set our handler for segfault */ +// signal(SIGQUIT, crash_handler); +// signal(SIGILL, crash_handler); +// signal(SIGABRT, crash_handler); +// signal(SIGFPE, crash_handler); + wkr_tmp_t *tmp = parse_args(argc, argv); if(tmp == NULL) return -1; loop = ev_default_loop (0); w = worker_new(loop, tmp); if(w==NULL) goto err; - // worker = w; + worker = w; // assert(w!=NULL); initialize_logger(w->tmp->log_file.str); - + redirect_standard_io(); + LOG_DEBUG(DEBUG,"control path = %s, Application baseuri = %s", w->tmp->ctl_path.str, w->tmp->resolver.str); if((retval = drop_privileges(w))!=0) { goto err; @@ -201,16 +321,10 @@ LOG_ERROR(WARN,"Error Initializing Workers."); retval = -1; goto err; } - // w->http = http_new(w); - // if(w->http == NULL) - // { - // goto err; - // } - //loading adapter according to application type LOG_DEBUG(DEBUG,"ruby lib = %s and webroar_root = %s", w->tmp->ruby_path.str, w->tmp->root_path.str); LOG_DEBUG(DEBUG,"path = %s, name = %s, type = %s, environment = %s, baseuri = %s, analytics = %c", w->tmp->path.str, w->tmp->name.str, w->tmp->type.str, @@ -218,31 +332,33 @@ LOG_INFO("Successfully loaded rack application=%s with environment=%s", w->tmp->path.str, w->tmp->env.str); //TODO: Windows Portability? - signal(SIGHUP, SIG_IGN); /* catch hangup signal */ - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); + signal(SIGHUP, sigproc); /* catch hangup signal */ + signal(SIGINT, sigproc); + signal(SIGTERM, sigproc); signal(SIGCHLD, SIG_IGN); signal(SIGTSTP, SIG_IGN); signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - signal(SIGKILL, sigproc); + signal(SIGTTIN, SIG_IGN); signal(SIGPIPE, SIG_IGN); worker_accept_requests(w); LOG_INFO("Worker ready for serving requests."); + if(!w->http->is_static){ + ev_idle_init (&idle_watcher, idle_cb); + start_idle_watcher(); + } + while(is_alive ==1) { + /* TODO: wrapping ev_loop() between TARP_* macros didn't worked in Ruby 1.9 */ + //TRAP_BEG; ev_loop(loop,EVLOOP_ONESHOT); + //TRAP_END; } err: - LOG_DEBUG(DEBUG,"stoping event loop"); - ev_unloop(loop,EVUNLOOP_ALL); - //TODO: send worker stopping signal - worker_free(&w); - LOG_INFO("Worker stopped and exiting gracefully."); - close_logger(); + cleanup(); return retval; }