aisl/src/instance.c

343 lines
6.0 KiB
C

/*
* src/instance.c
*
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/select.h>
#include <sys/time.h>
#ifndef AISL_WITHOUT_SSL
#include <openssl/err.h>
#endif
#include "debug.h"
#include "str-utils.h"
#include "buffer.h"
#include "client.h"
#include "server.h"
//#include "globals.h"
#include "stream.h"
#include "instance.h"
#ifndef AISL_WITHOUT_SSL
static uint32_t m_instances = 0;
static const aisl_ssl_t
aisl_new_ssl( aisl_t instance, const aisl_cfg_ssl_t cfg_ssl)
{
SSL_CTX * ssl_ctx = NULL;
aisl_ssl_t * list = instance->ssl,
ssl;
/* lookup for existing contexts */
while( (ssl = *list) )
{
if (ssl->key_file && strcmp(ssl->key_file, cfg_ssl->key_file)==0 &&
ssl->crt_file && strcmp(ssl->crt_file, cfg_ssl->crt_file)==0 )
{
ssl_ctx = ssl->ctx;
break;
}
list++;
}
ssl = aisl_ssl_new( cfg_ssl->host,
cfg_ssl->key_file,
cfg_ssl->crt_file,
ssl_ctx );
if (ssl)
{
if ( !ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance))
{
aisl_ssl_free(ssl);
ssl = NULL;
}
}
return ssl;
}
#endif
/* Initialization functions */
__attribute__ ((visibility ("default") ))
aisl_t
aisl_new( aisl_cfg_t cfg )
{
aisl_t instance;
/* allocate root structure */
if ( !(instance = calloc(1, sizeof(struct aisl))) )
goto finally;
/* allocate servers */
if ( !(instance->srv = calloc(cfg->srv_cnt+1, sizeof(aisl_server_t))) )
goto release;
for (int i=0; i<cfg->srv_cnt; i++)
{
DPRINTF("new srv %d", i);
if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance)))
goto release;
}
#ifndef AISL_WITHOUT_SSL
if ((m_instances++) == 0)
{
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
if ( !(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof(aisl_ssl_t))) )
goto release;
for (int i=0; i<cfg->ssl_cnt; i++)
{
DPRINTF("new ssl %d", i);
if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i])))
goto release;
}
#endif
if ( list_init(&instance->client_spool, cfg->client_spool_size) == -1 )
goto release;
instance->accept_limit = cfg->client_accept_limit;
instance->silence_timeout = cfg->client_silence_timeout;
instance->callback = cfg->callback;
instance->p_ctx = cfg->p_ctx;
goto finally;
release:
aisl_free(instance);
instance = NULL;
finally:
return instance;
}
__attribute__ ((visibility ("default") ))
void
aisl_free( aisl_t instance )
{
if (instance->srv)
{
aisl_server_t * srv = instance->srv;
while (*srv)
{
aisl_server_free(*srv);
srv++;
}
free(instance->srv);
}
list_release(&instance->client_spool, (list_destructor_t) aisl_client_free );
#ifndef AISL_WITHOUT_SSL
if (instance->ssl)
{
aisl_ssl_t * ssl = instance->ssl;
while (*ssl)
{
aisl_ssl_free(*ssl);
ssl++;
}
free(instance->ssl);
}
if ((--m_instances) == 0)
{
EVP_cleanup();
}
#endif
free(instance);
}
#ifndef AISL_WITHOUT_SSL
SSL_CTX *
aisl_get_ssl_ctx( aisl_t instance, const char * host )
{
aisl_ssl_t * list = instance->ssl,
ssl;
if (host)
{
while ( (ssl = *list) )
{
if (str_cmpi(ssl->host, host) == 0)
{
return ssl->ctx;
}
list++;
}
}
return NULL;
}
#endif
void
aisl_raise_evt( aisl_t instance, aisl_evt_t const evt )
{
#ifdef AISL_WITHOUT_STRINGIFIERS
DPRINTF("! %d", evt->code);
#else
DPRINTF("! %s", aisl_evt_code_to_string(evt->code));
#endif
if (instance->callback)
instance->callback(evt, instance->p_ctx);
}
void
aisl_raise( aisl_t instance,
void * source,
aisl_evt_code_t code,
aisl_status_t status )
{
struct aisl_evt evt;
evt.source = source;
evt.code = code;
evt.status = status;
aisl_raise_evt(instance, &evt);
}
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_run_cycle( aisl_t instance )
{
aisl_status_t result = AISL_IDLE;
aisl_server_t * list = instance->srv,
srv;
aisl_client_t cli;
while ( (srv = *list) )
{
cli = NULL;
if (aisl_server_touch(srv, &cli) != AISL_IDLE)
result = AISL_SUCCESS;
if (cli)
{
DPRINTF("Accepted %p", (void*)cli);
if ( list_append(&instance->client_spool, cli) == -1 )
aisl_client_free(cli);
}
list++;
}
for (int32_t i=0; i < instance->client_spool.count; i++)
{
cli = LIST_INDEX(instance->client_spool, i);
if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE)
result = AISL_SUCCESS;
/* if (aisl_client_is_timed_out( c, instance->silence_timeout ) )
aisl_raise( instance, c, AISL_EVENT_CLIENT_TIMEOUT );
*/
if ( !aisl_client_is_online(cli) )
{
aisl_client_free( cli );
list_remove_index(&instance->client_spool, i);
}
}
return result;
}
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_sleep( aisl_t instance, uint32_t usec )
{
int maxfd=0,
sd;
size_t i;
struct timeval timeout = {0,usec};
memset(&timeout, 0, sizeof(struct timeval));
timeout.tv_usec = usec;
fd_set fs;
FD_ZERO (&fs);
aisl_server_t * list = instance->srv,
srv;
while ( (srv = *list) )
{
sd = aisl_server_get_socket(srv);
if (sd != -1)
{
FD_SET(sd, &fs);
if (sd > maxfd) maxfd = sd;
}
list++;
}
for (i=0; i<instance->client_spool.count; i++)
{
aisl_client_t c = LIST_INDEX(instance->client_spool, i);
sd = aisl_client_get_socket(c);
if (sd != -1)
{
FD_SET(sd, &fs);
if (sd > maxfd) maxfd = sd;
}
}
switch ( select(maxfd+1, &fs, NULL, NULL, &timeout) )
{
case -1:
return AISL_SYSCALL_ERROR;
case 0:
return AISL_IDLE;
default:
return AISL_SUCCESS;
}
}