PageRenderTime 83ms CodeModel.GetById 40ms app.highlight 17ms RepoModel.GetById 23ms app.codeStats 1ms

/server.c

http://github.com/nicolasff/webdis
C | 245 lines | 166 code | 48 blank | 31 comment | 25 complexity | 036e92bbd94abcd8d65a1dc804ea7f0c MD5 | raw file
  1#include "server.h"
  2#include "worker.h"
  3#include "client.h"
  4#include "conf.h"
  5#include "version.h"
  6
  7#include <stdlib.h>
  8#include <stdio.h>
  9#include <unistd.h>
 10#include <signal.h>
 11#include <string.h>
 12#include <netinet/in.h>
 13#include <arpa/inet.h>
 14#include <fcntl.h>
 15#include <errno.h>
 16#include <sys/types.h>
 17#include <sys/socket.h>
 18#include <sys/ioctl.h>
 19
 20/**
 21 * Sets up a non-blocking socket
 22 */
 23static int
 24socket_setup(struct server *s, const char *ip, short port) {
 25
 26	int reuse = 1;
 27	struct sockaddr_in addr;
 28	int fd, ret;
 29
 30	memset(&addr, 0, sizeof(addr));
 31#if defined __BSD__
 32	addr.sin_len = sizeof(struct sockaddr_in);
 33#endif
 34	addr.sin_family = AF_INET;
 35	addr.sin_port = htons(port);
 36
 37	addr.sin_addr.s_addr = inet_addr(ip);
 38
 39	/* this sad list of tests could use a Maybe monad... */
 40
 41	/* create socket */
 42	fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 43	if (-1 == fd) {
 44		slog(s, WEBDIS_ERROR, strerror(errno), 0);
 45		return -1;
 46	}
 47
 48	/* reuse address if we've bound to it before. */
 49	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse,
 50				sizeof(reuse)) < 0) {
 51		slog(s, WEBDIS_ERROR, strerror(errno), 0);
 52		return -1;
 53	}
 54
 55	/* set socket as non-blocking. */
 56	ret = fcntl(fd, F_SETFD, O_NONBLOCK);
 57	if (0 != ret) {
 58		slog(s, WEBDIS_ERROR, strerror(errno), 0);
 59		return -1;
 60	}
 61
 62	/* bind */
 63	ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
 64	if (0 != ret) {
 65		slog(s, WEBDIS_ERROR, strerror(errno), 0);
 66		return -1;
 67	}
 68
 69	/* listen */
 70	ret = listen(fd, SOMAXCONN);
 71	if (0 != ret) {
 72		slog(s, WEBDIS_ERROR, strerror(errno), 0);
 73		return -1;
 74	}
 75
 76	/* there you go, ready to accept! */
 77	return fd;
 78}
 79
 80struct server *
 81server_new(const char *cfg_file) {
 82	
 83	int i;
 84	struct server *s = calloc(1, sizeof(struct server));
 85
 86	s->log.fd = -1;
 87	s->cfg = conf_read(cfg_file);
 88
 89	/* workers */
 90	s->w = calloc(s->cfg->http_threads, sizeof(struct worker*));
 91	for(i = 0; i < s->cfg->http_threads; ++i) {
 92		s->w[i] = worker_new(s);
 93	}
 94	return s;
 95}
 96
 97static void
 98server_can_accept(int fd, short event, void *ptr) {
 99
100	struct server *s = ptr;
101	struct worker *w;
102	struct http_client *c;
103	int client_fd;
104	struct sockaddr_in addr;
105	socklen_t addr_sz = sizeof(addr);
106	char on = 1;
107	(void)event;
108
109	/* select worker to send the client to */
110	w = s->w[s->next_worker];
111
112	/* accept client */
113	client_fd = accept(fd, (struct sockaddr*)&addr, &addr_sz);
114
115	/* make non-blocking */
116	ioctl(client_fd, (int)FIONBIO, (char *)&on);
117
118	/* create client and send to worker. */
119	if(client_fd > 0) {
120		c = http_client_new(w, client_fd, addr.sin_addr.s_addr);
121		worker_add_client(w, c);
122
123		/* loop over ring of workers */
124		s->next_worker = (s->next_worker + 1) % s->cfg->http_threads;
125	} else { /* too many connections */
126		slog(s, WEBDIS_NOTICE, "Too many connections", 0);
127	}
128}
129
130/**
131 * Daemonize server.
132 * (taken from Redis)
133 */
134static void
135server_daemonize(const char *pidfile) {
136	int fd;
137
138	if (fork() != 0) exit(0); /* parent exits */
139	setsid(); /* create a new session */
140
141	/* Every output goes to /dev/null. */
142	if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
143		dup2(fd, STDIN_FILENO);
144		dup2(fd, STDOUT_FILENO);
145		dup2(fd, STDERR_FILENO);
146		if (fd > STDERR_FILENO) close(fd);
147	}
148
149	/* write pidfile */
150	if (pidfile) {
151		FILE *f = fopen(pidfile, "w");
152		if (f) {
153			fprintf(f, "%d\n", (int)getpid());
154			fclose(f);
155		}
156	}
157}
158
159/* global pointer to the server object, used in signal handlers */
160static struct server *__server;
161
162static void
163server_handle_signal(int id) {
164
165	switch(id) {
166		case SIGHUP:
167			slog_init(__server);
168			break;
169		case SIGTERM:
170		case SIGINT:
171			slog(__server, WEBDIS_INFO, "Webdis terminating", 0);
172			exit(0);
173			break;
174		default:
175			break;
176	}
177}
178
179static void
180server_install_signal_handlers(struct server *s) {
181	__server = s;
182
183	signal(SIGHUP,  server_handle_signal);
184	signal(SIGTERM, server_handle_signal);
185	signal(SIGINT,  server_handle_signal);
186}
187
188int
189server_start(struct server *s) {
190
191	int i, ret;
192
193	/* initialize libevent */
194	s->base = event_base_new();
195
196	if(s->cfg->daemonize) {
197		server_daemonize(s->cfg->pidfile);
198
199		/* sometimes event mech gets lost on fork */
200		if(event_reinit(s->base) != 0) {
201			fprintf(stderr, "Error: event_reinit failed after fork");
202		}
203	}
204
205	/* ignore sigpipe */
206#ifdef SIGPIPE
207	signal(SIGPIPE, SIG_IGN);
208#endif
209
210	slog_init(s);
211
212	/* install signal handlers */
213	server_install_signal_handlers(s);
214
215	/* start worker threads */
216	for(i = 0; i < s->cfg->http_threads; ++i) {
217		worker_start(s->w[i]);
218	}
219
220	/* create socket */
221	s->fd = socket_setup(s, s->cfg->http_host, s->cfg->http_port);
222	if(s->fd < 0) {
223		return -1;
224	}
225	
226	/*set keepalive socket option to do with half connection*/
227        int keep_alive = 1;
228        setsockopt(s->fd , SOL_SOCKET, SO_KEEPALIVE, (void*)&keep_alive, sizeof(keep_alive));
229
230	/* start http server */
231	event_set(&s->ev, s->fd, EV_READ | EV_PERSIST, server_can_accept, s);
232	event_base_set(s->base, &s->ev);
233	ret = event_add(&s->ev, NULL);
234
235	if(ret < 0) {
236		slog(s, WEBDIS_ERROR, "Error calling event_add on socket", 0);
237		return -1;
238	}
239
240	slog(s, WEBDIS_INFO, "Webdis " WEBDIS_VERSION " up and running", 0);
241	event_base_dispatch(s->base);
242
243	return 0;
244}
245