fsm: Introduce default time-out handling

If a FSM doesn't specify any timer_cb, simply terminate the FSM by
default on time-out.  This is a reasonable default for most cases, and
avoids copy+pasting a one-line timer_cb function in every FSM.

Also, even if there is a timer_cb, let it have a return value to decide
if the core should terminate after return from timer_cb or not.

Change-Id: I0461a9593bfb729c82b7d1d1cf9f30b1079d0212
diff --git a/src/fsm.c b/src/fsm.c
index ede769d..8fedae2 100644
--- a/src/fsm.c
+++ b/src/fsm.c
@@ -136,10 +136,20 @@
 {
 	struct osmo_fsm_inst *fi = data;
 	struct osmo_fsm *fsm = fi->fsm;
+	uint32_t T = fi->T;
 
 	LOGPFSM(fi, "Timeout of T%u\n", fi->T);
 
-	fsm->timer_cb(fi);
+	if (fsm->timer_cb) {
+		int rc = fsm->timer_cb(fi);
+		if (rc != 1)
+			return;
+		LOGPFSM(fi, "timer_cb requested termination\n");
+	} else
+		LOGPFSM(fi, "No timer_cb, automatic termination\n");
+
+	/* if timer_cb returns 1 or there is no timer_cb */
+	osmo_fsm_inst_term(fi, OSMO_FSM_TERM_TIMEOUT, &T);
 }
 
 /*! \brief allocate a new instance of a specified FSM
@@ -317,13 +327,8 @@
 		st->onenter(fi, old_state);
 
 	if (timeout_secs) {
-		if (!fsm->timer_cb)
-			LOGP(fsm->log_subsys, LOGL_ERROR, "cannot start "
-			     "timer for FSM without timer call-back\n");
-		else {
-			fi->T = T;
-			osmo_timer_schedule(&fi->timer, timeout_secs, 0);
-		}
+		fi->T = T;
+		osmo_timer_schedule(&fi->timer, timeout_secs, 0);
 	}
 
 	return 0;