/server.c

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