fsm: allow graceful exit on FSM termination

The function _osmo_fsm_inst_term() terminates all child FSMs befor
it calls fi->fsm_cleanup(). This prevents the cleanup callback to
perform last actions on the child FSMs (e.g.
osmo_fsm_inst_unlink_parent()).

- Since moving the cleanup callack to the beginning of the function
  would alter the termination behavior and possibly cause malfunction
  in already existing implementation that use OSMO fsm, a new
  optional callback that is called immediately at the beginning of
  the terminatopn process is added.

Change-Id: I0fdda9fe994753f975a658c0f3fb3615949cc8bb
Closes: OS#2915
diff --git a/src/fsm.c b/src/fsm.c
index a127362..176aa8a 100644
--- a/src/fsm.c
+++ b/src/fsm.c
@@ -298,7 +298,11 @@
 
 /*! unlink child FSM from its parent FSM.
  *  \param[in] fi Descriptor of the child FSM to unlink.
- *  \param[in] ctx New talloc context */
+ *  \param[in] ctx New talloc context
+ *
+ * Never call this function from the cleanup callback, because at that time
+ * the child FSMs will already be terminated. If unlinking should be performed
+ * on FSM termination, use the grace callback instead. */
 void osmo_fsm_inst_unlink_parent(struct osmo_fsm_inst *fi, void *ctx)
 {
 	if (fi->proc.parent) {
@@ -312,7 +316,10 @@
 /*! change parent instance of an FSM.
  *  \param[in] fi Descriptor of the to-be-allocated FSM.
  *  \param[in] new_parent New parent FSM instance.
- *  \param[in] new_parent_term_event Event to be sent to parent when terminating. */
+ *  \param[in] new_parent_term_event Event to be sent to parent when terminating.
+ *
+ * Never call this function from the cleanup callback!
+ * (see also osmo_fsm_inst_unlink_parent()).*/
 void osmo_fsm_inst_change_parent(struct osmo_fsm_inst *fi,
 				 struct osmo_fsm_inst *new_parent,
 				 uint32_t new_parent_term_event)
@@ -528,6 +535,10 @@
 	LOGPFSMSRC(fi, file, line, "Terminating (cause = %s)\n",
 		   osmo_fsm_term_cause_name(cause));
 
+	/* graceful exit (optional) */
+	if (fi->fsm->pre_term)
+		fi->fsm->pre_term(fi, cause);
+
 	_osmo_fsm_inst_term_children(fi, OSMO_FSM_TERM_PARENT, NULL,
 				     file, line);