client: allocate clients dynamically

Due to the large buffers in the client struct, the static client array
eats several megabytes of RAM with a maximum of only 10 clients.  Stop
this waste and allocate each client struct from the heap.
This commit is contained in:
Max Kellermann 2008-08-28 20:03:48 +02:00
parent a091c148e6
commit 61443c13e6

View File

@ -28,6 +28,7 @@
#include "myfprintf.h" #include "myfprintf.h"
#include "os_compat.h" #include "os_compat.h"
#include "main_notify.h" #include "main_notify.h"
#include "dlist.h"
#include "../config.h" #include "../config.h"
@ -60,6 +61,8 @@ static struct strnode *list_cache_head;
static struct strnode *list_cache_tail; static struct strnode *list_cache_tail;
struct client { struct client {
struct list_head siblings;
char buffer[CLIENT_MAX_BUFFER_LENGTH]; char buffer[CLIENT_MAX_BUFFER_LENGTH];
size_t bufferLength; size_t bufferLength;
size_t bufferPos; size_t bufferPos;
@ -83,7 +86,8 @@ struct client {
size_t send_buf_alloc; /* bytes actually allocated */ size_t send_buf_alloc; /* bytes actually allocated */
}; };
static struct client *clients; static LIST_HEAD(clients);
static unsigned num_clients;
static void client_write_deferred(struct client *client); static void client_write_deferred(struct client *client);
@ -129,7 +133,7 @@ static void set_send_buf_size(struct client *client)
static void client_init(struct client *client, int fd) static void client_init(struct client *client, int fd)
{ {
assert(client->fd < 0); static unsigned int next_client_num;
client->cmd_list_size = 0; client->cmd_list_size = 0;
client->cmd_list_dup = 0; client->cmd_list_dup = 0;
@ -144,6 +148,7 @@ static void client_init(struct client *client, int fd)
client->deferred_send = NULL; client->deferred_send = NULL;
client->expired = 0; client->expired = 0;
client->deferred_bytes = 0; client->deferred_bytes = 0;
client->num = next_client_num++;
client->send_buf_used = 0; client->send_buf_used = 0;
client->permission = getDefaultPermissions(); client->permission = getDefaultPermissions();
@ -212,10 +217,15 @@ out:
static void client_close(struct client *client) static void client_close(struct client *client)
{ {
struct sllnode *buf; struct sllnode *buf;
if (client->fd < 0)
return; assert(client->fd >= 0);
xclose(client->fd); xclose(client->fd);
client->fd = -1;
assert(num_clients > 0);
assert(!list_empty(&clients));
list_del(&client->siblings);
--num_clients;
if (client->cmd_list) { if (client->cmd_list) {
free_cmd_list(client->cmd_list); free_cmd_list(client->cmd_list);
@ -231,18 +241,19 @@ static void client_close(struct client *client)
client->deferred_send = NULL; client->deferred_send = NULL;
} }
if (client->send_buf)
free(client->send_buf);
SECURE("client %i: closed\n", client->num); SECURE("client %i: closed\n", client->num);
free(client);
} }
void client_new(int fd, const struct sockaddr *addr) void client_new(int fd, const struct sockaddr *addr)
{ {
unsigned int i;
const char *hostname; const char *hostname;
struct client *client;
for (i = 0; i < client_max_connections if (num_clients >= client_max_connections) {
&& clients[i].fd >= 0; i++) /* nothing */ ;
if (i == client_max_connections) {
ERROR("Max Connections Reached!\n"); ERROR("Max Connections Reached!\n");
xclose(fd); xclose(fd);
return; return;
@ -281,8 +292,12 @@ void client_new(int fd, const struct sockaddr *addr)
default: default:
hostname = "unknown"; hostname = "unknown";
} }
SECURE("client %i: opened from %s\n", i, hostname);
client_init(&(clients[i]), fd); client = xcalloc(1, sizeof(*client));
list_add(&client->siblings, &clients);
++num_clients;
client_init(client, fd);
SECURE("client %i: opened from %s\n", client->num, hostname);
} }
static int client_process_line(struct client *client) static int client_process_line(struct client *client)
@ -424,55 +439,52 @@ static int client_read(struct client *client)
static void client_manager_register_read_fd(fd_set * fds, int *fdmax) static void client_manager_register_read_fd(fd_set * fds, int *fdmax)
{ {
unsigned int i; struct client *client;
FD_ZERO(fds); FD_ZERO(fds);
addListenSocketsToFdSet(fds, fdmax); addListenSocketsToFdSet(fds, fdmax);
for (i = 0; i < client_max_connections; i++) { list_for_each_entry(client, &clients, siblings) {
if (clients[i].fd >= 0 && !clients[i].expired if (!client->expired && !client->deferred_send) {
&& !clients[i].deferred_send) { FD_SET(client->fd, fds);
FD_SET(clients[i].fd, fds); if (*fdmax < client->fd)
if (*fdmax < clients[i].fd) *fdmax = client->fd;
*fdmax = clients[i].fd;
} }
} }
} }
static void client_manager_register_write_fd(fd_set * fds, int *fdmax) static void client_manager_register_write_fd(fd_set * fds, int *fdmax)
{ {
unsigned int i; struct client *client;
FD_ZERO(fds); FD_ZERO(fds);
for (i = 0; i < client_max_connections; i++) { list_for_each_entry(client, &clients, siblings) {
if (clients[i].fd >= 0 && !clients[i].expired if (client->fd >= 0 && !client->expired
&& clients[i].deferred_send) { && client->deferred_send) {
FD_SET(clients[i].fd, fds); FD_SET(client->fd, fds);
if (*fdmax < clients[i].fd) if (*fdmax < client->fd)
*fdmax = clients[i].fd; *fdmax = client->fd;
} }
} }
} }
static void closeNextErroredInterface(void) static void closeNextErroredInterface(void)
{ {
struct client *client, *n;
fd_set fds; fd_set fds;
struct timeval tv; struct timeval tv;
unsigned int i;
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 0; tv.tv_usec = 0;
for (i = 0; i < client_max_connections; i++) { list_for_each_entry_safe(client, n, &clients, siblings) {
if (clients[i].fd >= 0) { FD_ZERO(&fds);
FD_ZERO(&fds); FD_SET(client->fd, &fds);
FD_SET(clients[i].fd, &fds); if (select(client->fd + 1,
if (select(clients[i].fd + 1, &fds, NULL, NULL, &tv) < 0) {
&fds, NULL, NULL, &tv) < 0) { client_close(client);
client_close(&clients[i]); return;
return;
}
} }
} }
} }
@ -482,7 +494,7 @@ int client_manager_io(void)
fd_set rfds; fd_set rfds;
fd_set wfds; fd_set wfds;
fd_set efds; fd_set efds;
unsigned int i; struct client *client, *n;
int selret; int selret;
int fdmax; int fdmax;
@ -514,19 +526,17 @@ int client_manager_io(void)
getConnections(&rfds); getConnections(&rfds);
for (i = 0; i < client_max_connections; i++) { list_for_each_entry_safe(client, n, &clients, siblings) {
if (clients[i].fd >= 0 if (FD_ISSET(client->fd, &rfds)) {
&& FD_ISSET(clients[i].fd, &rfds)) {
if (COMMAND_RETURN_KILL == if (COMMAND_RETURN_KILL ==
client_read(&(clients[i]))) { client_read(client)) {
return COMMAND_RETURN_KILL; return COMMAND_RETURN_KILL;
} }
clients[i].lastTime = time(NULL); client->lastTime = time(NULL);
} }
if (clients[i].fd >= 0 if (FD_ISSET(client->fd, &wfds)) {
&& FD_ISSET(clients[i].fd, &wfds)) { client_write_deferred(client);
client_write_deferred(&clients[i]); client->lastTime = time(NULL);
clients[i].lastTime = time(NULL);
} }
} }
@ -538,7 +548,6 @@ int client_manager_io(void)
void client_manager_init(void) void client_manager_init(void)
{ {
unsigned int i;
char *test; char *test;
ConfigParam *param; ConfigParam *param;
@ -586,31 +595,19 @@ void client_manager_init(void)
client_max_output_buffer_size = tmp * 1024; client_max_output_buffer_size = tmp * 1024;
} }
clients = xmalloc(sizeof(clients[0]) * client_max_connections);
list_cache = xcalloc(client_list_cache_size, sizeof(struct strnode)); list_cache = xcalloc(client_list_cache_size, sizeof(struct strnode));
list_cache_head = &(list_cache[0]); list_cache_head = &(list_cache[0]);
list_cache_tail = &(list_cache[client_list_cache_size - 1]); list_cache_tail = &(list_cache[client_list_cache_size - 1]);
for (i = 0; i < client_max_connections; i++) {
clients[i].fd = -1;
clients[i].send_buf = NULL;
clients[i].send_buf_size = 0;
clients[i].send_buf_alloc = 0;
clients[i].num = i;
}
} }
static void client_close_all(void) static void client_close_all(void)
{ {
unsigned int i; struct client *client, *n;
list_for_each_entry_safe(client, n, &clients, siblings)
client_close(client);
num_clients = 0;
for (i = 0; i < client_max_connections; i++) {
if (clients[i].fd >= 0)
client_close(&(clients[i]));
if (clients[i].send_buf)
free(clients[i].send_buf);
}
free(list_cache); free(list_cache);
} }
@ -618,25 +615,21 @@ void client_manager_deinit(void)
{ {
client_close_all(); client_close_all();
free(clients);
client_max_connections = 0; client_max_connections = 0;
} }
void client_manager_expire(void) void client_manager_expire(void)
{ {
unsigned int i; struct client *client, *n;
for (i = 0; i < client_max_connections; i++) { list_for_each_entry_safe(client, n, &clients, siblings) {
if (clients[i].fd >= 0) { if (client->expired) {
if (clients[i].expired) { DEBUG("client %i: expired\n", client->num);
DEBUG("client %i: expired\n", i); client_close(client);
client_close(&(clients[i])); } else if (time(NULL) - client->lastTime >
} else if (time(NULL) - clients[i].lastTime > client_timeout) {
client_timeout) { DEBUG("client %i: timeout\n", client->num);
DEBUG("client %i: timeout\n", i); client_close(client);
client_close(&(clients[i]));
}
} }
} }
} }
@ -691,17 +684,11 @@ static void client_write_deferred(struct client *client)
static struct client *client_by_fd(int fd) static struct client *client_by_fd(int fd)
{ {
static unsigned int i; struct client *client;
assert(fd >= 0); list_for_each_entry(client, &clients, siblings)
if (client->fd == fd)
if (i < client_max_connections && clients[i].fd >= 0 && return client;
clients[i].fd == fd)
return &clients[i];
for (i = 0; i < client_max_connections; i++)
if (clients[i].fd == fd)
return &clients[i];
return NULL; return NULL;
} }
@ -745,8 +732,7 @@ static void client_write_output(struct client *client)
ssize_t ret; ssize_t ret;
struct sllnode *buf; struct sllnode *buf;
if (client->fd < 0 || client->expired || if (client->expired || !client->send_buf_used)
!client->send_buf_used)
return; return;
if ((buf = client->deferred_send)) { if ((buf = client->deferred_send)) {