/* WebROaR - Ruby Application Server - http://webroar.in/
* Copyright (C) 2009 WebROaR
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebROaR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebROaR. If not, see .
*/
#include
#include
#include
#include
//static wkr_t *worker = NULL;
struct ev_loop *loop; // Event loop
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
if(setgid(w->tmp->gid)!=0) {
LOG_ERROR(SEVERE,"setegid() failed");
return -1;
}
if(setuid(w->tmp->uid)!=0) {
LOG_ERROR(SEVERE,"seteuid() failed");
return -1;
}
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());
return 0;
}
/** Usage */
static inline void print_usage(char *appname) {
printf("usage: \n%s -a [-e ] [-u ] [-g ] ",appname);
printf("[-c ] [-i ] [-t ] [-n ] ");
printf("[-p ] [-r ]\n");
printf(" = path of rails application. e.g. /home/xyz/rails_projects/app1\n");
printf(" = rails environment. development/production. Default is production.\n");
printf(" = User id.\n");
printf(" = Group id.\n");
printf(" = Control port number (control sock path in case [-i 1])\n");
printf(" = Unix domain socket flag. Value should be 0 or 1.\n");
printf(" = Type of application {rails, merb}.\n");
printf(" = Name of application\n");
printf(" = Analytics flag. Value should be yes or no.\n");
printf(" = Application URL baseuri.\n");
}
void sigproc() {
// 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;
}
/** Parse command line arguments */
static inline wkr_tmp_t* parse_args(int argc, char **argv) {
int option;
extern char *optarg;
size_t len;
char *str;
int invalid_arg_flag = 0, app_path_flag = 0;
wkr_tmp_t *tmp = wkr_tmp_new();
if(tmp == NULL)
return NULL;
while ( (option=getopt(argc,argv,"a:b:e:l:f:g:u:c:i:t:n:o:p:r:k:")) != -1 ) {
str = optarg;
len = strlen(str);
switch ( option ) {
case 'a': // Application Path
wr_string_new(tmp->path, str, len);
app_path_flag = 1;
break;
case 'b': // Ruby library path
wr_string_new(tmp->ruby_path, str, len);
tmp->script_path.str = (char*) malloc(sizeof(char)*(tmp->ruby_path.len + 32));
tmp->script_path.len = sprintf(tmp->script_path.str, "%s%swebroar_app_loader.rb", tmp->ruby_path.str, WR_PATH_SEPARATOR);
break;
case 'e': // Application environment
wr_string_new(tmp->env, str, len);
break;
case 'l': // Logging level
#ifdef L_DEBUG
set_log_severity(DEBUG);
#else
set_log_severity(atoi(optarg));
#endif
break;
case 'f': // Log file name
wr_string_free(tmp->log_file);
wr_string_new(tmp->log_file, str, len);
break;
case 'g': // Group id
tmp->gid = atoi(optarg);
break;
case 'u': // User id
tmp->uid = atoi(optarg);
break;
case 'c': // Control path
wr_string_new(tmp->ctl_path, str, len);
break;
case 'i': // Unix domain socket flag
if(strcmp(optarg, "y")==0) {
tmp->is_uds = TRUE;
}
break;
case 't': // Application type
wr_string_new(tmp->type, str, len);
break;
case 'n': // Application name
wr_string_new(tmp->name, str, len);
wr_string_free(tmp->log_file);
tmp->log_file.str = (char*) malloc(sizeof(char)*(strlen(optarg)+8));
tmp->log_file.len = sprintf(tmp->log_file.str,"%s.log", optarg);
break;
case 'o': // Server root path
wr_string_new(tmp->root_path, str, len);
break;
case 'r': // Applicaiton base uri
wr_string_new(tmp->resolver, str, len);
break;
case 'p': // Analytics flag
if(strcmp(optarg, "y")==0) {
tmp->profiler = 'y';
}
break;
case 'k':
if(strcmp(optarg, "n")==0) {
tmp->keep_alive = FALSE;
}
break;
default:
invalid_arg_flag++;
}
}
if (invalid_arg_flag>0 || app_path_flag == 0) {
print_usage(argv[0]);
wkr_tmp_free(&tmp);
return NULL;
}
return tmp;
}
int main(int argc, char **argv) {
int port, retval = 0;
wkr_t* w = NULL;
if(argc==1) {
print_usage(argv[0]);
return -1;
}
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;
// assert(w!=NULL);
initialize_logger(w->tmp->log_file.str);
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;
}
w->http = http_new(w);
if(w->http == NULL) {
LOG_ERROR(SEVERE,"unable to load application.");
goto err;
}
retval = worker_connect(w);
if(retval<0) {
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,
w->tmp->env.str, w->tmp->resolver.str, w->tmp->profiler);
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(SIGCHLD, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGKILL, sigproc);
signal(SIGPIPE, SIG_IGN);
worker_accept_requests(w);
LOG_INFO("Worker ready for serving requests.");
while(is_alive ==1) {
ev_loop(loop,EVLOOP_ONESHOT);
}
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();
return retval;
}