Select Git revision
configure.in
Forked from
Nettle / nettle
Source project has a limited visibility.
connections.c 20.92 KiB
/*
* $Id: connections.c,v 0.19 1992/06/11 14:32:54 ceder Exp $
* Copyright (C) 1991 Lysator Academic Computer Association.
*
* This file is part of the LysKOM server.
*
* LysKOM is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* LysKOM is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with LysKOM; see the file COPYING. If not, write to
* Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
* or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
* MA 02139, USA.
*
* Please mail bug reports to bug-lyskom@lysator.liu.se.
*/
/*
* connections.c
*
* Denna fil inneh}ller niv}n ovanf|r isc.
*
* Created by Willf|r 31/3-90. Mostly written by ceder.
*/
static char *rcsid = "$Id: connections.c,v 0.19 1992/06/11 14:32:54 ceder Exp $";
#include <errno.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#include "s-string.h"
#include <kom-types.h>
#include "lyskomd.h"
#include <config.h>
#include <debug.h>
#include "end-of-atomic.h"
#include "log.h"
#include <services.h>
#include "isc-interface.h"
#include <kom-errno.h>
#include "com.h"
#include "connections.h"
#include "prot-a-parse.h"
#include "prot-a-output.h"
#include "prot-a.h"
#include <server/smalloc.h>
#include "prot-a-parse-arg.h"
#include "isc-parse.h"
#include "cache.h"
#include "send-async.h"
#include "mux.h"
#include "mux-parse.h"
#include "internal-connections.h"
#include "rfc931.h"
IscMaster * kom_server_mcb = NULL;
Connection * active_connection = NULL;
/*
* This is set TRUE when the server should be closed. It is checked
* each time around the main loop. It is set if someone with enough
* privileges issues a `shutdown', or of lyskomd receives a SIGHUP.
* This not an abort: all data is saved before we exit.
*/
volatile Bool go_and_die = FALSE;
/*
* Once upon a time the entire database was always held in core, and
* saved to disk when we got a SIGUSR1 signal. Nowadays the server
* keeps track of the time itself and saves at appropriate intervalls.
* It does still write some statistics when it get a SIGUSR1 signal.
* The signal handler sets do_sync_db. The name is retained for
* historical reasons.
*/
volatile Bool do_sync_db = FALSE;
jmp_buf parse_env;
const Fnc_descriptor fnc_defs[]={
#include "fnc-def-init.incl"
};
u_long service_statistics[sizeof (fnc_defs) / sizeof (Fnc_descriptor)];
BUGDECL;
static void
logout_client(Connection *cp)
{
Connection *real_active_connection;
if ( active_connection != NULL )
{
log("BUGCHK: logout_client(%d): connection %d is active.\n",
cp->session_no, active_connection->session_no);
}
switch (cp->magic)
{
case CONN_MAGIC_ALLOC:
break;
case CONN_MAGIC_FREE:
log("LOGOUT_CLIENT: Trying to free freed Connection - ignored!\n");
return;
default:
restart_kom("LOGOUT_CLIENT: Bad magic number\n");
}
if ( cp->pers_no != 0 )
{
real_active_connection = active_connection; /* Shouldn't be needed +++ */
active_connection = cp;
logout();
active_connection = real_active_connection;
}
switch(cp->protocol)
{
case 0: /* Hasn't yet allocated any protocol. */
break;
case 'A':
prot_a_destruct(cp);
break;
default:
restart_kom("logout_client(): Bad protocol.\n");
}
mux_close(cp); /* Close fd's. Clear mux and isc structures. */
kill_client(cp); /* Free the Connection */
}
/*
* This function is part of the shutdown tidy-up sequence.
*/
void
logout_all_clients(void)
{
Session_no sess = 0;
Connection *conn;
while ( (sess = traverse_connections (sess)) != 0)
{
conn = get_conn_by_number (sess);
if ( conn == NULL )
restart_kom("logout_all_clients(): cant get session %d.\n",
sess);
else
logout_client (conn);
}
if ( traverse_connections (0) != 0)
restart_kom("logout_all_clients(): traverse_connections(0) == %d.\n",
traverse_connections(0));
}
/*
* Call a function in services.c. A pointer to the result is returned.
* The pointer points to static data which is overwritten on each call.
*/
static Success
call_function (Connection * client,
Result_holder * res) /* This is a union. */
{
Success status=FAILURE; /* OK if the call was successful. */
if ( active_connection != NULL )
{
log("call_function(%d): active_connection = %d",
client->session_no, active_connection->session_no);
}
active_connection = client;
service_statistics[client->function]++;
#include "call-switch.incl"
active_connection = NULL;
return status;
}
static void
parse_packet(Connection *client)
{
if ( client->protocol == '\0' ) /* Not known yet. */
{
client->protocol = parse_char(client);
switch(client->protocol)
{
case 'A':
prot_a_init(client);
break;
default:
client->protocol = '\0';
mux_printf(client,
"%%%%LysKOM unsupported protocol.\n");
mux_flush(client);
BUG(("%%%%Unsupported protocol.\n"));
longjmp(parse_env, ISC_LOGOUT);
}
}
switch(client->protocol)
{
case 'A':
prot_a_parse_packet(client);
break;
default:
restart_kom("parse_packet(): Bad protocol.\n");
break;
}
}
/*
* Free all parsed areas which are no longer needed. Re-initialize all
* parse_pos fields so that the parse will expect a new function.
*
* This function is called
* when a parse error occurs
* when a parse is complete and the function has executed.
*/
static void
free_parsed(Connection *client)
{
s_clear(&client->c_string0);
s_clear(&client->c_string1);
client->string0 = EMPTY_STRING; /* So that no one frees it. */
sfree( client->c_misc_info_p);
client->c_misc_info_p = NULL;
sfree( client->c_local_text_no_p);
client->c_local_text_no_p = NULL;
client->parse_pos = 0;
client->fnc_parse_pos = 0;
client->array_parse_pos = 0;
client->struct_parse_pos = 0;
client->string_parse_pos = 0;
}
/*
* Send a reply to a call.
*/
static void
reply(Connection *client,
Success status,
Result_holder *result)
{
switch(client->protocol)
{
case 'A':
prot_a_reply(client, status, result);
break;
default:
restart_kom("reply(): Bad protocol.\n");
break;
}
}
/*
* Try to parse enough data from client->unparsed to call a function.
* If more data is needed set client->more_to_parse to FALSE.
*/
static void
parse_unparsed(Connection *client)
{
String tmp_str = EMPTY_STRING;
Success status;
Result_holder result;
switch ( setjmp(parse_env) )
{
case 0 :
/* Parse message. If message is complete call function and reply. */
parse_packet(client);
client->last_request = time(NULL);
status = call_function(client, &result);
reply(client, status, &result);
free_parsed(client);
end_of_atomic(FALSE);
break;
case ISC_PROTOCOL_ERR:
s_clear(&client->string0);
free_parsed(client);
mux_printf(client, "%% LysKOM protocol error.\n");
mux_flush(client);
BUG(("%%%% Protocol error.\n"));
s_clear(&client->unparsed);
client->first_to_parse = 0;
client->more_to_parse = FALSE;
end_of_atomic(FALSE);
break;
case ISC_MSG_INCOMPLETE:
client->more_to_parse = FALSE;
break;
case ISC_LOGOUT:
add_to_kill_list(client);
break;
}
/* Delete the parsed part of 'unparsed' */
if ( s_substr(&tmp_str, client->unparsed,
client->first_to_parse, END_OF_STRING) != OK )
restart_kom("parse_unparsed: s_substr\n");
s_clear(&client->unparsed);
client->unparsed = tmp_str;
client->first_to_parse = 0;
}
/*
* There is a message in the event. Parse it.
*/
static void
parse_message(Connection *cp, String tmp_str)
{
VBUGSTR(tmp_str);
cp->more_to_parse = TRUE;
if ( s_strcat(&cp->unparsed, tmp_str) != OK )
restart_kom("parse_message(): s_strcat\n");
tmp_str = EMPTY_STRING;
/* Parse this packet, but leave the last token if it isn't complete. */
parse_unparsed(cp);
return;
}
static void
mux_handle_packet(Mux *mux)
{
Mux_client *mcp = NULL;
Connection *cp = NULL;
switch(mux->parse.function)
{
case 0: /* ping */
isc_printf(mux->scb, "0\n");
isc_flush(mux->scb);
break;
case 1: /* login */
mcp = mux_getclientbyid(mux, mux->parse.num);
if (mcp)
{ /* ERROR %d: Already logged in\n */
isc_printf(mux->scb, "4\n");
isc_flush(mux->scb);
return;
}
cp = new_client();
cp->mux = mux;
cp->hostname = mux->parse.string;
mux->parse.string = EMPTY_STRING;
mux_addclient(mux, mux->parse.num, cp);
BUG(("\n[Client %d from %.*s via MUX %s",
cp->session_no,
(int)cp->hostname.len,
cp->hostname.string,
isc_gethostname(mux->scb->info.tcp.raddr, NULL, 0)));
BUG((" is connecting]\n"));
break;
case 2: /* logout */
mcp = mux_getclientbyid(mux, mux->parse.num);
if (!mcp)
{ /* ERROR %d: No such mux-session */
isc_printf(mux->scb, "4\n");
isc_flush(mux->scb);
return;
}
cp = mcp->conn;
BUG(("\n[Client %d via MUX(%s)",
cp->session_no,
isc_gethostname(mux->scb->info.tcp.raddr, NULL, 0)));
BUG((" is logging out by request]\n"));
add_to_kill_list(cp);
break;
case 3: /* msg */
mcp = mux_getclientbyid(mux, mux->parse.num);
if (!mcp)
{ /* ERROR %d: No such mux-session */
isc_printf(mux->scb, "4\n");
isc_flush(mux->scb);
return;
}
cp = mcp->conn;
VBUG(("\n[Message client %d via MUX #%d]\n",
cp->session_no, (int)mux->scb));
parse_message(cp, mux->parse.string);
break;
default:
VBUG(("\nMUX Protocol error from MUX #%d]\n",
(int)mux->scb));
isc_printf(mux->scb, "5\n");
isc_flush(mux->scb);
break;
}
}
static void
mux_logout(Mux *mp)
{
int i;
for (i = 0; i < mp->client_c; i++)
if (mp->client_v[i].conn)
{
BUG(("\n[Client %d via MUX(%s)",
mp->client_v[i].conn->session_no,
isc_gethostname(mp->scb->info.tcp.raddr, NULL, 0)));
BUG((" is logging out by MUX shutdown]\n"));
logout_client(mp->client_v[i].conn);
end_of_atomic(FALSE);
}
isc_destroy(kom_server_mcb, mp->scb);
mux_destruct(mp);
}
static void
mux_parse_unparsed(Mux *mux)
{
String tmp_str;
while ( mux->parse.more_to_parse )
{
switch ( setjmp(mux_parse_env) )
{
case 0 :
/* Parse message. Take apropriate action if message is complete. */
mux_parse_packet(mux);
mux_handle_packet(mux);
mux_free_parsed(mux);
break;
case MUX_PROTOCOL_ERR:
s_clear(&mux->parse.string);
mux_free_parsed(mux);
isc_printf(mux->scb, "5\n");
isc_flush(mux->scb);
BUG(("%%%% Mux protocol error.\n"));
s_clear(&mux->parse.unparsed);
mux->parse.first_to_parse = 0;
mux->parse.more_to_parse = FALSE;
break;
case MUX_MSG_INCOMPLETE:
mux->parse.more_to_parse = FALSE;
break;
case MUX_LOGOUT:
mux_logout(mux);
return;
}
/* Delete the parsed part of 'unparsed' */
tmp_str = EMPTY_STRING;
if ( s_substr(&tmp_str, mux->parse.unparsed,
mux->parse.first_to_parse, END_OF_STRING) != OK )
restart_kom("mux_parse_unparsed: s_substr\n");
s_clear(&mux->parse.unparsed);
mux->parse.unparsed = tmp_str;
mux->parse.first_to_parse = 0;
}
}
static void
mux_parse_message(Mux *mp, String tmp_str)
{
VBUGSTR(tmp_str);
BUG(("mux_parse_message(): mp->parse.unparsed = { len = %d, "
"string = %p }\n", mp->parse.unparsed.len,
(void *)mp->parse.unparsed.string));
if ( s_strcat(&mp->parse.unparsed, tmp_str) != OK )
restart_kom("mux_parse_message(): s_strcat\n");
tmp_str = EMPTY_STRING;
/* Parse this packet, but leave the last token if it isn't complete. */
mux_parse_unparsed(mp);
return;
}
/*
* parse data in client->unparsed and mux->unparsed if there
* is data that has not yet been taken care of. Return TRUE
* if there was no data to take care of.
*/
static Bool
parse_forgotten(void)
{
Bool flg = FALSE; /* Was there anything? */
Connection *ci;
Session_no session_no = 0;
if ( traverse_connections(0) == 0 )
return TRUE;
while ( ( session_no = traverse_connections( session_no ) ) != 0 )
{
ci = get_conn_by_number (session_no);
if ( ci->more_to_parse )
{
parse_unparsed(ci);
flg = TRUE;
}
}
return !flg;
}
/* Return 1 if the named file exists, 0 otherwise */
static int
fexists(const char *filename)
{
struct stat buf;
int code;
code = !stat(filename, &buf);
errno = 0;
return code;
}
void
dump_statistics(void)
{
static time_t last_dump = NO_TIME;
int i;
time_t now;
FILE *fp;
extern char statisticfile[1024]; /* Defined in ramkomd.c */
if ( (fp = fopen(statisticfile, "a")) == NULL )
{
log(__FILE__ ": dump_statistics(): can't open file %s\n",
statisticfile);
return;
}
time(&now);
if (last_dump == NO_TIME)
{
fprintf(fp, "RESTART\n");
last_dump = now;
}
fprintf(fp, "TIME: %s", ctime(&now));
fprintf(fp, "SECONDS: %d\n", (int)difftime(now, last_dump));
fprintf(fp, "STATISTICS:");
for ( i = 0; i < sizeof(service_statistics) / sizeof(u_long); i++)
{
fprintf(fp, " %lu", service_statistics[i]);
service_statistics[i]=0;
}
fprintf(fp, "\n");
fclose(fp);
last_dump = now;
}
/* List of connections to kill. */
Session_no *kill_list = NULL;
int kill_list_size = 0;
/* Schedule this client for termination. */
void
add_to_kill_list(Connection *conn)
{
int i;
for (i = 0; i < kill_list_size; i++)
if (kill_list[kill_list_size] == conn->session_no)
{
log("add_to_kill_list(%d): already present as %d of %d.\n",
conn->session_no, i, kill_list_size);
return;
}
if (kill_list == NULL)
{
if (kill_list_size != 0)
restart_kom("add_to_kill_list(): size = %d\n", kill_list_size);
kill_list_size = 1;
kill_list = smalloc(sizeof(Session_no));
}
else
{
kill_list_size++;
kill_list = srealloc(kill_list, kill_list_size * sizeof(Session_no));
}
kill_list[kill_list_size-1] = conn->session_no;
}
/*
* check_kill_flg must NEVER be called inside an atomic call!
*/
static void
check_kill_flg(void)
{
Connection *conn;
if ( active_connection != NULL )
{
restart_kom("check_kill_flg: active_connection == %d",
active_connection->session_no);
}
while (kill_list_size > 0)
{
--kill_list_size;
conn = get_conn_by_number (kill_list[kill_list_size]);
if (conn == NULL)
{
log("check_kill_flg(): Connection %d doesn't exist.\n",
kill_list[kill_list_size]);
}
else
{
logout_client (conn);
end_of_atomic (FALSE);
}
}
if (kill_list != NULL)
{
sfree (kill_list);
kill_list = NULL;
}
}
static void
login_request(IscEvent *event)
{
Connection * cp;
char *realuser;
int localport;
IscAddress *isc_adr;
char *hostname = NULL;
if (isc_sessions(kom_server_mcb) >= MAX_NO_OF_CONNECTIONS)
{
BUG(("Connection attempt rejected.\n"));
isc_printf(event->session, "%s", "%% No connections left.\n");
isc_flush(event->session);
isc_destroy(kom_server_mcb, event->session);
async_rejected_connection();
return;
}
/* Supress logins if /etc/nologin exists */
if (fexists("/etc/nologin"))
{
isc_printf(event->session, "%s", "%% No logins allowed.\n");
isc_flush(event->session);
isc_destroy(kom_server_mcb, event->session);
return;
}
/* Find out if it is a mux or a client that tries to connect to
us. Do it by getting the local port number. */
isc_adr = isc_getladdress (event->session);
if (isc_adr == NULL)
restart_kom("login_request(): can't isc_getladdress (%lu)\n",
event->session);
localport = isc_getportnum (isc_adr);
isc_freeaddress (isc_adr);
if (localport == num_ip_mux_port)
{
/* Multiplexer requesting connection */
/*
** Create, and setup the MUX for the MUX case
*/
event->session->udg = mux_create(MUX_TYPE_MUX, event->session);
BUG(("MUX #%d at %s connecting.\n",
(int)event->session,
isc_gethostname(event->session->info.tcp.raddr, NULL, 0)));
}
else if (localport == num_ip_client_port)
{
/* Client requesting connection */
/*
** Create, and setup the MUX for the CLIENT case
*/
event->session->udg = mux_create(MUX_TYPE_CLIENT,
event->session);
cp = new_client();
cp->mux = event->session->udg;
hostname = isc_gethostname(event->session->info.tcp.raddr, NULL, 0);
if (hostname == NULL)
hostname = isc_getipnum(event->session->info.tcp.raddr, NULL, 0);
if (hostname == NULL)
{
log("WNG: login_request(): unknown hostid.\n");
s_crea_str(&cp->hostname, "unknown");
}
else
s_crea_str(&cp->hostname, hostname);
/* Get the real user name, as returned by the Ident protocol
(rfc 931). */
realuser = get_real_username(event->session);
if (realuser != NULL)
s_crea_str(&cp->ident_user, realuser);
mux_addclient(event->session->udg, 0, cp);
BUG(("\n[Client %d from %s", cp->session_no,
isc_gethostname(event->session->info.tcp.raddr, NULL, 0)));
BUG((" is connecting]\n"));
}
else
{
/* Help! It was neither a client, nor a mux. */
restart_kom("login_request(): Neither client, nor mux.\n");
}
}
static void
logout_request(IscEvent *event)
{
Connection * cp;
if (event->session->udg->type == MUX_TYPE_MUX)
{
BUG(("\n[MUX #%d at %s", (int)event->session,
isc_gethostname(event->session->info.tcp.raddr, NULL, 0)));
BUG((" is logging out]\n"));
/*
** Logout any remaining clients
*/
mux_logout(event->session->udg);
}
else
{
cp = event->session->udg->client_v[0].conn;
BUG(("\n[Client %d from %s", cp->session_no,
isc_gethostname(event->session->info.tcp.raddr, NULL, 0)));
BUG((" is logging out]\n"));
add_to_kill_list(cp);
}
}
static void
message_request(IscEvent *event)
{
Connection *cp;
String tmp_str;
if (event->session->udg->type == MUX_TYPE_MUX)
{
event->session->udg->parse.more_to_parse = TRUE;
tmp_str.string = (unsigned char *)event->msg->buffer;
tmp_str.len = event->msg->length;
mux_parse_message(event->session->udg, tmp_str);
}
else
{
cp = event->session->udg->client_v[0].conn;
VBUG(("\n[Message from client %d]\n",
cp->session_no));
/*
** Pass the message on to the parser
*/
tmp_str.string = (unsigned char *)event->msg->buffer;
tmp_str.len = event->msg->length;
parse_message(cp, tmp_str);
}
}
static long
timevaldiff(struct timeval a,
struct timeval b)
{
return 1000000 * (a.tv_sec - b.tv_sec) + a.tv_usec - b.tv_usec;
}
void
toploop(void)
{
IscEvent * event;
Bool pending_input = TRUE; /* Should be TRUE whenever there
is or might be pending input
from any client in the unparsed
buffer. */
long timeout = 0; /* In milliseconds. Start with a
small value since we should
start garbing right away. */
struct timeval before, after;
while ( !go_and_die )
{
if (do_sync_db)
{
/*
* do_sync_db used to save the entire database. Nowadays
* all it does is to print some statistics.
*/
cache_sync(); /* A noop in lyskomd. Actually
stops the server and dumps
everything in ramkomd. */
dump_statistics();
do_sync_db = FALSE;
}
gettimeofday(&before, NULL);
event = isc_getnextevent(kom_server_mcb,
pending_input ? 0 : timeout );
gettimeofday(&after, NULL);
timeout -= timevaldiff(after, before) / 1000;
if ( timeout < 0 )
timeout = 0;
if ( event == NULL )
restart_kom("toploop(): Got NULL event - shouldn't happen...\n");
switch ( event->event )
{
case ISC_EVENT_ERROR:
if (errno != EINTR)
{
log("toploop: ISC_EVENT_ERROR (error = '%s'):\n '%s'\n",
strerror(errno), event->msg ? event->msg->buffer
: "(unknown)");
}
break;
case ISC_EVENT_TIMEOUT:
BUG((">"));
#ifdef DEBUG
fflush(stdout);
#endif
if ( pending_input == FALSE )
timeout = end_of_atomic(TRUE); /* Idle. Do some cleaning up. */
else
{
if ( timeout == 0 )
end_of_atomic(FALSE);
pending_input = !parse_forgotten();
}
break;
case ISC_EVENT_LOGIN:
pending_input = TRUE;
login_request(event);
break;
case ISC_EVENT_LOGOUT:
pending_input = TRUE;
logout_request(event);
break;
case ISC_EVENT_MESSAGE:
pending_input = TRUE;
message_request(event);
break;
default:
pending_input = TRUE;
log("ERROR: toploop(): Unknown ISC_EVENT\n");
break;
}
isc_dispose(event);
check_kill_flg();
}
}