catalinux / Conn (public) (License: LGPLv2) (since 2016-03-01) (hash sha1)
Net library for easy building ipv4/ipv6 network daemons/clients

/Conn_engine_core.c (6a1675c08b466c03c54121a053cf00168da08a31) (23351 bytes) (mode 100644) (type blob)

/*
 * Author: Catalin(ux) M BOIE <catab at embedromix.ro>
 * Date: 2004-2008
 * Description:	Some functions to help writing network servers and clients,
 *		both ipv4 and ipv6.
 * Licence: LGPL
 */

#include "Conn_engine_core.h"


/* Visible variables */
void		(*Conn_accept_cb)(struct Conn *C) = NULL;
void		(*Conn_recv_cb)(struct Conn *C) = NULL;
void		(*Conn_send_cb)(struct Conn *C) = NULL;
void		(*Conn_data_cb)(struct Conn *C) = NULL;
void		(*Conn_close_cb)(struct Conn *C) = NULL;
void		(*Conn_trigger_cb)(struct Conn *C) = NULL;
void		(*Conn_error_cb)(struct Conn *C) = NULL;
void		(*Conn_connected_cb)(struct Conn *C) = NULL;
void		(*Conn_accept_error_cb)(struct Conn *C) = NULL;

char		*(*Conn_status_slot_html_cb)(const struct Conn *C);
char		*(*Conn_status_cb)(void);


unsigned int		Conn_max_reached = 0;
unsigned int		Conn_default_ibuf = 128;
unsigned int		Conn_default_obuf = 128;
unsigned int		Conn_max_ibuf = 4096000;
unsigned int		Conn_max_obuf = 4096000;

/* Max bytes enqueued on one send/recv call */
unsigned int		Conn_max_send = 32 * 1024;
unsigned int		Conn_max_recv = 32 * 1024;

unsigned int		Conn_no = 0;
unsigned int		Conn_max = 0;
unsigned long		Conn_total = 0;
unsigned int		Conn_start = 0;
unsigned int		Conn_pending = 0;
struct timeval		Conn_now;
unsigned short		Conn_level = 0;			/* debug level */
unsigned int		Conn_accept_is_allowed;
unsigned int		Conn_accept_is_allowed_last;

struct Conn		*Conns = NULL;
unsigned int		Conn_inited = 0;
unsigned int		Conn_allocated = 0;
unsigned long long	Conn_id = 1;

char			Conn_error[512];

FILE			*Conn_Log = NULL;
int			debug_band = 11;

/* Flags */
unsigned int		CONN_POLLIN;
unsigned int		CONN_POLLOUT;
unsigned int		CONN_POLLPRI;
unsigned int		CONN_POLLERR;
unsigned int		CONN_POLLHUP;
unsigned int		CONN_POLLNVAL;
unsigned int		CONN_POLLRDNORM;
unsigned int		CONN_POLLRDBAND;

/* queues */
struct Conn_queue	Conn_queue_free;


/* Functions */

char *Conn_strerror(void)
{
	return Conn_error;
}

/*
 * Difference between two timeval strutures, in milliseconds
 */
long long Conn_time_diff(const struct timeval *t1, const struct timeval *t2)
{
	return (t1->tv_sec - t2->tv_sec) * 1000
		+ (t1->tv_usec - t2->tv_usec) / 1000;
}

/*
 * Returns string representation of errno code
 */
char *Conn_errno(const struct Conn *C)
{
	static char buf[256];
	char *is;

	switch (C->error_state) {
		case CONN_ERROR_USERREQ:	is = "user"; break;
		case CONN_ERROR_POLL:		is = "poll"; break;
		case CONN_ERROR_RECV:		is = "recv"; break;
		case CONN_ERROR_SEND:		is = "send"; break;
		case CONN_ERROR_SOCKET:		is = "socket"; break;
		case CONN_ERROR_HANGUP:		is = "hangup"; break;
		case CONN_ERROR_GETADDRINFO:	is = "lookup error"; break;
		case CONN_ERROR_EXPIRED:	is = "expired"; break;
		case CONN_ERROR_ACCEPT:		is = "accept"; break;
		case CONN_ERROR_MEM:		is = "allocation failed"; break;
		case CONN_ERROR_CONNECT:	is = "connect"; break;
		case CONN_ERROR_READ_TIMEOUT:	is = "read timeout"; break;
		case CONN_ERROR_CONN_TIMEOUT:	is = "conn timeout"; break;

		default: is = "?";
	}

	snprintf(buf, sizeof(buf), "%s (%s)",
		is, (C->xerrno > 0) ? strerror(C->xerrno) : "-");

	return buf;
}

/* set noblocking */
int Conn_setnonblock(const int fd)
{
	int	ret;
	long	flags;

	flags = fcntl(fd, F_GETFL, 0);
	if (flags == -1)
		return -1;

	flags |= O_NONBLOCK;

	ret = fcntl(fd, F_SETFL, flags);

	return ret;
}

void Log(const unsigned short level, char *format, ...)
{
	va_list	ap;
	FILE *out;

	if (level > Conn_level)
		return;

	if (Conn_Log == NULL)
		out = stderr;
	else
		out = Conn_Log;
	fprintf(out, "%ld.%06ld ",
		Conn_now.tv_sec, Conn_now.tv_usec);

	va_start(ap, format);
	vfprintf(out, format, ap);
	va_end(ap);
}

char *Conn_dump(const char *buf_src, const int len_src)
{
	int i, j;
	char tmp[3];
	char *buf_dst;
	unsigned char c;

	if (len_src < 0)
		return strdup("[Error: len < 0]");

	Log(30, "\tConn_dump(%p, len=%d)\n",
		buf_src, len_src);

	buf_dst = malloc(len_src * 4 + 1);
	if (buf_dst == NULL)
		return strdup("Memory allocation error1!");

	j = 0;
	for (i = 0; i < len_src; i++) {
		c = buf_src[i];
		if ((c < 32) || (c > 127)) {
			buf_dst[j++] = '[';
			snprintf(tmp, sizeof(tmp), "%02x", c);
			buf_dst[j++] = tmp[0];
			buf_dst[j++] = tmp[1];
			buf_dst[j++] = ']';
		} else {
			buf_dst[j++] = c;
		}
	}

	buf_dst[j] = '\0';

	/*
	Log(0, "%s ([%s], %d, [%s], %d\n",
		__FUNCTION__, buf_src, len_src, buf_dst, len_dst);
	*/

	return buf_dst;
}

char *Conn_dumphex(const char *buf_src, const int len_src)
{
	int i, j;
	char tmp[3];
	char *buf_dst;
	unsigned char c;

	if (len_src < 0)
		return strdup("[Error: len < 0]");

	Log(30, "\tConn_dumphex(%p, len=%d)\n",
		buf_src, len_src);

	buf_dst = malloc(len_src * 2 + 1);
	if (buf_dst == NULL)
		return strdup("Memory allocation error1!");

	j = 0;
	for (i = 0; i < len_src; i++) {
		c = buf_src[i];
		snprintf(tmp, sizeof(tmp), "%02x", c);
		buf_dst[j++] = tmp[0];
		buf_dst[j++] = tmp[1];
	}

	buf_dst[j] = '\0';

	return buf_dst;
}

void Conn_debug(FILE *f, const unsigned short debug)
{
	Conn_Log = f;
	Conn_level = debug;
}

char *Conn_state(const struct Conn *C)
{
	switch (C->state) {
		case CONN_STATE_FREE:		return "FREE";
		case CONN_STATE_EMPTY:		return "EMPTY";
		case CONN_STATE_OPEN:		return "OPEN";
		case CONN_STATE_LISTEN:		return "LISTEN";
		case CONN_STATE_CONNECT_0:	return "CONN0";
		case CONN_STATE_CONNECT_a:	return "CONNa";
		case CONN_STATE_CONNECT_b:	return "CONNb";

		default:			return "BUG?";
	}
}

/*
 * Expand the requested buffer
 * what = 0 for out buffer, what = 1 for input buffer
 * returns 0 if OK, -1 on error
 */
int Conn_try_expand_buf(struct Conn *C, const int what, const int needed)
{
	char *p;
	unsigned int hm;
	unsigned int slot, old_size, amount, head, tail;

	slot = C->slot;

	if (what == 0) {
		head = C->obuf_head;
		tail = C->obuf_tail;
	} else {
		head = C->ibuf_head;
		tail = C->ibuf_tail;
	}

	Log(10, "\tTry to expand buffer on slot %u for [%s] needed=%d head=%u tail=%u.\n",
		slot, what == 0 ? "o" : "i", needed,
		head, tail);

	amount = needed;

	if (what == 0) {
		if (amount < Conn_default_obuf)
			amount = Conn_default_obuf;
		old_size = Conns[slot].obuf_size;
		hm = Conns[slot].obuf_size + amount;
		if (hm > Conn_max_obuf)
			hm = Conn_max_obuf;
		p = realloc(Conns[slot].obuf, hm);
		if (p == NULL) {
			Log(3, "Cannot realloc obuf!\n");
			return -1;
		}
		Conns[slot].obuf = p;
		Conns[slot].obuf_size = hm;
		Log(10, "\tSucces. Old/new size = %u/%u.\n",
			old_size, Conns[slot].obuf_size);
	} else {
		if (amount < Conn_default_ibuf)
			amount = Conn_default_ibuf;
		old_size = Conns[slot].ibuf_size;
		hm = Conns[slot].ibuf_size + amount;
		if (hm > Conn_max_ibuf)
			hm = Conn_max_ibuf;
		p = realloc(Conns[slot].ibuf, hm);
		if (p == NULL) {
			Log(3, "Cannot realloc ibuf!\n");
			return -1;
		}
		Conns[slot].ibuf = p;
		Conns[slot].ibuf_size = hm;
		Log(10, "\tSucces. Old/new size = %u/%u.\n",
			old_size, Conns[slot].ibuf_size);
	}

	return 0;
}

static void Conn_poll_status(const short ev, char *ret)
{
	strcpy(ret, "________");

	if (ev & CONN_POLLIN)		ret[0] = 'I';
	if (ev & CONN_POLLPRI)		ret[1] = 'P';
	if (ev & CONN_POLLOUT)		ret[2] = 'O';
	if (ev & CONN_POLLERR)		ret[3] = 'E';
	if (ev & CONN_POLLHUP)		ret[4] = 'H';
	if (ev & CONN_POLLNVAL)		ret[5] = 'V';
	if (ev & CONN_POLLRDNORM)	ret[6] = 'r';
	if (ev & CONN_POLLRDBAND)	ret[7] = 'R';
}

char *Conn_domain(const struct Conn *C)
{
	switch (C->sock_domain) {
		case PF_INET:		return "IPv4";
		case PF_INET6:		return "IPv6";
		case PF_PACKET:		return "PACKET";

		default:		return "?";
	}
}

char *Conn_type(const struct Conn *C)
{
	switch (C->sock_type) {
		case SOCK_STREAM:	return "stream";
		case SOCK_DGRAM:	return "dgram";
		case SOCK_RAW:		return "raw";

		default:		return "?";
	}
}

static char *Conn_socktype(const struct Conn *C)
{
	switch (C->type) {
		case Conn_type_MASTER:		return "master";
		case Conn_type_CLIENT:		return "client";
		case Conn_type_UNK:		return "unk";

		default:
			return "?";
	}
}

/*
 * Returns a nice speed
 */
void Conn_speed(char *dst, const unsigned int dst_len, const unsigned int speed)
{
	float sp;

	sp = speed;

	if (speed < 1000)
		snprintf(dst, dst_len, "%.2fBps", sp);
	else if (speed < 1000 * 1000)
		snprintf(dst, dst_len, "%.2fKBps", sp / 1000);
	else
		snprintf(dst, dst_len, "%.2fMBps", sp / 1000 / 1000);
}

char *Conn_status_slot(const struct Conn *C)
{
	static char tmp[1024];
	char polle[16], pollr[16];
	char speedi[32], speedo[32];
	unsigned int dT, si, so;

	Conn_poll_status(C->events, polle);
	Conn_poll_status(C->revents, pollr);

	dT = Conn_now.tv_sec - C->start;
	if (dT == 0)
		dT = 1;
	si = C->bi / dT;
	so = C->bo / dT;

	Conn_speed(speedi, sizeof(speedi), si);
	Conn_speed(speedo, sizeof(speedo), so);

	snprintf(tmp, sizeof(tmp), "%4d fd%4d"
		" %4s %6s %5s %6s"
		" %39s/%-5d\n"
		"            Via%-5d [%s][%s] IO=%llu/%llu"
		" BS=%u/%u S=%s/%s"
		" T=%ld bw=%u f=%u tk=%u id=%llu\n",
		C->slot, C->fd,
		Conn_domain(C), Conn_type(C), Conn_socktype(C), Conn_state(C),
		C->addr, C->port, C->via, polle, pollr, C->bi, C->bo,
		C->ibuf_size, C->obuf_size, speedi, speedo,
		Conn_now.tv_sec - C->start,
		C->band_width, C->band_factor, C->band_tokens,
		C->id);

	return tmp;
}

char *Conn_status_slot_html(const struct Conn *C)
{
	static char tmp[1024];
	char polle[16], pollr[16], *ext = "";
	char speedi[32], speedo[32];
	unsigned int dT, si, so;

	Conn_poll_status(C->events, polle);
	Conn_poll_status(C->revents, pollr);

	dT = Conn_now.tv_sec - C->start;
	if (dT == 0)
		dT = 1;
	si = C->bi / dT;
	so = C->bo / dT;

	Conn_speed(speedi, sizeof(speedi), si);
	Conn_speed(speedo, sizeof(speedo), so);

	if (Conn_status_slot_html_cb)
		ext = Conn_status_slot_html_cb(C);

	snprintf(tmp, sizeof(tmp), "<td>%llu</td><td>%d</td><td>%d</td>"
		"<td>%s</td><td>%s</td><td>%s</td><td>%s</td>"
		"<td>%s/%d</td>"
		"<td>%d</td><td>%s</td><td>%s</td><td>%llu / %llu</td>"
		"<td>%u / %u</td><td>%s / %s</td><td>%ld</td>"
		"<td>%u</td><td>%u</td><td>%u</td>"
		"%s\n",
		C->id, C->slot, C->fd,
		Conn_domain(C), Conn_type(C), Conn_socktype(C), Conn_state(C),
		C->addr, C->port, C->via, polle, pollr, C->bi, C->bo,
		C->ibuf_size, C->obuf_size,
		speedi, speedo, Conn_now.tv_sec - C->start,
		C->band_width, C->band_factor, C->band_tokens,
		ext);

	return tmp;
}

/* flags: bit 1 = 1 - html */
char *Conn_status(const unsigned int flags)
{
	unsigned int len = 0, i, max;
	struct Conn *C;
	char tmp[512], tmp_len;
	char polle[16], pollr[16];
	char *buf, *slot, *ext = "";
	char speedi[32], speedo[32];
	unsigned long long bi, bo, dT;

	max = (Conn_no + 1) * 512 - 1;
	buf = malloc(max + 1);
	if (!buf)
		return strdup("No enough memory!");

	strcpy(buf, "");

	gettimeofday(&Conn_now, NULL);
	/* TODO: "len += " is incorrect */
	tmp_len = snprintf(tmp, sizeof(tmp), "Conn_pending=%d  Conn_no/Conn_max=%d/%d  Conn_total=%lu  Conn_uptime=%lus  Conn_allocated=%d\n",
		Conn_pending, Conn_no, Conn_max, Conn_total, Conn_now.tv_sec - Conn_start, Conn_allocated);
	if (len + tmp_len < max) {
		strcat(buf, tmp);
		len += tmp_len;
	}

	if (flags & 1)
		if (Conn_status_cb)
			ext = Conn_status_cb();

	if (flags & 1) {
		strcat(buf, "<table border=\"0\" cellspacing=\"1\" cellpadding=\"3\" bgcolor=\"#aaaaaa\">\n");
		strcat(buf, "<tr bgcolor=\"ffffff\">\n");
		strcat(buf, "<td>ID</td>");
		strcat(buf, "<td>Slot</td>");
		strcat(buf, "<td>FD</td>");
		strcat(buf, "<td>Dom</td>");
		strcat(buf, "<td>Type</td>");
		strcat(buf, "<td>SType</td>");
		strcat(buf, "<td>State</td>");
		strcat(buf, "<td>Addr/port</td>");
		strcat(buf, "<td>Via</td>");
		strcat(buf, "<td>Polle</td>");
		strcat(buf, "<td>Pollr</td>");
		strcat(buf, "<td>BI/BO</td>");
		strcat(buf, "<td>BUF I/O</td>");
		strcat(buf, "<td>Speed I/O</td>");
		strcat(buf, "<td>Elap (s)</td>");
		strcat(buf, "<td>Band</td>");
		strcat(buf, "<td>F</td>");
		strcat(buf, "<td>Tks</td>");
		strcat(buf, ext);
		strcat(buf, "</tr>\n");
	} else {
		strcat(buf, ext);
	}

	bi = 0; bo = 0; dT = 0;
	for (i = 0; i < Conn_no; i++) {
		C = &Conns[i];
		if (C->state == CONN_STATE_FREE)
			continue;

		if (C->type == Conn_type_CLIENT) {
			bi += C->bi;
			bo += C->bo;
			dT += Conn_now.tv_sec - C->start;
		}

		if (flags & 1)
			strcat(buf, "<tr bgcolor=\"ffffff\">\n");

		Conn_poll_status(C->events, polle);
		Conn_poll_status(C->revents, pollr);

		if ((flags & 1) == 0)
			slot = Conn_status_slot(C);
		else
			slot = Conn_status_slot_html(C);
		len += snprintf(tmp, sizeof(tmp), "%s", slot);
		if (len < max)
			strcat(buf, tmp);

		if (flags & 1)
			strcat(buf, "</tr>\n");
	}

	if (flags & 1)
		strcat(buf, "</table>\n");

	if (dT == 0)
		dT = 1;

	Conn_speed(speedi, sizeof(speedi), bi / dT);
	Conn_speed(speedo, sizeof(speedo), bo / dT);

	tmp_len = snprintf(tmp, sizeof(tmp), "Total speed I/O: %s / %s."
		" Total bytes I/O: %llu / %llu\n",
		speedi, speedo, bi, bo);
	if (len + tmp_len < max) {
		strcat(buf, tmp);
		len += tmp_len;
	}

	return buf;
}

/*
 * Returns the number of bytes in 'in' buffer
*/
unsigned int Conn_qlen(const struct Conn *C)
{
	return C->ibuf_tail - C->ibuf_head;
}

/*
 * Returns 1 if we can ignore this connection
 */
int Conn_ignore(const struct Conn *C)
{
	if (C->error_state > 0)
		return 1;

	return 0;
}

/*
 * Close a connection if it exceeded maximum idle time or got a timeout
 */
void Conn_expire(struct Conn *C)
{
	long long diff_ms;

	if ((C->trigger > 0)
		&& (C->last_trigger + C->trigger < Conn_now.tv_sec)) {
		C->last_trigger = Conn_now.tv_sec;
		if (C->cb_trigger)
			C->cb_trigger(C);
		else if (Conn_trigger_cb)
			Conn_trigger_cb(C);
	}

	if ((C->idle > 0) && (C->trecv.tv_sec + C->idle < Conn_now.tv_sec)) {
		C->error_state = CONN_ERROR_EXPIRED;
	} else if ((C->read_timeout > 0) && (C->tsend.tv_sec > 0)
		&& (C->tsend.tv_sec > C->trecv.tv_sec)) {
		diff_ms = Conn_time_diff(&Conn_now, &C->tsend);
		if (diff_ms > C->read_timeout) {
			C->error_state = CONN_ERROR_READ_TIMEOUT;
		}
	} else if ((C->conn_timeout > 0) && (C->state == CONN_STATE_CONNECT_b)) {
		diff_ms = Conn_time_diff(&Conn_now, &C->conn_syn);
		if (diff_ms > C->conn_timeout) {
			/* connection attempt expired */
			C->error_state = CONN_ERROR_CONN_TIMEOUT;
		}
	}
}

/*
 * Set NODELAY on socket
 */
int Conn_nodelay(const struct Conn *C)
{
	int i = 1;

	return setsockopt(C->fd, SOL_TCP, TCP_NODELAY, &i, sizeof(i));
}

void Conn_rollback(struct Conn *C, const unsigned int bytes)
{
	if (C->obuf_tail - C->obuf_head <= bytes)
		C->obuf_tail -= bytes;
}

/*
 * Returns a pointer to current in buffer
 */
char *Conn_ibuf(const struct Conn *C)
{
	return C->ibuf + C->ibuf_head;
}

/*
 * Returns a pointer to current out buffer
 */
char *Conn_obuf(const struct Conn *C)
{
	return C->obuf + C->obuf_head;
}

/*
 * Returns the id of a connection
 */
unsigned long long Conn_getid(const struct Conn *C)
{
	return C->id;
}

/*
 * Returns a Conn* searching by id
 */
struct Conn *Conn_get(const unsigned long long id)
{
	struct Conn *R = NULL;
	int i;

	for (i = Conn_no - 1; i >= 0; i--) {
		if (Conns[i].id == id) {
			R = &Conns[i];
			break;
		}
	}

	return R;
}

/*
 * Returns the fd associated with C
 */
int Conn_get_fd(const struct Conn *C)
{
	if (C == NULL)
		return -1;

	return C->fd;
}

/*
 * Returns the timeval of the last packet
 */
void Conn_last_time(const struct Conn *C, struct timeval *tv)
{
	*tv = C->trecv;
}

/*
 * Search for str in active buffer from a given offset
 * Returns pointer to string if match or NUll if doesn't.
 */
char *Conn_ostrstr(struct Conn *C, const unsigned int off, const char *str)
{
	unsigned int len, str_len, i;
	char *buf, *ret = NULL;

	len = C->ibuf_tail - C->ibuf_head - off;
	buf = C->ibuf + C->ibuf_head + off;
	str_len = strlen(str);

	if (len < str_len)
		return NULL;

	i = 0;
	while (i <= len - str_len) {
		if (strncmp(buf + i, str, str_len) == 0) {
			ret = buf + i;
			break;
		}

		i++;
	}

	return ret;
}

/*
 * Search for str in active buffer
 * Returns pointer to string if match or NUll if doesn't.
 */
char *Conn_strstr(struct Conn *C, const char *str)
{
	return Conn_ostrstr(C, 0, str);
}

/*
 * Set a callback
 */
int Conn_set_cb(struct Conn *C, const unsigned int type, void (*f)(struct Conn *))
{
	switch (type) {
		case CONN_CB_ACCEPT:	C->cb_accept = f; break;
		case CONN_CB_RECV:	C->cb_recv = f; break;
		case CONN_CB_SEND:	C->cb_send = f; break;
		case CONN_CB_DATA:	C->cb_data = f; break;
		case CONN_CB_CLOSE:	C->cb_close = f; break;
		case CONN_CB_TRIGGER:	C->cb_trigger = f; break;
		case CONN_CB_ERROR:	C->cb_error = f; break;
		case CONN_CB_CONNECTED:	C->cb_connected = f; break;
		case CONN_CB_ACCEPT_ERROR:	C->cb_accept_error = f; break;

		default:
			return -1;
	}

	return 0;
}

/*
 * Returns a '\0' terminated line, modifying received buffer
 */
char *Conn_get_line(struct Conn *C)
{
	char *cr;

	cr = Conn_strstr(C, "\n");
	if (!cr)
		return NULL;

	*cr = '\0';

	return Conn_ibuf(C);
}

/*
 * Helper help building text line daemons
 */
void Conn_for_every_line(struct Conn *C, int (*cb)(struct Conn *C, char *line))
{
	int ret = 0;
	char *line;
	unsigned int line_size;

	if (cb == NULL)
		return;

	while (1) {
		line = Conn_get_line(C);
		if (line == NULL)
			break;

		line_size = strlen(line) + 1;

		ret = cb(C, line);
		if (ret != 0)
			break;

		Conn_eat(C, line_size);
	}
}

/*
 * Eat @bytes from head of input buffer
 */
void Conn_eat(struct Conn *C, const unsigned int bytes)
{
	unsigned int slot;

	slot = C->slot;

	/* advance head */
	Conns[slot].ibuf_head += bytes;
	if (Conns[slot].ibuf_head >= Conns[slot].ibuf_tail) {
		Conns[slot].ibuf_head = 0;
		Conns[slot].ibuf_tail = 0;
	}

	Log(10, "Conn_eat(C, %u) head=%u tail=%u qlen=%u\n",
		bytes, C->ibuf_head, C->ibuf_tail,
		Conn_qlen(C));
}

/*
 * Eat all input buffer
 */
void Conn_eatall(struct Conn *C)
{
	Conn_eat(C, Conn_qlen(C));
}

/*
 * If put buffer is empty, just mark for closing.
 * If we have data, set the flag to do the closing after send.
 */
void Conn_close(struct Conn *C)
{
	Log(10, "%s: Mark slot=%u, id=%llu for closing...\n",
		__FUNCTION__, C->slot, C->id);

	if (C->obuf_head == C->obuf_tail)
		C->error_state = CONN_ERROR_USERREQ;
	else
		C->flags |= CONN_FLAGS_CLOSE_AFTER_SEND;
}

/*
 * Set some internal parameters
 */
void Conn_set(struct Conn *C, const unsigned int var, const int val)
{
	int fd;

	fd = Conn_get_fd(C);

	switch (var) {
	case CONN_PARA_AUTO_RECONNECT:
		C->flags |= (val == 0) ? 0 : CONN_FLAGS_AUTO_RECONNECT;
		break;
	case CONN_PARA_RECONNECT_DELAY:
		C->delay = val;
		break;
	case CONN_PARA_IDLE_TIME:
		C->idle = val;
		break;
	case CONN_PARA_READ_TIMEOUT:
		C->read_timeout = val;
		break;
	case CONN_PARA_CONN_TIMEOUT:
		C->conn_timeout = val;
		break;
	case CONN_PARA_TRIGGER:
		C->trigger = val;
		break;
	case CONN_PARA_IBUF:
		setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val));
		break;
	case CONN_PARA_OBUF:
		setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val));
		break;
	}
}

/*
 * Init a queue
 */
void Conn_queue_init(struct Conn_queue *q)
{
	q->head = q->tail = NULL;
}

/*
 * Add a slot in a queue
 */
int Conn_queue_add(struct Conn_queue *q, const unsigned int slot)
{
	struct Conn_queue_entry *p;

	p = (struct Conn_queue_entry *) calloc(1, sizeof(struct Conn_queue_entry));
	if (!p)
		return -1;

	p->slot = slot;
	p->next = NULL;

	if (q->head == NULL) {
		q->head = p;
	} else {
		q->tail->next = p;
	}

	q->tail = p;

	return 0;
}

/*
 * Destroys a queue
 */
void Conn_queue_destroy(struct Conn_queue *q)
{
	struct Conn_queue_entry *p, *next;

	p = q->head;
	while (p) {
		next = p->next;
		free(p);
		p = next;
	}

	q->head = q->tail = NULL;
}

/* Misc */
/*
 * Returns the address family for address stored in @addr.
 */
int Conn_addr_family(const char *addr)
{
	struct addrinfo hints, *results = NULL;
	int ret;

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = 0;
	hints.ai_flags = AI_NUMERICHOST;
	hints.ai_protocol = 0;
	hints.ai_canonname = NULL;
	hints.ai_addr = NULL;
	hints.ai_next = NULL;

	ret = getaddrinfo(addr, NULL, &hints, &results);
	if (ret != 0) {
		snprintf(Conn_error, sizeof(Conn_error),
			"getaddrinfo error on %s (%s)",
			addr, gai_strerror(ret));
		if (results)
			freeaddrinfo(results);
		return -1;
	}

	ret = results->ai_family;

	freeaddrinfo(results);

	return ret;
}

/* Splitting stuff */

/*
 * Cut in place the end \r and \n.
 */
static void cut(char *s)
{
	int pos;

	pos = strlen(s) - 1;

	while ((s[pos] == '\r') || (s[pos] == '\n')) {
		s[pos] = '\0';
		pos--;
	}
}

/*
 * Free a prealocated structure
 */
void Conn_split_free(struct Conn_split **s)
{
	struct Conn_split *p, *next;

	if (!s)
		return;

	p = *s;
	if (!p)
		return;

	while (p) {
		free(p->r);
		free(p->l);
		next = p->next;
		free(p);
		p = next;
	}

	*s = NULL;
}

/*
 * Split a buffer pointed by C to var,value pairs.
 */
struct Conn_split *Conn_split(char *line)
{
	char *p;
	struct Conn_split *ret = NULL, *q, *last;
	char l[128], r[4096];
	unsigned int i;
	char search_for;

	cut(line);

	/* do the spliting */
	p = line;
	while (*p != '\0') {
		/* skip empty space */
		while ((*p == ' ') || (*p == '\t'))
			p++;

		/* Building left */
		i = 0;
		while ((i < sizeof(l) - 1) && (*p != '\0') && (*p != '=')
			&& (*p != '\r')) {
			l[i++] = *p;
			p++;
		}
		l[i] = '\0';

		/* Building right */
		r[0] = '\0';
		if (*p != '\0') {
			/* skip '=' */
			p++;

			search_for = ' ';
			if (*p == '"') {
				search_for = '"';
				p++;
			}

			i = 0;
			while ((i < sizeof(r) - 1) && (*p != '\r') && (*p != '\0')
				&& (*p != search_for)) {
				r[i++] = *p;
				p++;
			}
			r[i] = '\0';

			if (*p == search_for)
				p++;
		}

		/* alloc data and fill it */
		q = (struct Conn_split *) calloc(1, sizeof(struct Conn_split));
		if (!q) {
			snprintf(Conn_error, sizeof(Conn_error),
				"cannot alloc memory!\n");
			goto out_free;
		}
		q->l = strdup(l);
		if (!q->l) {
			free(q);
			goto out_free;
		}
		q->r = strdup(r);
		if (!q->r) {
			free(q->l);
			free(q);
			goto out_free;
		}
		q->len = strlen(q->r);

		if (ret == NULL) {
			ret = q;
			last = ret;
		} else {
			last->next = q;
			last = q;
		}
	}

	return ret;

	out_free:
	Conn_split_free(&ret);

	return NULL;
}

/*
 * Search for a string and return the value
 */
char *Conn_split_get_size(const struct Conn_split *s, const char *l,
	unsigned int *size)
{
	while (s) {
		if (strcmp(l, s->l) == 0) {
			if (size != NULL)
				*size = s->len;
			return s->r;
		}
		s = s->next;
	}

	return NULL;
}

/*
 * Search for a string and return the value
 */
char *Conn_split_get(const struct Conn_split *s, const char *l)
{
	return Conn_split_get_size(s, l, NULL);
}

/*
 * Search for a string and return the value or "" if not found
 */
char *Conn_split_get_e(const struct Conn_split *s, const char *l,
	unsigned int *size)
{
	char *r;

	r = Conn_split_get_size(s, l, size);
	if (!r)
		r = "";

	return r;
}

/*
 * Return a value as unsigned long
 */
unsigned long Conn_split_get_ul(const struct Conn_split *s, const char *l,
	unsigned int base)
{
	char *r;
	unsigned long ret = 0;

	r = Conn_split_get(s, l);
	if (r)
		ret = strtoul(r, NULL, base);

	return ret;
}

/*
 * Return a value as double
 */
double Conn_split_get_d(const struct Conn_split *s, const char *l)
{
	char *r;
	double ret = 0;

	r = Conn_split_get(s, l);
	if (r)
		ret = strtod(r, NULL);

	return ret;
}


Mode Type Size Ref File
100644 blob 70 9964a59b5d89f394cc4250ed6d6ce67a5f0cd196 .gitignore
100644 blob 1945 fecf0e7a7e8580485101a179685aedc7e00affbb Changelog.pre109
100644 blob 25523 d85b0e9c52392d2114fd5a46fde6b95469ed4e68 Conn.c
100644 blob 820 77f3d32a7beb5f3d5e00daa043634309d144d016 Conn.h
100644 blob 726 64b1bad93a84f87c3e93fc24ac5341db691ea578 Conn.spec.in
100644 blob 66 68138d781ca754b15e14c687da91ee261b2c41f3 Conn_config.h.in
100644 blob 23351 6a1675c08b466c03c54121a053cf00168da08a31 Conn_engine_core.c
100644 blob 9151 948c1652b906eeede45fa9020029a84b5de92a1f Conn_engine_core.h
100644 blob 3601 bd8a8669bbfbafe3af4b1d3291041ed64577b73a Conn_engine_epoll.c
100644 blob 602 b648aaf1ad3c79ac5b6f425989aa5fee9a0d9f30 Conn_engine_epoll.h
100644 blob 2589 264b9b69fcb9dbda1c562316594208c753e4bacd Conn_engine_poll.c
100644 blob 589 f897f7d70dd5b17256cb51dce4637f0a8cf6291d Conn_engine_poll.h
100644 blob 30 d987fa5df957830331139935d517009e2911b0cf INSTALL
100644 blob 25275 92b8903ff3fea7f49ef5c041b67a087bca21c5ec LICENSE
100644 blob 1311 3c820df3b36b4bc844c52bc8c7e86f7843b67bb6 Makefile.in
100644 blob 192 5b11bdfb23857d8588845465aef993b320596b44 README
100644 blob 1679 74fec409297402fe691c8cef3f775994f7d312f2 TODO
100755 blob 23 d33bb6c4ecdce1390ce1db3c79ea3b93e22ea755 configure
040000 tree - d4c9c4a69c5cfa2a84316967185f1661b6817779 docs
100755 blob 10344 8acd6afdceefbb056b57e9d09a9943857800df8e duilder
100644 blob 276 f34bf151ee9b0b2e4731771041f3ee39ffe131db duilder.conf
040000 tree - 06c1a6f477d485f1d6d96b95466ba4338f426879 examples
040000 tree - 753d3d6e89c88cc27b99ef8babbf0f763a3287a7 tests
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/catalinux/Conn

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/catalinux/Conn

Clone this repository using git:
git clone git://git.rocketgit.com/user/catalinux/Conn

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main