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:
parent
a091c148e6
commit
61443c13e6
164
src/client.c
164
src/client.c
@ -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)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user