diff options
| author | tv <tv@krebsco.de> | 2019-12-29 15:50:32 +0100 | 
|---|---|---|
| committer | tv <tv@krebsco.de> | 2019-12-29 15:50:32 +0100 | 
| commit | 8ec8e8a051c57230443e97279b274fcb620e9540 (patch) | |
| tree | 9ab650bba2292671ce36a9d1fac782bc6862ba18 | |
| parent | c6a8697800376a02b868cdea8fc1bf55f12798f1 (diff) | |
[WIP] prometheus module (with prometheus_vty)
| -rw-r--r-- | include/osmocom/vty/prometheus.h | 135 | ||||
| -rw-r--r-- | src/vty/Makefile.am | 3 | ||||
| -rw-r--r-- | src/vty/prometheus_vty.c | 679 | 
3 files changed, 816 insertions, 1 deletions
| diff --git a/include/osmocom/vty/prometheus.h b/include/osmocom/vty/prometheus.h new file mode 100644 index 00000000..2a09a127 --- /dev/null +++ b/include/osmocom/vty/prometheus.h @@ -0,0 +1,135 @@ +#include <osmocom/core/select.h> + + +/*! A telnet connection */ +struct prom_connection { +	/*! linked list header for internal management */ +	struct llist_head entry; +	/*! private data pointer passed through */ +	void *priv; +	/*! filedsecriptor (socket ) */ +	struct osmo_fd fd; +	/*! VTY instance associated with telnet connection */ +	struct prom_vty *vty; +	/*! logging target associated with this telnet connection */ +	struct log_target *dbg; +}; + + +int osmo_prom_init(void *ctx, int port); + +int osmo_prom_init_dynif(void *ctx, const char *host, int port); + +#define PROM_HTTP_BUFSIZ 512 + +//#define PROM_VTY_BUFSIZ 512 + +// TODO kill PROM_VTY_MAXHIST +#define PROM_VTY_MAXHIST 0 + +// +//enum event { +//	PROM_VTY_SERV, +//	PROM_VTY_READ, +//	PROM_VTY_WRITE, +//	PROM_VTY_CLOSED, +//	PROM_VTY_TIMEOUT_RESET, +//}; + +/*! Internal representation of a single VTY */ +struct prom_vty { +	/*! underlying file (if any) */ +	FILE *file; + +	/*! private data, specified by creator */ +	void *priv; + +	/*! File descripter of this vty. */ +	int fd; + +	/*! Is this vty connect to file or not */ +	//enum prom_vty_type type; + +	/*! Node status of this vty */ +	int node; + +	/*! Failure count */ +	int fail; + +	/*! Output buffer. */ +	struct buffer *obuf; + +	/*! Command input buffer */ +	char *buf; + +	/*! Command cursor point */ +	int cp; + +	/*! Command length */ +	int length; + +	/*! Command max length. */ +	int max; + +	/*! Histry of command */ +	char *hist[PROM_VTY_MAXHIST]; + +	/*! History lookup current point */ +	int hp; + +	/*! History insert end point */ +	int hindex; + +	/*! For current referencing point of interface, route-map, +	   access-list etc... */ +	void *index; + +	/*! For multiple level index treatment such as key chain and key. */ +	void *index_sub; + +	/*! For escape character. */ +	unsigned char escape; + +	/*! Current vty status. */ +	enum { PROM_VTY_NORMAL, PROM_VTY_CLOSE, PROM_VTY_MORE, PROM_VTY_MORELINE } status; + +	/*! IAC handling +	 * +	 * IAC handling: was the last character received the IAC +	 * (interpret-as-command) escape character (and therefore the next +	 * character will be the command code)?  Refer to Telnet RFC 854. */ +	unsigned char iac; + +	/*! IAC SB (option subnegotiation) handling */ +	unsigned char iac_sb_in_progress; +	/* At the moment, we care only about the NAWS (window size) negotiation, +	 * and that requires just a 5-character buffer (RFC 1073): +	 * <NAWS char> <16-bit width> <16-bit height> */ +#define TELNET_NAWS_SB_LEN 5 +	/*! sub-negotiation buffer */ +	unsigned char sb_buf[TELNET_NAWS_SB_LEN]; +	/*! How many subnegotiation characters have we received?   +	 * +	 * We just drop those that do not fit in the buffer. */ +	size_t sb_len; + +	/*! Window width */ +	int width; +	/*! Widnow height */ +	int height; + +	/*! Configure lines. */ +	int lines; + +	int monitor; + +	/*! In configure mode. */ +	int config; + +	/*! List of parent nodes, last item is the outermost parent. */ +	struct llist_head parent_nodes; + +	/*! When reading from a config file, these are the indenting characters expected for children of +	 * the current VTY node. */ +	char *indent; +}; diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index abed92ac..450549d0 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -12,7 +12,8 @@ lib_LTLIBRARIES = libosmovty.la  libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \  			telnet_interface.c logging_vty.c stats_vty.c \  			fsm_vty.c talloc_ctx_vty.c \ -			tdef_vty.c +			tdef_vty.c \ +			prometheus_vty.c  libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined  libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS)  endif diff --git a/src/vty/prometheus_vty.c b/src/vty/prometheus_vty.c new file mode 100644 index 00000000..89652444 --- /dev/null +++ b/src/vty/prometheus_vty.c @@ -0,0 +1,679 @@ +//#include <sys/socket.h> +//#include <netinet/in.h> +#include <errno.h> +//#include <stdlib.h> +//#include <stdio.h> +#include <stdarg.h> // TODO killme? +#include <string.h> +#include <unistd.h> +#include <termios.h> // TODO killme +// +//#include <osmocom/core/msgb.h> +//XXX[osmo_counter]#include <osmocom/core/linuxlist.h> +//XXX[osmo_counter]#include <osmocom/core/counter.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/stat_item.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/select.h> +// +#include <osmocom/vty/prometheus.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/command.h> + +#include <osmocom/core/rate_ctr.h> + + +/* per connection data */ +LLIST_HEAD(prom_active_connections); + +static void *prom_ctx; + +/* Vector which store each vty structure. */ +static vector prom_vtyvec; + +/* per network data */ +static int prom_new_connection(struct osmo_fd *fd, unsigned int what); + +static struct osmo_fd server_socket = { +	.when	    = OSMO_FD_READ, +	.cb	    = prom_new_connection, +	.priv_nr    = 0, +}; + +/*! Create new vty structure. */ +//struct prom_vty *vty_create (int vty_sock, void *priv) +// +/*! Create new vty structure. */ +// TODO extern struct host prom_host; + +/*! Return if this VTY is a shell or not */ +//int prom_vty_shell(struct prom_vty *vty) +//{ +//	return vty->type == VTY_SHELL ? 1 : 0; +//} + + +/*! close a telnet connection */ +int prom_telnet_close_client(struct osmo_fd *fd) +{ +	struct prom_connection *conn = (struct prom_connection*)fd->data; +	char sock_name_buf[OSMO_SOCK_NAME_MAXLEN]; +	int rc; + +	/* FIXME: getsockname() always fails: "Bad file descriptor" */ +	rc = osmo_sock_get_name_buf(sock_name_buf, OSMO_SOCK_NAME_MAXLEN, fd->fd); +	LOGP(DLGLOBAL, LOGL_INFO, "Closing telnet connection %s\n", +	     (rc <= 0) ? "r=NULL<->l=NULL" : sock_name_buf); + +	close(fd->fd); +	osmo_fd_unregister(fd); + +	if (conn->dbg) { +		log_del_target(conn->dbg); +		talloc_free(conn->dbg); +	} + +	llist_del(&conn->entry); +	talloc_free(conn); +	return 0; +} + + + +/*! callback from core VTY code about VTY related events */ +void prom_vty_event(enum event event, int sock, struct prom_vty *vty) +{ +	struct vty_signal_data sig_data; +	struct prom_connection *connection = vty->priv; +	struct osmo_fd *bfd; + +	//if (vty->type != VTY_TERM) +	//	return; + +	sig_data.event = event; +	sig_data.sock = sock; +	sig_data.vty = vty; +	osmo_signal_dispatch(SS_L_VTY, S_VTY_EVENT, &sig_data); + +	if (!connection) +		return; + +	bfd = &connection->fd; + +	switch (event) { +	case VTY_READ: +		bfd->when |= OSMO_FD_READ; +		break; +	case VTY_WRITE: +		bfd->when |= OSMO_FD_WRITE; +		break; +	case VTY_CLOSED: +		/* vty layer is about to free() vty */ +		prom_telnet_close_client(bfd); +		break; +	default: +		break; +	} +} + +int prom_vty_out_va(struct prom_vty *vty, const char *format, va_list ap) +{ +	int len = 0; +	int size = 1024; +	char buf[1024]; +	char *p = NULL; + +	// TODO don't use prom_vty_shell +	//if (prom_vty_shell(vty)) { +	//	vprintf(format, ap); +	//} else { +		va_list args; +		/* Try to write to initial buffer.  */ +		va_copy(args, ap); +		len = vsnprintf(buf, sizeof buf, format, args); +		va_end(args); + +		/* Initial buffer is not enough.  */ +		if (len < 0 || len >= size) { +			while (1) { +				if (len > -1) +					size = len + 1; +				else +					size = size * 2; + +				p = talloc_realloc_size(vty, p, size); +				if (!p) +					return -1; + +				va_copy(args, ap); +				len = vsnprintf(p, size, format, args); +				va_end(args); + +				if (len > -1 && len < size) +					break; +			} +		} + +		/* When initial buffer is enough to store all output.  */ +		if (!p) +			p = buf; + +		/* Pointer p must point out buffer. */ +		buffer_put(vty->obuf, (unsigned char *) p, len); + +		/* If p is not different with buf, it is allocated buffer.  */ +		if (p != buf) +			talloc_free(p); +	//} + +	prom_vty_event(VTY_WRITE, vty->fd, vty); + +	return len; +} + +/*! VTY standard output function + *  \param[in] vty VTY to which we should print + *  \param[in] format variable-length format string + */ +int prom_vty_out(struct prom_vty *vty, const char *format, ...) +{ +	va_list args; +	int rc; +	va_start(args, format); +	rc = prom_vty_out_va(vty, format, args); +	va_end(args); +	return rc; +} + + +/*! Initialize telnet based VTY interface listening to 127.0.0.1 + *  \param[in] tall_ctx \ref talloc context + *  \param[in] priv private data to be passed to callback + *  \param[in] port TCP port number to bind to + */ +//int telnet_init(void *tall_ctx, void *priv, int port) +//{ +//	return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port); +//} + +int osmo_prom_init(void *ctx, int port) +{ +	return osmo_prom_init_dynif(ctx, "127.0.0.1", port); +} + +/*! Initialize telnet based VTY interface + *  \param[in] tall_ctx \ref talloc context + *  \param[in] priv private data to be passed to callback + *  \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...) + *  \param[in] port TCP port number to bind to + */ +//int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) + +int osmo_prom_init_dynif(void *ctx, const char *host, int port) +{ +	int rc; + +	prom_ctx = talloc_named_const(ctx, 1, +			"prometheus_connection"); + +	rc = osmo_sock_init_ofd( +			&server_socket, +			AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, +			host, port, OSMO_SOCK_F_BIND +			); + +	server_socket.data = NULL; // könnte priv + +	if (rc < 0) { +		LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n", +		     host, port); +		return -1; +	} + +	prom_vtyvec = vector_init(VECTOR_MIN_SIZE); + +	LOGP(DLGLOBAL, LOGL_NOTICE, "Available via HTTP %s %d\n", host, port); +	return 0; +} + +/*! Unlock the configuration from a given VTY + *  \param[in] vty VTY from which the configuration shall be unlocked + *  \returns 0 in case of success + */ +int prom_vty_config_unlock(struct prom_vty *vty) +{ +	//if (vty_config == 1 && vty->config == 1) { +	//	vty->config = 0; +	//	vty_config = 0; +	//} +	return vty->config; +} + + +void prom_vty_close(struct prom_vty *vty) +{ +	int i; + +	/* VTY_CLOSED is handled by the telnet_interface */ +	prom_vty_event(VTY_CLOSED, vty->fd, vty); + +	if (vty->obuf)  { +		/* Flush buffer. */ +		buffer_flush_all(vty->obuf, vty->fd); + +		/* Free input buffer. */ +		buffer_free(vty->obuf); +		vty->obuf = NULL; +	} + +	/* Free command history. */ +	for (i = 0; i < PROM_VTY_MAXHIST; i++) +		if (vty->hist[i]) +			talloc_free(vty->hist[i]); + +	/* Unset vector. */ +	vector_unset(prom_vtyvec, vty->fd); + +	/* Close socket (ignore standard I/O streams). */ +	if (vty->fd > 2) { +		close(vty->fd); +		vty->fd = -1; +	} + +	if (vty->buf) { +		talloc_free(vty->buf); +		vty->buf = NULL; +	} + +	/* Check configure. */ +	prom_vty_config_unlock(vty); + +	/* OK free vty. */ +	talloc_free(vty); +} + +//XXX[osmo_counter]static int osmo_counter_handler(struct osmo_counter *counter, void *vty_) +//XXX[osmo_counter]{ +//XXX[osmo_counter]	struct vty *vty = vty_; +//XXX[osmo_counter]	const char *description = counter->description; +//XXX[osmo_counter] +//XXX[osmo_counter]	if (!counter->description) +//XXX[osmo_counter]		description = counter->name; +//XXX[osmo_counter] +//XXX[osmo_counter]	//vty_out(vty, " %s%s: %8lu%s", +//XXX[osmo_counter]	//	vctx->prefix, description, +//XXX[osmo_counter]	//	osmo_counter_get(counter), VTY_NEWLINE); +//XXX[osmo_counter] +//XXX[osmo_counter]	char buf[1024]; +//XXX[osmo_counter] +//XXX[osmo_counter]	// TODO only export current value and let graphing tools sum it up? +//XXX[osmo_counter] +//XXX[osmo_counter]	/* TODO ensure name contains only [a-zA-Z0-] */ +//XXX[osmo_counter]	const char *name = description; +//XXX[osmo_counter] +//XXX[osmo_counter]	snprintf(buf, sizeof buf, +//XXX[osmo_counter]		"%s %d\n", +//XXX[osmo_counter]		name, +//XXX[osmo_counter]		osmo_counter_get(counter) +//XXX[osmo_counter]	); +//XXX[osmo_counter] +//XXX[osmo_counter]	prom_vty_out(vty, "%X\r\n", strlen(buf)); +//XXX[osmo_counter]	prom_vty_out(vty, "%s\r\n", buf); +//XXX[osmo_counter] +//XXX[osmo_counter]	return 0; +//XXX[osmo_counter]} + + +void sanitize_name(char *name) { +	for (char c; (c = *name) != '\0'; name++) { +		if ('a' <= c && c <= 'z') continue; +		if ('A' <= c && c <= 'Z') continue; +		if ('0' <= c && c <= '9') continue; +		if (c == ':' || c == '_') continue; +		*name = '_'; +	} +} + + +static int rate_ctr_handler( +	struct rate_ctr_group *ctrg, struct rate_ctr *ctr, +	const struct rate_ctr_desc *desc, void *vty_) +{ +	struct vty *vty = vty_; + +	char buf[1024]; +	char name[1024]; + +	// TODO only export current value and let graphing tools sum it up? + +	strcpy(name, desc->name); +	sanitize_name(name); + +	char *group = ctrg->desc->group_name_prefix; +	//char intv_sec_name[1024]; +	//char intv_min_name[1024]; +	//char intv_hour_name[1024]; +	//char intv_day_name[1024]; + +	//strcpy(intv_sec_name, name); +	//strcpy(intv_min_name, name); +	//strcpy(intv_hour_name, name); +	//strcpy(intv_day_name, name); + +	//strcat(intv_sec_name, "_intv_sec"); +	//strcat(intv_min_name, "_intv_min"); +	//strcat(intv_hour_name, "_intv_hour"); +	//strcat(intv_day_name, "_intv_day"); + +	snprintf(buf, sizeof buf, +		//"%s %d\n" +		//"%s %d\n" +		//"%s %d\n" +		//"%s %d\n" +		"%s{group=\"%s\"} %d\n", +		//intv_sec_name, ctr->intv[RATE_CTR_INTV_SEC].rate, +		//intv_min_name, ctr->intv[RATE_CTR_INTV_MIN].rate, +		//intv_hour_name, ctr->intv[RATE_CTR_INTV_HOUR].rate, +		//intv_day_name, ctr->intv[RATE_CTR_INTV_DAY].rate +		name, group, ctr->current +	); + +	prom_vty_out(vty, "%X\r\n", strlen(buf)); +	prom_vty_out(vty, "%s\r\n", buf); + +	return 0; +} + +static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vty_) +{ +	struct vty *vty = vty_; + +	//TODO +//	if (ctrg->idx) +//		prom_vty_out(vty, "%s (%d):\r\n", +//			ctrg->desc->group_description, ctrg->idx); +//	else +//		prom_vty_out(vty, "%s:\r\n", +//			ctrg->desc->group_description); + +	rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vty); + +	return 0; +} + +static int osmo_stat_item_handler( +	struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *vty_) +{ +	struct vty *vty = vty_; +	const char *unit = +		item->desc->unit != OSMO_STAT_ITEM_NO_UNIT ? +		item->desc->unit : ""; + +	char buf[1024]; +	char name[1024]; + +	//vty_out(vty, " %s%s: %8" PRIi32 " %s%s", +	//	vctx->prefix, item->desc->description, +	//	osmo_stat_item_get_last(item), +	//	unit, VTY_NEWLINE); + +	strcpy(name, item->desc->name); +	sanitize_name(name); + +	char *group = statg->desc->group_name_prefix; +	int value = osmo_stat_item_get_last(item); +	// TODO unit +	snprintf(buf, sizeof buf, "%s{group=\"%s\",unit=\"%s\"} %d\n", name, group, unit, value); + +	prom_vty_out(vty, "%X\r\n", strlen(buf)); +	prom_vty_out(vty, "%s\r\n", buf); + +	return 0; +} + +static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vty_) +{ +	struct vty *vty = vty_; + +	//if (statg->idx) +	//	vty_out(vty, "%s%s (%d):%s", vctx->prefix, +	//		statg->desc->group_description, statg->idx, +	//		VTY_NEWLINE); +	//else +	//	vty_out(vty, "%s%s:%s", vctx->prefix, +	//		statg->desc->group_description, VTY_NEWLINE); + +	osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vty); + +	return 0; +} + +/*! Read data via vty socket. */ +int prom_vty_read(struct prom_vty *vty) +{ +	//int i; +	int nbytes; +	unsigned char buf[VTY_READ_BUFSIZ]; + +	int vty_sock = vty->fd; + +	/* Read raw data from socket */ +	if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { +		if (nbytes < 0) { +			if (ERRNO_IO_RETRY(errno)) { +				prom_vty_event(VTY_READ, vty_sock, vty); +				return 0; +			} +		} +		buffer_reset(vty->obuf); +		vty->status = PROM_VTY_CLOSE; +	} + +	//LOGP(DLGLOBAL, LOGL_NOTICE, "read %d byte(s)\n", nbytes); +	prom_vty_out(vty, +	    "HTTP/1.1 200 OK\r\n" +	    "Content-Type: text/plain\r\n" +	    "Transfer-Encoding: chunked\r\n" +	    // "Content-Length: 6\r\n" +	    "\r\n" +	); +	//vty->status = PROM_VTY_CLOSE; + +	//XXX[osmo_counter]osmo_counters_for_each(osmo_counter_handler, vty); +	rate_ctr_for_each_group(rate_ctr_group_handler, vty); +	osmo_stat_item_for_each_group(osmo_stat_item_group_handler, vty); + +	/* finish chunked transfer encoding */ +	prom_vty_out(vty, "0\r\n\r\n"); + +	// TODO parse + +	/* Check status. */ +	if (vty->status == PROM_VTY_CLOSE) { +		prom_vty_close(vty); +		return -EBADF; +	} else { +		prom_vty_event(VTY_WRITE, vty_sock, vty); +		prom_vty_event(VTY_READ, vty_sock, vty); +	} + +	return 0; +} + + +static int client_data(struct osmo_fd *fd, unsigned int what) +{ +	struct prom_connection *conn = fd->data; +	int rc = 0; + +	if (what & OSMO_FD_READ) { +		conn->fd.when &= ~OSMO_FD_READ; +		// TODO feed server +		rc = prom_vty_read(conn->vty); +	} + +	/* vty might have been closed from vithin prom_vty_read() */ +	if (rc == -EBADF) +		return rc; + +	if (what & OSMO_FD_WRITE) { +		rc = buffer_flush_all(conn->vty->obuf, fd->fd); +		if (rc == BUFFER_EMPTY) +			conn->fd.when &= ~OSMO_FD_WRITE; +	} + +	return rc; +} + + +/*! Allocate a new vty interface structure */ +struct prom_vty *prom_vty_new(void) +{ +	struct prom_vty *new = talloc_zero(tall_vty_ctx, struct vty); + +	if (!new) +		goto out; + +	INIT_LLIST_HEAD(&new->parent_nodes); + +	new->obuf = buffer_new(new, 0);	/* Use default buffer size. */ +	if (!new->obuf) +		goto out_new; +	new->buf = _talloc_zero(new, PROM_HTTP_BUFSIZ, "prom_vty_new->buf"); +	if (!new->buf) +		goto out_obuf; + +	new->max = PROM_HTTP_BUFSIZ; +	new->fd = -1; + +	return new; + +out_obuf: +	buffer_free(new->obuf); +out_new: +	talloc_free(new); +	new = NULL; +out: +	return new; +} + +struct vty * +prom_vty_create (int vty_sock, void *priv) +{ +  struct prom_vty *vty; + +	struct termios t = {}; + +	tcgetattr(vty_sock, &t); +	cfmakeraw(&t); +	tcsetattr(vty_sock, TCSANOW, &t); + +  /* Allocate new vty structure and set up default values. */ +  vty = prom_vty_new (); +  vty->fd = vty_sock; +  vty->priv = priv; +  //vty->type = VTY_TERM; // TODO kill vty->type +  //if (!password_check) +  //  { +  //    if (prom_host.advanced) +  //      vty->node = ENABLE_NODE; +  //    else +  //      vty->node = VIEW_NODE; +  //  } +  //else +  //  vty->node = AUTH_NODE; +  vty->fail = 0; +  vty->cp = 0; +  // TODO vty_clear_buf (vty); +  vty->length = 0; +  memset (vty->hist, 0, sizeof (vty->hist)); +  vty->hp = 0; +  vty->hindex = 0; +  vector_set_index (prom_vtyvec, vty_sock, vty); +  vty->status = PROM_VTY_NORMAL; +  //if (prom_host.lines >= 0) +  //  vty->lines = prom_host.lines; +  //else +  //  vty->lines = -1; + +  //if (password_check) +  //  { +  //    /* Vty is not available if password isn't set. */ +  //    if (prom_host.password == NULL && prom_host.password_encrypt == NULL) +  //      { +  //        prom_vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); +  //        vty->status = PROM_VTY_CLOSE; +  //        prom_vty_close (vty); +  //        return NULL; +  //      } +  //  } + +  /* Say hello to the world. */ +  //prom_vty_hello (vty); +  //if (password_check) +  //  prom_vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + +  /* Setting up terminal. */ +  // TODO vty_will_echo (vty); +  //TODO vty_will_suppress_go_ahead (vty); + +  // TODO vty_dont_linemode (vty); +  // TODO vty_do_window_size (vty); +  /* vty_dont_lflow_ahead (vty); */ + +  // TODO vty_prompt (vty); + +  /* Add read/write thread. */ +  prom_vty_event (VTY_WRITE, vty_sock, vty); +  prom_vty_event (VTY_READ, vty_sock, vty); + +  return vty; +} + + +static int prom_new_connection(struct osmo_fd *fd, unsigned int what) +{ +	//LOGP(DLGLOBAL, LOGL_ERROR, "derp\n"); +	struct prom_connection *connection; +	struct sockaddr_in sockaddr; +	socklen_t len = sizeof(sockaddr); +	int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); +	char sock_name_buf[OSMO_SOCK_NAME_MAXLEN]; +	int rc; + +	if (new_connection < 0) { +		LOGP(DLGLOBAL, LOGL_ERROR, "HTTP accept failed\n"); +		return new_connection; +	} + +	rc = osmo_sock_get_name_buf(sock_name_buf, OSMO_SOCK_NAME_MAXLEN, new_connection); +	LOGP(DLGLOBAL, LOGL_INFO, "Accept()ed new HTTP connection %s\n", +	     (rc <= 0) ? "r=NULL<->l=NULL" : sock_name_buf); + +	connection = talloc_zero(prom_ctx, struct prom_connection); +	connection->priv = fd->data; +	connection->fd.data = connection; +	connection->fd.fd = new_connection; +	connection->fd.when = OSMO_FD_READ; +	connection->fd.cb = client_data; +	rc = osmo_fd_register(&connection->fd); +	if (rc < 0) { +		talloc_free(connection); +		return rc; +	} +	llist_add_tail(&connection->entry, &prom_active_connections); + +	// TODO create server +	connection->vty = prom_vty_create(new_connection, connection); +	if (!connection->vty) { +		LOGP(DLGLOBAL, LOGL_ERROR, "couldn't create VTY\n"); +		/* prom_vty_create() is already closing the fd if it returns NULL */ +		talloc_free(connection); +		return -1; +	} + +	return 0; +} + | 
