int main (int ac, char *av[]) { int ret, ch; char *logfile = NULL; int i, ok; char *tmp, *ptr; uv_signal_t *sigint; uv_signal_t *sigterm; char *pidfile = NULL; struct destination *destination = NULL; struct listener *listener; struct rlimit rl; rl.rlim_cur = 65535; rl.rlim_max = 65535; setrlimit (RLIMIT_NOFILE, &rl); rl.rlim_cur = RLIM_INFINITY; rl.rlim_max = RLIM_INFINITY; setrlimit (RLIMIT_CORE, &rl); signal (SIGPIPE, SIG_IGN); char *mysqltype = NULL; setenv ("TZ", ":/etc/localtime", 0); tzset (); memset (logstring, '\0', sizeof (logstring)); for (i = 0; i < ac; i++) { if (strlen (logstring) + strlen (av[i]) >= sizeof (logstring)) { break; } strcat (logstring, av[i]); if (i != ac - 1) { strcat (logstring, " "); } } pool = malloc (sizeof (*pool)); bufpool_init (pool, BUF_SIZE); /* uv_timer_t *handle; handle = malloc (sizeof(uv_timer_t)); uv_timer_init(uv_default_loop(),handle); uv_timer_start(handle, bufpool_print_stats, 1000, 1000); */ sigint = malloc (sizeof (uv_signal_t)); sigterm = malloc (sizeof (uv_signal_t)); uv_signal_init (uv_default_loop (), sigint); uv_signal_init (uv_default_loop (), sigterm); uv_signal_start (sigint, signal_handler, SIGINT); uv_signal_start (sigterm, signal_handler, SIGTERM); openlog ("rum", LOG_NDELAY | LOG_PID, LOG_DAEMON); if (ac == 1) { usage (); } /* destination is global variable a pointer to struct destination * struct destination forms a linked list * first_destination is pointer to first struct * * struct listener is the same */ listener = NULL; int option_index = 0; static struct option long_options[] = { {"background", no_argument, 0, 'b'}, {"destination", required_argument, 0, 'd'}, {"source", required_argument, 0, 's'}, {"stats", required_argument, 0, 'm'}, {"logfile", required_argument, 0, 'l'}, {"mysql-cdb", required_argument, 0, 'M'}, {"postgresql-cdb", required_argument, 0, 'P'}, {"mysqltype", required_argument, 0, 't'}, {"failover-r", required_argument, 0, 'R'}, {"failover", required_argument, 0, 'f'}, {"read-timeout", required_argument, 0, 0}, {"connect-timeout", required_argument, 0, 0}, {"pidfile", required_argument, 0, 'p'}, {"loglogins", no_argument, 0, 'L'}, {0, 0, 0, 0} }; while ((ch = getopt_long (ac, av, "bd:s:m:l:M:P:t:r:f:R:p:L", long_options, &option_index)) != -1) { switch (ch) { case 0: if (strcmp (long_options[option_index].name, "read-timeout") == 0) read_timeout = atoi (optarg); if (strcmp (long_options[option_index].name, "connect-timeout") == 0) connect_timeout = atoi (optarg); break; case 'b': daemonize = 1; break; case 's': case 'm': if (listener == NULL) { first_listener = listener = malloc (sizeof (struct listener)); } else { listener->next = malloc (sizeof (struct listener)); listener = listener->next; } listener->s = strdup (optarg); listener->stream = NULL; listener->next = NULL; /* vynulujeme statistiky */ listener->nr_conn = 0; listener->nr_allconn = 0; listener->input_bytes = 0; listener->output_bytes = 0; if (ch == 's') { listener->type = LISTENER_DEFAULT; } else if (ch == 'm') { listener->type = LISTENER_STATS; } break; case 'M': /* enable mysql module */ mysql_cdb_file = strdup (optarg); break; case 'P': /* enable mysql module */ postgresql_cdb_file = strdup (optarg); break; case 'd': first_destination = destination = malloc (sizeof (struct destination)); prepare_upstream (optarg, destination); break; case 'l': logfile = strdup (optarg); break; case 'L': loglogins = 1; break; case 't': mysqltype = optarg; break; case 'f': mode = MODE_FAILOVER; ptr = tmp = strdup (optarg); i = 0; while (tmp[i] != '\0') { if (tmp[i] == ',') { tmp[i] = '\0'; add_destination (ptr); destinations++; ptr = tmp + i + 1; } i++; } add_destination (ptr); destinations++; break; case 'R': mode = MODE_FAILOVER_R; ptr = tmp = strdup (optarg); i = 0; while (tmp[i] != '\0') { if (tmp[i] == ',') { tmp[i] = '\0'; add_destination (ptr); destinations++; ptr = tmp + i + 1; } i++; } add_destination (ptr); destinations++; randomize_destinations (); break; case 'p': pidfile = strdup (optarg); break; } } /* if mysql module is enabled, open cdb file and create EV_SIGNAL event which call repoen_cdb(). * if someone send SIGUSR1 cdb file is reopened, but this is automatically triggered by timeout with * CDB_RELOAD_TIME seconds (default 2s) * * reopen_cdb is called from main event loop, it is not called directly by signal, * so it is race condition free (safe to free and init global cdb variable) */ if (mysql_cdb_file) { init_mysql_cdb_file (mysqltype); } if (postgresql_cdb_file) { init_postgresql_cdb_file (mysqltype); } if (daemonize) { if (logfile) { if (daemon (0, 1) < 0) { perror ("daemon()"); exit (0); } close (0); close (1); close (2); ret = open (logfile, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); if (ret != -1) { dup2 (ret, 1); dup2 (ret, 2); } } else { if (daemon (0, 0) < 0) { perror ("daemon()"); exit (0); } } } /* add all listen (-s -m) ports to event_base, if someone connect: accept_connect is executed with struct listener argument */ for (listener = first_listener; listener; listener = listener->next) { for (i = 0, ok = 0; i < 10; i++) { listener->stream = create_listen_socket (listener->s); listener->stream->data = listener; int r = uv_listen ((uv_stream_t *) listener->stream, -1, on_incoming_connection); if (r) { logmsg ("listen to %s failed, retrying", listener->s); uv_close ((uv_handle_t *) listener->stream, on_close_listener); usleep (200 * 1000); } else { logmsg ("listening on %s", listener->s); ok = 1; break; } } if (ok == 0) { logmsg ("listen to %s failed, exiting", listener->s); _exit (-1); } } if (!first_destination && !mysql_cdb_file && !postgresql_cdb_file) { usage (); } if (daemonize) { if (pidfile) { FILE *fp = fopen(pidfile, "w"); if (fp) { fprintf (fp, "%d", getpid()); fclose (fp); } else { logmsg("cannot open pidfile %s (%s)", pidfile, strerror (errno)); } } } /* main libuv loop */ uv_run (uv_default_loop (), UV_RUN_DEFAULT); /* SIGINT || SIGTERM received, clean up */ bufpool_done (pool); free (pool); if (mysql_cdb_file) { free (mysql_cdb_file); } if (postgresql_cdb_file) { free (postgresql_cdb_file); } struct destination *dst; dst = first_destination; while (dst) { destination = dst->next; free (dst->s); free (dst); dst = destination; } free (sigint); free (sigterm); exit (0); }
int main (int ac, char *av[]) { int ret,ch,daemonize=0; char *logfile=NULL; struct destination *destination; struct listener *listener; signal(SIGPIPE ,SIG_IGN); if (ac==1) { usage(); } /* destination is global variable a pointer to struct destination * struct destination forms a linked list * first_destination is pointer to first struct * * struct listener is the same */ first_destination=destination=malloc(sizeof(struct destination)); listener=NULL; while ((ch = getopt(ac, av, "bd:s:m:l:M:")) != -1) { switch (ch) { case 'b': daemonize=1; break; case 's': case 'm': if (listener==NULL) { first_listener = listener = malloc(sizeof(struct listener)); } else { listener->next = malloc(sizeof(struct listener)); listener = listener->next; } listener->s=strdup(optarg); listener->fd=create_listen_socket(optarg); listener->next=NULL; /* vynulujeme statistiky */ listener->nr_conn=0; listener->nr_allconn=0; listener->input_bytes=0; listener->output_bytes=0; if (ch=='s') { listener->type=LISTENER_DEFAULT; } else if (ch=='m') { listener->type=LISTENER_STATS; } break; case 'M': /* enable mysql module */ mysql_cdb_file=strdup(optarg); break; case 'd': prepareclient(optarg, destination); break; case 'l': logfile=strdup(optarg); break; } } event_base=event_base_new(); /* if mysql module is enabled, open cdb file and create EV_SIGNAL event which call repoen_cdb(). * if someone send SIGUSR1 cdb file is reopened, but this is automatically triggered by timeout with * CDB_RELOAD_TIME seconds (default 2s) * * reopen_cdb is called from main event loop, it is not called directly by signal, * so it is race condition free (safe to free and init global cdb variable) */ if (mysql_cdb_file) { init_mysql_cdb_file(); } if (daemonize) { if (logfile) { if (daemon(0,1)<0) { perror("daemon()"); exit(0); } close(0); close(1); close(2); ret=open(logfile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR); if (ret!=-1) { dup2(ret,1); dup2(ret,2); } } else { if (daemon(0,0)<0) { perror("daemon()"); exit(0); } } } /* add all listen (-s -m) ports to event_base, if someone connect: accept_connect is executed with struct listener argument */ for (listener=first_listener; listener; listener=listener->next) { struct event *ev; ev=event_new(event_base, listener->fd, EV_READ|EV_PERSIST, accept_connect, listener); event_add(ev,NULL); } /* main libevent loop */ event_base_loop(event_base,0); usage(); exit(0); }