Merge branch 'master' into on-waves/mgcp
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 6a87782..259e6d6 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -5,7 +5,8 @@
 		 ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
 		 bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \
 		 silent_call.h mgcp.h meas_rep.h rest_octets.h \
-		 system_information.h handover.h mgcp_internal.h
+		 system_information.h handover.h mgcp_internal.h \
+		 vty.h
 
 openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
 openbscdir = $(includedir)/openbsc
diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h
new file mode 100644
index 0000000..40e2191
--- /dev/null
+++ b/openbsc/include/openbsc/vty.h
@@ -0,0 +1,6 @@
+#ifndef OPENBSC_VTY_H
+#define OPENBSC_VTY_H
+
+void openbsc_vty_add_cmds(void);
+
+#endif
diff --git a/openbsc/include/sccp/sccp.h b/openbsc/include/sccp/sccp.h
index 643479a..604a2ac 100644
--- a/openbsc/include/sccp/sccp.h
+++ b/openbsc/include/sccp/sccp.h
@@ -94,7 +94,7 @@
  *   call sccp_system_incoming for incoming data (from the network)
  *   sccp will call outgoing whenever outgoing data exists
  */
-int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *context);
+int sccp_system_init(void (*outgoing)(struct msgb *data, void *ctx), void *context);
 int sccp_system_incoming(struct msgb *data);
 
 /**
@@ -106,6 +106,11 @@
 int sccp_connection_free(struct sccp_connection *connection);
 
 /**
+ * internal.. 
+ */
+int sccp_connection_force_free(struct sccp_connection *conn);
+
+/**
  * Create a new socket. Set your callbacks and then call bind to open
  * the connection.
  */
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index cbc809d..2c1d37a 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -17,7 +17,7 @@
 		input/misdn.c input/ipaccess.c \
 		talloc_ctx.c system_information.c rest_octets.c \
 		rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \
-		bts_unknown.c bsc_version.c bsc_api.c
+		bts_unknown.c bsc_version.c bsc_api.c vty_interface_cmds.c
 
 libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
 		mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
@@ -42,7 +42,7 @@
 isdnsync_SOURCES = isdnsync.c
 
 bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \
-		   debug.c telnet_interface.c
+		   debug.c telnet_interface.c vty_interface_cmds.c
 bsc_mgcp_LDADD = libvty.a
 
 ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 312d0f2..8bf47ab 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -288,6 +288,8 @@
 	return subscr;
 }
 
+static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size);
+
 static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
 {
 	dbi_result result;
@@ -316,9 +318,10 @@
 		strncpy(equip->imei, string, sizeof(equip->imei));
 
 	string = dbi_result_get_string(result, "classmark1");
-	if (string)
-		 cm1 = atoi(string) & 0xff;
-	equip->classmark1 = *((struct gsm48_classmark1 *) &cm1);
+	if (string) {
+		cm1 = atoi(string) & 0xff;
+		memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1));
+	}
 
 	equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
 	cm2 = dbi_result_get_binary(result, "classmark2");
diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c
index 56930d4..83b01f2 100644
--- a/openbsc/src/input/misdn.c
+++ b/openbsc/src/input/misdn.c
@@ -262,7 +262,7 @@
 
 	ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
 	if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
-		DEBUGP(DMIB, "send returns %d instead of %lu\n", ret,
+		DEBUGP(DMIB, "send returns %d instead of %zu\n", ret,
 			sizeof(*hh) + BCHAN_TX_GRAN);
 
 	return ret;
diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c
index 73ce2df..3860813 100644
--- a/openbsc/src/ipaccess/ipaccess-proxy.c
+++ b/openbsc/src/ipaccess/ipaccess-proxy.c
@@ -550,6 +550,7 @@
 		}
 
 		/* lookup BTS, create sign_link, ... */
+		site_id = bts_id = trx_id = 0;
 		parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
 			     &site_id, &bts_id, &trx_id);
 		ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
diff --git a/openbsc/src/mgcp/mgcp_main.c b/openbsc/src/mgcp/mgcp_main.c
index 5e337e9..80b7b54 100644
--- a/openbsc/src/mgcp/mgcp_main.c
+++ b/openbsc/src/mgcp/mgcp_main.c
@@ -40,6 +40,7 @@
 #include <openbsc/mgcp.h>
 #include <openbsc/mgcp_internal.h>
 #include <openbsc/telnet_interface.h>
+#include <openbsc/vty.h>
 
 #include <vty/command.h>
 
@@ -255,6 +256,7 @@
 	cmd_init(1);
 	vty_init();
 
+	openbsc_vty_add_cmds();
         mgcp_vty_init();
 	return 0;
 }
diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c
index b1da2c7..e0fd02e 100644
--- a/openbsc/src/sccp/sccp.c
+++ b/openbsc/src/sccp/sccp.c
@@ -45,7 +45,7 @@
 
 struct sccp_system {
 	/* layer3 -> layer2 */
-	int (*write_data)(struct msgb *data, void *context);
+	void (*write_data)(struct msgb *data, void *context);
 	void *write_context;
 };
 
@@ -91,9 +91,9 @@
 }
 
 
-static int _send_msg(struct msgb *msg)
+static void _send_msg(struct msgb *msg)
 {
-	return sccp_system.write_data(msg, sccp_system.write_context);
+	sccp_system.write_data(msg, sccp_system.write_context);
 }
 
 /*
@@ -499,7 +499,6 @@
 {
 	struct sccp_data_unitdata *udt;
 	u_int8_t *data;
-	int ret;
 
 	if (msgb_l3len(payload) > 256) {
 		DEBUGP(DSCCP, "The payload is too big for one udt\n");
@@ -533,10 +532,8 @@
 	data[0] = msgb_l3len(payload);
 	memcpy(&data[1], payload->l3h, msgb_l3len(payload));
 
-	ret = _send_msg(msg);
-	msgb_free(msg);
-
-	return ret;
+	_send_msg(msg);
+	return 0;
 }
 
 static int _sccp_handle_read(struct msgb *msgb)
@@ -627,7 +624,6 @@
 	struct msgb *msgb;
 	struct sccp_connection_refused *ref;
 	u_int8_t *data;
-	int ret;
 
 	msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
 				   SCCP_MSG_HEADROOM, "sccp ref");
@@ -643,9 +639,8 @@
 	data = msgb_put(msgb, 1);
 	data[0] = SCCP_PNC_END_OF_OPTIONAL;
 
-	ret = _send_msg(msgb);
-	msgb_free(msgb);
-	return ret;
+	_send_msg(msgb);
+	return 0;
 }
 
 static int _sccp_send_connection_confirm(struct sccp_connection *connection)
@@ -653,7 +648,6 @@
 	struct msgb *response;
 	struct sccp_connection_confirm *confirm;
 	u_int8_t *optional_data;
-	int ret;
 
 	if (assign_source_local_reference(connection) != 0)
 		return -1;
@@ -677,11 +671,9 @@
 	optional_data = (u_int8_t *) msgb_put(response, 1);
 	optional_data[0] = SCCP_PNC_END_OF_OPTIONAL;
 
-	ret = _send_msg(response);
-	msgb_free(response);
-
+	_send_msg(response);
 	_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED);
-	return ret;
+	return 0;
 }
 
 static int _sccp_send_connection_request(struct sccp_connection *connection,
@@ -691,7 +683,6 @@
 	struct sccp_connection_request *req;
 	u_int8_t *data;
 	u_int8_t extra_size = 3 + 1;
-	int ret;
 
 
 	if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) {
@@ -741,10 +732,8 @@
 	llist_add_tail(&connection->list, &sccp_connections);
 	_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REQUEST);
 
-	ret = _send_msg(request);
-	msgb_free(request);
-
-	return ret;
+	_send_msg(request);
+	return 0;
 }
 
 static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
@@ -753,7 +742,6 @@
 	struct sccp_data_form1 *dt1;
 	u_int8_t *data;
 	int extra_size;
-	int ret;
 
 	if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
 		DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n");
@@ -777,17 +765,14 @@
 	data[0] = extra_size - 1;
 	memcpy(&data[1], _data->l3h, extra_size - 1);
 
-	ret = _send_msg(msgb);
-	msgb_free(msgb);
-
-	return ret;
+	_send_msg(msgb);
+	return 0;
 }
 
 static int _sccp_send_connection_it(struct sccp_connection *conn)
 {
 	struct msgb *msgb;
 	struct sccp_data_it *it;
-	int ret;
 
 	msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
 				   SCCP_MSG_HEADROOM, "sccp it");
@@ -803,9 +788,8 @@
 	it->sequencing[0] = it->sequencing[1] = 0;
 	it->credit = 0;
 
-	ret = _send_msg(msgb);
-	msgb_free(msgb);
-	return ret;
+	_send_msg(msgb);
+	return 0;
 }
 
 static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
@@ -813,7 +797,6 @@
 	struct msgb *msg;
 	struct sccp_connection_released *rel;
 	u_int8_t *data;
-	int ret;
 
 	msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM,
 				  "sccp: connection released");
@@ -832,10 +815,8 @@
 	data[0] = SCCP_PNC_END_OF_OPTIONAL;
 
 	_sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE);
-	ret = _send_msg(msg);
-	msgb_free(msg);
-
-	return ret;
+	_send_msg(msg);
+	return 0;
 }
 
 /*
@@ -982,7 +963,6 @@
 {
 	struct msgb *msgb;
 	struct sccp_connection_release_complete *rlc;
-	int ret;
 
 	msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
 				   SCCP_MSG_HEADROOM, "sccp rlc");
@@ -995,8 +975,7 @@
 	memcpy(&rlc->source_local_reference,
 	       &connection->source_local_reference, sizeof(struct sccp_source_reference));
 
-	ret = _send_msg(msgb);
-	msgb_free(msgb);
+	_send_msg(msgb);
 
 	/*
 	 * Remove from the list of active connections and set the state. User code
@@ -1004,8 +983,7 @@
 	 */
 	llist_del(&connection->list);
 	_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
-
-	return ret;
+	return 0;
 }
 
 /* connection released, send a released confirm */
@@ -1118,7 +1096,7 @@
 }
 
 
-int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *ctx)
+int sccp_system_init(void (*outgoing)(struct msgb *data, void *ctx), void *ctx)
 {
 	sccp_system.write_data = outgoing;
 	sccp_system.write_context = ctx;
@@ -1220,6 +1198,17 @@
 	return 0;
 }
 
+int sccp_connection_force_free(struct sccp_connection *con)
+{
+	if (con->connection_state > SCCP_CONNECTION_STATE_NONE &&
+	    con->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE)
+		llist_del(&con->list);
+
+	con->connection_state = SCCP_CONNECTION_STATE_REFUSED;
+	sccp_connection_free(con);
+        return 0;
+}
+
 struct sccp_connection *sccp_connection_socket(void)
 {
 	return talloc_zero(tall_sccp_ctx, struct sccp_connection);
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index f2ac12d..b5bdbc8 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -38,6 +38,7 @@
 #include <openbsc/db.h>
 #include <osmocore/talloc.h>
 #include <openbsc/telnet_interface.h>
+#include <openbsc/vty.h>
 
 static struct gsm_network *gsmnet;
 
@@ -615,7 +616,7 @@
 		vty_out(vty, "  No Subscriber%s", VTY_NEWLINE);
 	if (is_ipaccess_bts(lchan->ts->trx->bts)) {
 		struct in_addr ia;
-		ia.s_addr = lchan->abis_ip.bound_ip;
+		ia.s_addr = htonl(lchan->abis_ip.bound_ip);
 		vty_out(vty, "  Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
 			inet_ntoa(ia), lchan->abis_ip.bound_port,
 			lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id,
@@ -878,204 +879,6 @@
 	return CMD_SUCCESS;
 }
 
-static void _vty_output(struct log_target *tgt, const char *line)
-{
-	struct vty *vty = tgt->tgt_vty.vty;
-	vty_out(vty, "%s", line);
-	/* This is an ugly hack, but there is no easy way... */
-	if (strchr(line, '\n'))
-		vty_out(vty, "\r");
-}
-
-struct log_target *log_target_create_vty(struct vty *vty)
-{
-	struct log_target *target;
-
-	target = log_target_create();
-	if (!target)
-		return NULL;
-
-	target->tgt_vty.vty = vty;
-	target->output = _vty_output;
-	return target;
-}
-
-DEFUN(enable_logging,
-      enable_logging_cmd,
-      "logging enable",
-      "Enables logging to this vty\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (conn->dbg) {
-		vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	conn->dbg = log_target_create_vty(vty);
-	if (!conn->dbg)
-		return CMD_WARNING;
-
-	log_add_target(conn->dbg);
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_fltr_imsi,
-      logging_fltr_imsi_cmd,
-      "logging filter imsi IMSI",
-      "Print all messages related to a IMSI\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	log_set_imsi_filter(conn->dbg, argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_fltr_all,
-      logging_fltr_all_cmd,
-      "logging filter all <0-1>",
-      "Print all messages to the console\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	log_set_all_filter(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_use_clr,
-      logging_use_clr_cmd,
-      "logging color <0-1>",
-      "Use color for printing messages\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	log_set_use_color(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_prnt_timestamp,
-      logging_prnt_timestamp_cmd,
-      "logging timestamp <0-1>",
-      "Print the timestamp of each message\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	log_set_print_timestamp(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-/* FIXME: those have to be kept in sync with the log levels and categories */
-#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)"
-#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
-DEFUN(logging_level,
-      logging_level_cmd,
-      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
-      "Set the log level for a specified category\n")
-{
-	struct telnet_connection *conn;
-	int category = log_parse_category(argv[0]);
-	int level = log_parse_level(argv[1]);
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	if (category < 0) {
-		vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	if (level < 0) {
-		vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	conn->dbg->categories[category].enabled = 1;
-	conn->dbg->categories[category].loglevel = level;
-
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_set_category_mask,
-      logging_set_category_mask_cmd,
-      "logging set log mask MASK",
-      "Decide which categories to output.\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	log_parse_category_mask(conn->dbg, argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_set_log_level,
-      logging_set_log_level_cmd,
-      "logging set log level <0-8>",
-      "Set the global log level. The value 0 implies no filtering.\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	log_set_log_level(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-DEFUN(diable_logging,
-      disable_logging_cmd,
-      "logging disable",
-      "Disables logging to this vty\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	log_del_target(conn->dbg);
-	talloc_free(conn->dbg);
-	conn->dbg = NULL;
-	return CMD_SUCCESS;
-}
-
 DEFUN(show_stats,
       show_stats_cmd,
       "show statistics",
@@ -1957,15 +1760,7 @@
 	install_element(VIEW_NODE, &show_paging_cmd);
 	install_element(VIEW_NODE, &show_stats_cmd);
 
-	install_element(VIEW_NODE, &enable_logging_cmd);
-	install_element(VIEW_NODE, &disable_logging_cmd);
-	install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
-	install_element(VIEW_NODE, &logging_fltr_all_cmd);
-	install_element(VIEW_NODE, &logging_use_clr_cmd);
-	install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
-	install_element(VIEW_NODE, &logging_set_category_mask_cmd);
-	install_element(VIEW_NODE, &logging_level_cmd);
-	install_element(VIEW_NODE, &logging_set_log_level_cmd);
+	openbsc_vty_add_cmds();
 
 	install_element(CONFIG_NODE, &cfg_net_cmd);
 	install_node(&net_node, config_write_net);
diff --git a/openbsc/src/vty_interface_cmds.c b/openbsc/src/vty_interface_cmds.c
new file mode 100644
index 0000000..d494584
--- /dev/null
+++ b/openbsc/src/vty_interface_cmds.c
@@ -0,0 +1,243 @@
+/* OpenBSC logging helper for the VTY */
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2010 by Holger Hans Peter Freyther
+ * All Rights Reserved
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/vty.h>
+#include <openbsc/telnet_interface.h>
+
+#include <osmocore/talloc.h>
+
+#include <vty/command.h>
+#include <vty/buffer.h>
+#include <vty/vty.h>
+
+#include <stdlib.h>
+
+static void _vty_output(struct log_target *tgt, const char *line)
+{
+	struct vty *vty = tgt->tgt_vty.vty;
+	vty_out(vty, "%s", line);
+	/* This is an ugly hack, but there is no easy way... */
+	if (strchr(line, '\n'))
+		vty_out(vty, "\r");
+}
+
+struct log_target *log_target_create_vty(struct vty *vty)
+{
+	struct log_target *target;
+
+	target = log_target_create();
+	if (!target)
+		return NULL;
+
+	target->tgt_vty.vty = vty;
+	target->output = _vty_output;
+	return target;
+}
+
+DEFUN(enable_logging,
+      enable_logging_cmd,
+      "logging enable",
+      "Enables logging to this vty\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (conn->dbg) {
+		vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	conn->dbg = log_target_create_vty(vty);
+	if (!conn->dbg)
+		return CMD_WARNING;
+
+	log_add_target(conn->dbg);
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_imsi,
+      logging_fltr_imsi_cmd,
+      "logging filter imsi IMSI",
+      "Print all messages related to a IMSI\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_imsi_filter(conn->dbg, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_all,
+      logging_fltr_all_cmd,
+      "logging filter all <0-1>",
+      "Print all messages to the console\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_all_filter(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_use_clr,
+      logging_use_clr_cmd,
+      "logging color <0-1>",
+      "Use color for printing messages\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_use_color(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_prnt_timestamp,
+      logging_prnt_timestamp_cmd,
+      "logging timestamp <0-1>",
+      "Print the timestamp of each message\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_print_timestamp(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+/* FIXME: those have to be kept in sync with the log levels and categories */
+#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)"
+#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
+DEFUN(logging_level,
+      logging_level_cmd,
+      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
+      "Set the log level for a specified category\n")
+{
+	struct telnet_connection *conn;
+	int category = log_parse_category(argv[0]);
+	int level = log_parse_level(argv[1]);
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (category < 0) {
+		vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (level < 0) {
+		vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	conn->dbg->categories[category].enabled = 1;
+	conn->dbg->categories[category].loglevel = level;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_category_mask,
+      logging_set_category_mask_cmd,
+      "logging set log mask MASK",
+      "Decide which categories to output.\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_parse_category_mask(conn->dbg, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_log_level,
+      logging_set_log_level_cmd,
+      "logging set log level <0-8>",
+      "Set the global log level. The value 0 implies no filtering.\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_log_level(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(diable_logging,
+      disable_logging_cmd,
+      "logging disable",
+      "Disables logging to this vty\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_del_target(conn->dbg);
+	talloc_free(conn->dbg);
+	conn->dbg = NULL;
+	return CMD_SUCCESS;
+}
+
+void openbsc_vty_add_cmds()
+{
+	install_element(VIEW_NODE, &enable_logging_cmd);
+	install_element(VIEW_NODE, &disable_logging_cmd);
+	install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
+	install_element(VIEW_NODE, &logging_fltr_all_cmd);
+	install_element(VIEW_NODE, &logging_use_clr_cmd);
+	install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
+	install_element(VIEW_NODE, &logging_set_category_mask_cmd);
+	install_element(VIEW_NODE, &logging_level_cmd);
+	install_element(VIEW_NODE, &logging_set_log_level_cmd);
+
+}
diff --git a/openbsc/tests/debug/debug_test.c b/openbsc/tests/debug/debug_test.c
index 695d65c..f3e4837 100644
--- a/openbsc/tests/debug/debug_test.c
+++ b/openbsc/tests/debug/debug_test.c
@@ -38,4 +38,6 @@
 	DEBUGP(DRLL, "You should see this\n");
 	DEBUGP(DCC, "You should see this\n");
 	DEBUGP(DMM, "You should not see this\n");
+
+	return 0;
 }
diff --git a/openbsc/tests/sccp/sccp_test.c b/openbsc/tests/sccp/sccp_test.c
index eb41d3e..0c2adc8 100644
--- a/openbsc/tests/sccp/sccp_test.c
+++ b/openbsc/tests/sccp/sccp_test.c
@@ -354,14 +354,14 @@
 	return 0;
 }
 
-int sccp_write_cb(struct msgb *data, void *ctx)
+void sccp_write_cb(struct msgb *data, void *ctx)
 {
 	int i = 0;
 	const u_int8_t *got, *wanted;
 
 	if (test_data[current_test].response == NULL) {
 		FAIL("Didn't expect write callback\n");
-		return -1;
+		goto exit;
 	} else if (test_data[current_test].response_length != msgb_l2len(data)) {
 		FAIL("Size does not match. Got: %d Wanted: %d\n",
 		     msgb_l2len(data), test_data[current_test].response_length);
@@ -374,12 +374,14 @@
 		if (got[i] != wanted[i]) {
 			FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
 			     got[i], wanted[i], i);
-			return -1;
+			goto exit;
 		}
 	}
 
 	write_called = 1;
-	return 0;
+
+exit:
+	msgb_free(data);
 }
 
 void sccp_c_read(struct sccp_connection *connection, struct msgb *msgb, unsigned int len)
@@ -409,7 +411,7 @@
 	return 0;
 }
 
-static int sccp_udt_write_cb(struct msgb *data, void *context)
+static void sccp_udt_write_cb(struct msgb *data, void *context)
 {
 	const u_int8_t *got, *wanted;
 	int i;
@@ -419,7 +421,7 @@
 	if (send_data[current_test].length != msgb_l2len(data)) {
 		FAIL("Size does not match. Got: %d Wanted: %d\n",
 		     msgb_l2len(data), send_data[current_test].length);
-		return -1;
+		goto exit;
 	}
 
 	got = &data->l2h[0];
@@ -429,12 +431,14 @@
 		if (got[i] != wanted[i]) {
 			FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
 			     got[i], wanted[i], i);
-			return -1;
+			goto exit;
 		}
 	}
 
 	matched = 1;
-	return 0;
+
+exit:
+	msgb_free(data);
 }
 
 static void test_sccp_system(void)
@@ -504,11 +508,11 @@
 	return 0;
 }
 
-static int sccp_write_loop(struct msgb *data, void *context)
+static void sccp_write_loop(struct msgb *data, void *context)
 {
 	/* send it back to us */
 	sccp_system_incoming(data);
-	return 0;
+	msgb_free(data);
 }
 
 static void test_sccp_udt_communication(void)