ctrl: Allow handling CTRL get/set replies in user defined code

Prior to this patch, it was not possible to gather SET/GET reply
information when implementing a CTRL client using libosmocontrol. This
is specially important when using the GET command, since one wants to
receive the queried value.
CTRL traps can also be handled this way by extending this patch in the
future if needed.

Change-Id: Id3c4631cd32c13e78e11b6e8194b8c16307ec4f1
diff --git a/TODO-RELEASE b/TODO-RELEASE
index e8fb32b..501f25b 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -12,3 +12,4 @@
 libosmogsm	API change		struct ipac_preproc_ave_cfg: new zero-sized flexible array member at end
 libosmovty	ABI change		struct cmd_element: add a field for program specific attributes
 libosmovty	ABI change		struct vty_app_info: optional program specific attributes description
+libosmoctrl     ABI change		struct ctrl_handle changed size (new field "reply_cb" at the end)
diff --git a/include/osmocom/ctrl/control_if.h b/include/osmocom/ctrl/control_if.h
index 5fa9588..b73296f 100644
--- a/include/osmocom/ctrl/control_if.h
+++ b/include/osmocom/ctrl/control_if.h
@@ -9,6 +9,7 @@
 
 typedef int (*ctrl_cmd_lookup)(void *data, vector vline, int *node_type,
 				void **node_data, int *i);
+typedef void (*ctrl_cmd_reply_cb)(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data);
 
 struct ctrl_handle {
 	struct osmo_fd listen_fd;
@@ -18,6 +19,12 @@
 
 	/* List of control connections */
 	struct llist_head ccon_list;
+
+	/* User defined GET/SET REPLY handler. User can set cmd->defer to 1 in
+	   order to own and keep the cmd pointer and free it after the function
+	   returns. "data" param is the user data pointer supplied during
+	   ctrl_handle allocation  */
+	ctrl_cmd_reply_cb reply_cb;
 };
 
 
diff --git a/src/ctrl/control_if.c b/src/ctrl/control_if.c
index ce2e367..9e3e3a9 100644
--- a/src/ctrl/control_if.c
+++ b/src/ctrl/control_if.c
@@ -221,6 +221,10 @@
 
 	if (cmd->type == CTRL_TYPE_SET_REPLY ||
 	    cmd->type == CTRL_TYPE_GET_REPLY) {
+		if (ctrl->reply_cb) {
+			ctrl->reply_cb(ctrl, cmd, data);
+			return CTRL_CMD_HANDLED;
+		}
 		if (strncmp(cmd->reply, "OK", 2) == 0) {
 			LOGP(DLCTRL, LOGL_DEBUG, "%s <%s> for %s is OK\n",
 			     get_value_string(ctrl_type_vals, cmd->type),