tcpserve.c (4945B)
1 /************************************************************************* 2 * This file is part of Crosroads 1.23, a load balancer and fail over 3 * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. 4 * Visit http://crossroads.e-tunity.com for information. 5 *************************************************************************/ 6 #include "crossroads.h" 7 8 void tcpserve (int server_sock) { 9 fd_set set; 10 int backend_sock, new, size, i, pid, backend_selected; 11 struct sockaddr_in clientname; 12 static int wakeup_started = 0; 13 14 /* Set up our signal handlers. */ 15 if (!wakeup_started++) { 16 /* Interruption signals */ 17 for (i = 0; relevant_sigs[i]; i++) 18 signal (relevant_sigs[i], interrupt); 19 /* Child termination is ignored. */ 20 signal (SIGCHLD, SIG_IGN); 21 /* Set wakeup handler for the wakeup calls. */ 22 if (activeservice->rev_interval) { 23 if ( (pid = fork()) < 0 ) 24 error ("Fork failed: %s", strerror(errno)); 25 else if (!pid) { 26 set_program_title ("Service %s: wakeup", activeservice->name); 27 wakeup_handler(); 28 } else { 29 msg ("Service %s: started wakeup handler at pid %d", 30 activeservice->name, pid); 31 servicereport->rev_pid = pid; 32 } 33 } 34 } 35 36 msg ("Service %s: awaiting activity on port %d (socket fd %d)", 37 activeservice->name, activeservice->port, server_sock); 38 39 if (listen (server_sock, activeservice->backlog + 1) < 0) 40 error ("Service %s: failed to listen to server_socket: %s", 41 activeservice->name, strerror(errno)); 42 43 /* We're a service now. Never return, never exit 44 * (unless something is REALLY wrong). 45 */ 46 while (1) { 47 /* Block until we get a connection. */ 48 FD_ZERO (&set); 49 FD_SET (server_sock, &set); 50 if (select (FD_SETSIZE, &set, 0, 0, 0) < 0) { 51 msg ("Service %s: interrupt while waiting for activity: %d (%s)", 52 activeservice->name, errno, strerror(errno)); 53 continue; 54 } 55 56 /* Accept the client-side connection. */ 57 size = sizeof(clientname); 58 if ( (new = accept (server_sock, (struct sockaddr *) &clientname, 59 (socklen_t *) &size)) < 0 ) 60 error ("Service %s: failure while accepting on server socket: %s", 61 activeservice->name, strerror(errno)); 62 63 /* Store client IP, it's used for lots of logging. */ 64 client_ip = inet_ntoa (clientname.sin_addr); 65 msg ("Service %s: connection from %s, socket %d", 66 activeservice->name, client_ip, new); 67 if (ipf_denied ()) { 68 warning ("Service %s: %s matches deny list, " 69 "terminating connection", activeservice->name, client_ip); 70 close (new); 71 continue; 72 } 73 if (!ipf_allowed ()) { 74 warning ("Service %s: %s failes to match allow list, " 75 "terminating connection", activeservice->name, client_ip); 76 close (new); 77 continue; 78 } 79 80 /* Backend yet to be defined. */ 81 current_backend = -1; 82 83 /* Incase of a http type service: handle separately. */ 84 if (activeservice->type == type_http) { 85 http_serve (new); 86 close (new); 87 continue; 88 } 89 90 /* This is an 'any' service type. 91 * Leave it alone if there are no back ends or if we exceed 92 * the max allowed clients. IN THAT CASE WE RETURN so that 93 * runservice() may close the listener socket, sleep, and 94 * create a new one. 95 */ 96 if (! backend_count()) { 97 warning ("Service %s: no back ends available", 98 activeservice->name); 99 close (new); 100 return; 101 } 102 103 /* Retry back ends until we succeed. */ 104 backend_selected = 0; 105 while (!backend_selected) { 106 msg ("Service %s: About to choose a back end", 107 activeservice->name); 108 choose_backend(); 109 if (current_backend < 0) { 110 /* No back ends available now. NOTE: we return so that 111 * runservice() will retry in some time. */ 112 close (new); 113 return; 114 } 115 116 /* Connect to the backend. If this fails then we'll sleep 117 * and re-enter the backend selection loop instead of returning. 118 * In the loop we may need to decide to return after all. */ 119 msg ("Service %s: trying back end %s, port %d", 120 activeservice->name, 121 activeservice->backend[current_backend].server, 122 activeservice->backend[current_backend].port); 123 if ( (backend_sock = backend_connect()) < 0 ) { 124 warning ("Service %s: failed to connect to server %s:%d " 125 "(ret %d)", activeservice->name, 126 activeservice->backend[current_backend].server, 127 activeservice->backend[current_backend].port, 128 backend_sock); 129 continue; 130 } 131 132 /* Got a live one! */ 133 backend_selected++; 134 if (fork_tcp_servicer (current_backend)) { 135 /* We're the parent branch here. Close sockets so that 136 * we don't run out of file descriptors, and loop into 137 * the next select/accept run. */ 138 close (new); 139 close (backend_sock); 140 } else { 141 /* Child branch: piggyback to and fro. 142 * This one never returns. */ 143 incr_client_count(); 144 log_activity_start (); 145 flag_verbose = 146 activeservice->backend[current_backend].verbosity; 147 copysockets (new, backend_sock); 148 } 149 } 150 } 151 }