<p>Neels Hofmeyr <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12717">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  Neels Hofmeyr: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">add osmo_tdef API, originally adopted from osmo-bsc T_def<br><br>Move T_def from osmo-bsc to libosmocore as osmo_tdef. Adjust naming to be more<br>consistent. Upgrade to first class API:<br>- add timer grouping<br>- add generic vty support<br>- add mising API doc<br>- add C test<br>- add VTY transcript tests, also as examples for using the API<br><br>From osmo_fsm_inst_state_chg() API doc, cross reference to osmo_tdef API.<br><br>The root reason for moving to libosmocore is that I want to use the<br>mgw_endpoint_fsm in osmo-msc for inter-MSC handover, and hence want to move the<br>FSM to libosmo-mgcp-client. This FSM uses the T_def from osmo-bsc. Though the<br>mgw_endpoint_fsm's use of T_def is minimal, I intend to use the osmo_tdef API<br>in osmo-msc (and probably elsewhere) as well. libosmocore is the most sensible<br>place for this.<br><br>osmo_tdef provides:<br><br>- a list of Tnnnn (GSM) timers with description, unit and default value.<br>- vty UI to allow users to configure non-default timeouts.<br>- API to tie T timers to osmo_fsm states and set them on state transitions.<br><br>- a few standard units (minute, second, millisecond) as well as a custom unit<br>  (which relies on the timer's human readable description to indicate the<br>  meaning of the value).<br>- conversion for standard units: for example, some GSM timers are defined in<br>  minutes, while our FSM definitions need timeouts in seconds. Conversion is<br>  for convenience only and can be easily avoided via the custom unit.<br><br>By keeping separate osmo_tdef arrays, several groups of timers can be kept<br>separately. The VTY tests in tests/tdef/ showcase different schemes:<br><br>- tests/vty/tdef_vty_test_config_root.c:<br>  Keep several timer definitions in separately named groups: showcase the<br>  osmo_tdef_vty_groups*() API. Each timer group exists exactly once.<br><br>- tests/vty/tdef_vty_test_config_subnode.c:<br>  Keep a single list of timers without separate grouping.<br>  Put this list on a specific subnode below the CONFIG_NODE.<br>  There could be several separate subnodes with timers like this, i.e.<br>  continuing from this example, sets timers could be separated by placing<br>  timers in specific config subnodes instead of using the global group name.<br><br>- tests/vty/tdef_vty_test_dynamic.c:<br>  Dynamically allocate timer definitions per each new created object.<br>  Thus there can be an arbitrary number of independent timer definitions, one<br>  per allocated object.<br><br>T_def was introduced during the recent osmo-bsc refactoring for inter-BSC<br>handover, and has proven useful:<br><br>- without osmo_tdef, each invocation of osmo_fsm_inst_state_chg() needs to be<br>  programmed with the right timeout value, for all code paths that invoke this<br>  state change. It is a likely source of errors to get one of them wrong.  By<br>  defining a T timer exactly for an FSM state, the caller can merely invoke the<br>  state change and trust on the original state definition to apply the correct<br>  timeout.<br><br>- it is helpful to have a standardized config file UI to provide user<br>  configurable timeouts, instead of inventing new VTY commands for each<br>  separate application of T timer numbers.<br><br>Change-Id: Ibd6b1ed7f1bd6e1f2e0fde53352055a4468f23e5<br>---<br>M include/Makefile.am<br>A include/osmocom/core/tdef.h<br>A include/osmocom/vty/tdef_vty.h<br>M src/Makefile.am<br>M src/fsm.c<br>A src/tdef.c<br>M src/vty/Makefile.am<br>A src/vty/tdef_vty.c<br>M tests/Makefile.am<br>A tests/tdef/tdef_test.c<br>A tests/tdef/tdef_test.ok<br>A tests/tdef/tdef_vty_test_config_root.c<br>A tests/tdef/tdef_vty_test_config_root.vty<br>A tests/tdef/tdef_vty_test_config_subnode.c<br>A tests/tdef/tdef_vty_test_config_subnode.vty<br>A tests/tdef/tdef_vty_test_dynamic.c<br>A tests/tdef/tdef_vty_test_dynamic.vty<br>M tests/testsuite.at<br>18 files changed, 2,998 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index 25a6d75..17f7d1c 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -47,6 +47,7 @@</span><br><span>                        osmocom/core/statistics.h \</span><br><span>                        osmocom/core/strrb.h \</span><br><span>                        osmocom/core/talloc.h \</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmocom/core/tdef.h \</span><br><span>                        osmocom/core/timer.h \</span><br><span>                        osmocom/core/timer_compat.h \</span><br><span>                        osmocom/core/utils.h \</span><br><span>@@ -154,6 +155,7 @@</span><br><span>                           osmocom/vty/vector.h \</span><br><span>                           osmocom/vty/vty.h \</span><br><span>                           osmocom/vty/ports.h \</span><br><span style="color: hsl(120, 100%, 40%);">+                          osmocom/vty/tdef_vty.h \</span><br><span>                           osmocom/ctrl/control_vty.h</span><br><span> endif</span><br><span> </span><br><span>diff --git a/include/osmocom/core/tdef.h b/include/osmocom/core/tdef.h</span><br><span>new file mode 100644</span><br><span>index 0000000..92b7159</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/core/tdef.h</span><br><span>@@ -0,0 +1,172 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file tdef.h</span><br><span style="color: hsl(120, 100%, 40%);">+ * API to define Tnnn timers globally and use for FSM state changes.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fsm_inst;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \defgroup Tdef  Tnnn timer configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file tdef.h</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum osmo_tdef_unit {</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_TDEF_S = 0,        /*!< most T are in seconds, keep 0 as default. */</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_TDEF_MS,           /*!< milliseconds */</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_TDEF_M,            /*!< minutes */</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_TDEF_CUSTOM,       /*!< unspecified unit, explained in osmo_tdef.desc. */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+extern const struct value_string osmo_tdef_unit_names[];</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \return enum osmo_tdef_unit value as human readable unit letter, or "custom-unit". */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline const char *osmo_tdef_unit_name(enum osmo_tdef_unit val)</span><br><span style="color: hsl(120, 100%, 40%);">+{ return get_value_string(osmo_tdef_unit_names, val); }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Define a GSM timer of the form Tnnn, with unit, default value and doc string.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Typically used as an array with the last entry being left zero-initialized, e.g.:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *         struct osmo_tdef tdefs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ *                 { .T=10, .default_val=6, .desc="RR Assignment" },</span><br><span style="color: hsl(120, 100%, 40%);">+ *                 { .T=101, .default_val=10, .desc="inter-BSC Handover MT, HO Request to HO Accept" },</span><br><span style="color: hsl(120, 100%, 40%);">+ *                 { .T=3101, .default_val=3, .desc="RR Immediate Assignment" },</span><br><span style="color: hsl(120, 100%, 40%);">+ *                 {}</span><br><span style="color: hsl(120, 100%, 40%);">+ *         };</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Program initialization should call osmo_tdefs_reset() so that all timers return the default_val, until e.g. the VTY</span><br><span style="color: hsl(120, 100%, 40%);">+ * configuration sets user-defined values (see osmo_tdef_vty_init()).</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef {</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! T1234 number; type corresponds to struct osmo_fsm_inst.T. Negative and zero T numbers are actually possible,</span><br><span style="color: hsl(120, 100%, 40%);">+       * but be aware that osmo_tdef_fsm_inst_state_chg() interprets T == 0 as "no timer". */</span><br><span style="color: hsl(120, 100%, 40%);">+     const int T;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Timeout duration (according to unit), default value; type corresponds to osmo_fsm_inst_state_chg()'s</span><br><span style="color: hsl(120, 100%, 40%);">+   * timeout_secs argument. Note that osmo_fsm_inst_state_chg() clamps the range. */</span><br><span style="color: hsl(120, 100%, 40%);">+    const unsigned long default_val;</span><br><span style="color: hsl(120, 100%, 40%);">+      const enum osmo_tdef_unit unit;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Human readable description. For unit == OSMO_TDEF_CUSTOM, this should include an explanation of the value's</span><br><span style="color: hsl(120, 100%, 40%);">+    * unit. Best keep this a short one-liner (e.g. for VTY output). */</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *desc;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Currently active timeout value, e.g. set by user config. This is the only mutable member: a user may</span><br><span style="color: hsl(120, 100%, 40%);">+       * configure the timeout value, but neither unit nor any other field. */</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned long val;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Iterate an array of struct osmo_tdef, the last item should be fully zero, i.e. "{}".</span><br><span style="color: hsl(120, 100%, 40%);">+ * Example:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     struct osmo_tdef *t;</span><br><span style="color: hsl(120, 100%, 40%);">+ *     osmo_tdef_for_each(t, tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+ *             printf("%lu %s %s\n", t->val, osmo_tdef_unit_name(t->unit), t->desc);</span><br><span style="color: hsl(120, 100%, 40%);">+ *     }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[inout] t  A struct osmo_tdef *t used for iteration, will point at the current entry inside the loop scope.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of struct osmo_tdef to iterate, zero-terminated.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define osmo_tdef_for_each(t, tdefs) \</span><br><span style="color: hsl(120, 100%, 40%);">+      for (t = tdefs; t && (t->T || t->default_val || t->desc); t++)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdefs_reset(struct osmo_tdef *tdefs);</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit,</span><br><span style="color: hsl(120, 100%, 40%);">+                        unsigned long val_if_not_present);</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef *osmo_tdef_get_entry(struct osmo_tdef *tdefs, int T);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Using osmo_tdef for osmo_fsm_inst: array entry for a mapping of state numbers to timeout definitions.</span><br><span style="color: hsl(120, 100%, 40%);">+ * For a usage example, see osmo_tdef_get_state_timeout() and test_tdef_state_timeout() in tdef_test.c. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef_state_timeout {</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Timer number to match struct osmo_tdef.T, and to pass to osmo_fsm_inst_state_chg(). */</span><br><span style="color: hsl(120, 100%, 40%);">+    int T;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! If true, call osmo_fsm_inst_state_chg_keep_timer().</span><br><span style="color: hsl(120, 100%, 40%);">+        * If T == 0, keep previous T number, otherwise also set fi->T. */</span><br><span style="color: hsl(120, 100%, 40%);">+ bool keep_timer;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                               const struct osmo_tdef_state_timeout *timeouts_array);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Call osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(), depending on the timeouts_array, tdefs and</span><br><span style="color: hsl(120, 100%, 40%);">+ * default_timeout.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * A T timer configured in sub-second precision is rounded up to the next full second. A timer in unit =</span><br><span style="color: hsl(120, 100%, 40%);">+ * OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense for custom units!).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See osmo_tdef_get_state_timeout() and osmo_tdef_get().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * If no T timer is defined for the given state (T == 0), invoke the state change without a timeout.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Should a T number be defined in timeouts_array that is not defined in tdefs, use default_timeout (in seconds). If</span><br><span style="color: hsl(120, 100%, 40%);">+ * default_timeout is negative, a missing T definition in tdefs instead causes a program abort.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is best used by wrapping this function call in a macro suitable for a specific FSM implementation, which can</span><br><span style="color: hsl(120, 100%, 40%);">+ * become as short as: my_fsm_state_chg(fi, NEXT_STATE):</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     #define my_fsm_state_chg(fi, NEXT_STATE) \</span><br><span style="color: hsl(120, 100%, 40%);">+ *             osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, my_fsm_timeouts, global_T_defs, 5)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     my_fsm_state_chg(fi, MY_FSM_STATE_1);</span><br><span style="color: hsl(120, 100%, 40%);">+ *     // -> No timeout configured, will enter state without timeout.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     my_fsm_state_chg(fi, MY_FSM_STATE_3);</span><br><span style="color: hsl(120, 100%, 40%);">+ *     // T423 configured for this state, will look up T423 in tdefs, or use 5 seconds if unset.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     my_fsm_state_chg(fi, MY_FSM_STATE_8);</span><br><span style="color: hsl(120, 100%, 40%);">+ *     // keep_timer == true for this state, will invoke osmo_fsm_inst_state_chg_keep_timer().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[inout] fi  osmo_fsm_inst to transition to another state.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] state  State number to transition to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] timeouts_array  Array of struct osmo_tdef_state_timeout[32] to look up state in.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of struct osmo_tdef (last entry zero initialized) to look up T in.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] default_timeout  If a T is set in timeouts_array, but no timeout value is configured for T, then use this</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             default timeout value as fallback, or pass -1 to abort the program.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Return value from osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer().</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout) \</span><br><span style="color: hsl(120, 100%, 40%);">+        _osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout, \</span><br><span style="color: hsl(120, 100%, 40%);">+                                  __FILE__, __LINE__)</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_tdef_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t state,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  const struct osmo_tdef_state_timeout *timeouts_array,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 const struct osmo_tdef *tdefs, unsigned long default_timeout,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 const char *file, int line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Manage timer definitions in named groups.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This should be defined as an array with the final element kept fully zero-initialized,</span><br><span style="color: hsl(120, 100%, 40%);">+ * to be compatible with osmo_tdef_vty* API. There must not be any tdefs == NULL entries except on the final</span><br><span style="color: hsl(120, 100%, 40%);">+ * zero-initialized entry. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef_group {</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *desc;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_tdef *tdefs;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Iterate an array of struct osmo_tdef_group, the last item should be fully zero, i.e. "{}".</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[inout] g  A struct osmo_tdef_group *g used for iteration, will point at the current entry inside the loop scope.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of struct osmo_tdef_group to iterate, zero-terminated.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define osmo_tdef_groups_for_each(g, tdef_groups) \</span><br><span style="color: hsl(120, 100%, 40%);">+     for (g = tdef_groups; g && g->tdefs; g++)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/include/osmocom/vty/tdef_vty.h b/include/osmocom/vty/tdef_vty.h</span><br><span>new file mode 100644</span><br><span>index 0000000..f55239a</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/vty/tdef_vty.h</span><br><span>@@ -0,0 +1,67 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file tdef_vty.h</span><br><span style="color: hsl(120, 100%, 40%);">+ * API to configure osmo_tdef Tnnn timers from VTY configuration.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct vty;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \defgroup Tdef_VTY  Tnnn timer VTY configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file tdef_vty.h</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef;</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef_group;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_ARG_T "TNNNN"</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_DOC_T "T-number, optionally preceded by 't' or 'T'.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_ARG_T_OPTIONAL "[" OSMO_TDEF_VTY_ARG_T "]"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_ARG_VAL "(<0-2147483647>|default)"</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_DOC_VAL "New timer value\n" "Set to default timer value\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_ARG_VAL_OPTIONAL "[" OSMO_TDEF_VTY_ARG_VAL "]"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_ARG_SET   OSMO_TDEF_VTY_ARG_T " " OSMO_TDEF_VTY_ARG_VAL</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_DOC_SET  OSMO_TDEF_VTY_DOC_T OSMO_TDEF_VTY_DOC_VAL</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_TDEF_VTY_ARG_SET_OPTIONAL       OSMO_TDEF_VTY_ARG_T_OPTIONAL " " OSMO_TDEF_VTY_ARG_VAL_OPTIONAL</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tdef_vty_set_cmd(struct vty *vty, struct osmo_tdef *tdefs, const char **args);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tdef_vty_show_cmd(struct vty *vty, struct osmo_tdef *tdefs, const char *T_arg,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const char *prefix_fmt, ...);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_write(struct vty *vty, struct osmo_tdef *tdefs,</span><br><span style="color: hsl(120, 100%, 40%);">+                   const char *prefix_fmt, ...);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_one(struct vty *vty, struct osmo_tdef *t, const char *prefix_fmt, ...);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_all(struct vty *vty, struct osmo_tdef *tdefs, const char *prefix_fmt, ...);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_one_va(struct vty *vty, struct osmo_tdef *t, const char *prefix_fmt, va_list va);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_all_va(struct vty *vty, struct osmo_tdef *tdefs, const char *prefix_fmt, va_list va);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *tdefs, const char *osmo_tdef_str);</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned long osmo_tdef_vty_parse_val_arg(const char *val_arg, unsigned long default_val);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_group *groups);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_groups_write(struct vty *vty, const char *indent);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index 6840f79..27ab702 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -23,7 +23,8 @@</span><br><span>                       loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \</span><br><span>                     macaddr.c stat_item.c stats.c stats_statsd.c prim.c \</span><br><span>                        conv_acc.c conv_acc_generic.c sercomm.c prbs.c \</span><br><span style="color: hsl(0, 100%, 40%);">-                        isdnhdlc.c</span><br><span style="color: hsl(120, 100%, 40%);">+                    isdnhdlc.c \</span><br><span style="color: hsl(120, 100%, 40%);">+                  tdef.c</span><br><span> </span><br><span> if HAVE_SSSE3</span><br><span> libosmocore_la_SOURCES += conv_acc_sse.c</span><br><span>diff --git a/src/fsm.c b/src/fsm.c</span><br><span>index 0d31f87..6e15ab7 100644</span><br><span>--- a/src/fsm.c</span><br><span>+++ b/src/fsm.c</span><br><span>@@ -498,6 +498,10 @@</span><br><span>  *  timer_cb. If passing timeout_secs == 0, it is recommended to also pass T ==</span><br><span>  *  0, so that fi->T is reset to 0 when no timeout is invoked.</span><br><span>  *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  See also osmo_tdef_fsm_inst_state_chg() from the osmo_tdef API, which</span><br><span style="color: hsl(120, 100%, 40%);">+ *  provides a unified way to configure and apply GSM style Tnnnn timers to FSM</span><br><span style="color: hsl(120, 100%, 40%);">+ *  state transitions.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span>  *  Range: since time_t's maximum value is not well defined in a cross platform</span><br><span>  *  way, clamp timeout_secs to the maximum of the signed 32bit range, or roughly</span><br><span>  *  68 years (float(0x7fffffff) / (60. * 60 * 24 * 365.25) = 68.0497). Thus</span><br><span>diff --git a/src/tdef.c b/src/tdef.c</span><br><span>new file mode 100644</span><br><span>index 0000000..7e79d68</span><br><span>--- /dev/null</span><br><span>+++ b/src/tdef.c</span><br><span>@@ -0,0 +1,282 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file tdef.c</span><br><span style="color: hsl(120, 100%, 40%);">+ * Implementation to define Tnnn timers globally and use for FSM state changes.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \addtogroup Tdef</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Implementation to define Tnnn timers globally and use for FSM state changes.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See also \ref Tdef_VTY</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_tdef provides:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - a list of Tnnnn (GSM) timers with description, unit and default value.</span><br><span style="color: hsl(120, 100%, 40%);">+ * - vty UI to allow users to configure non-default timeouts.</span><br><span style="color: hsl(120, 100%, 40%);">+ * - API to tie T timers to osmo_fsm states and set them on state transitions.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - a few standard units (minute, second, millisecond) as well as a custom unit</span><br><span style="color: hsl(120, 100%, 40%);">+ *   (which relies on the timer's human readable description to indicate the</span><br><span style="color: hsl(120, 100%, 40%);">+ *   meaning of the value).</span><br><span style="color: hsl(120, 100%, 40%);">+ * - conversion for standard units: for example, some GSM timers are defined in</span><br><span style="color: hsl(120, 100%, 40%);">+ *   minutes, while our FSM definitions need timeouts in seconds. Conversion is</span><br><span style="color: hsl(120, 100%, 40%);">+ *   for convenience only and can be easily avoided via the custom unit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * By keeping separate osmo_tdef arrays, several groups of timers can be kept</span><br><span style="color: hsl(120, 100%, 40%);">+ * separately. The VTY tests in tests/tdef/ showcase different schemes:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - \ref tests/vty/tdef_vty_test_config_root.c:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Keep several timer definitions in separately named groups: showcase the</span><br><span style="color: hsl(120, 100%, 40%);">+ *   osmo_tdef_vty_groups*() API. Each timer group exists exactly once.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - \ref tests/vty/tdef_vty_test_config_subnode.c:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Keep a single list of timers without separate grouping.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Put this list on a specific subnode below the CONFIG_NODE.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   There could be several separate subnodes with timers like this, i.e.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   continuing from this example, sets of timers could be separated by placing</span><br><span style="color: hsl(120, 100%, 40%);">+ *   timers in specific config subnodes instead of using the global group name.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - \ref tests/vty/tdef_vty_test_dynamic.c:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Dynamically allocate timer definitions per each new created object.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Thus there can be an arbitrary number of independent timer definitions, one</span><br><span style="color: hsl(120, 100%, 40%);">+ *   per allocated object.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_tdef was introduced because:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - without osmo_tdef, each invocation of osmo_fsm_inst_state_chg() needs to be</span><br><span style="color: hsl(120, 100%, 40%);">+ *   programmed with the right timeout value, for all code paths that invoke this</span><br><span style="color: hsl(120, 100%, 40%);">+ *   state change. It is a likely source of errors to get one of them wrong.  By</span><br><span style="color: hsl(120, 100%, 40%);">+ *   defining a T timer exactly for an FSM state, the caller can merely invoke the</span><br><span style="color: hsl(120, 100%, 40%);">+ *   state change and trust on the original state definition to apply the correct</span><br><span style="color: hsl(120, 100%, 40%);">+ *   timeout.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - it is helpful to have a standardized config file UI to provide user</span><br><span style="color: hsl(120, 100%, 40%);">+ *   configurable timeouts, instead of inventing new VTY commands for each</span><br><span style="color: hsl(120, 100%, 40%);">+ *   separate application of T timer numbers. See \ref tdef_vty.h.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file tdef.c</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! a = return_val * b. \return 0 if factor is below 1. */</span><br><span style="color: hsl(120, 100%, 40%);">+static unsigned long osmo_tdef_factor(enum osmo_tdef_unit a, enum osmo_tdef_unit b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       if (b == a</span><br><span style="color: hsl(120, 100%, 40%);">+        || b == OSMO_TDEF_CUSTOM || a == OSMO_TDEF_CUSTOM)</span><br><span style="color: hsl(120, 100%, 40%);">+                return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (b) {</span><br><span style="color: hsl(120, 100%, 40%);">+  case OSMO_TDEF_MS:</span><br><span style="color: hsl(120, 100%, 40%);">+            switch (a) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case OSMO_TDEF_S:</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+          case OSMO_TDEF_M:</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 60*1000;</span><br><span style="color: hsl(120, 100%, 40%);">+               default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     case OSMO_TDEF_S:</span><br><span style="color: hsl(120, 100%, 40%);">+             switch (a) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case OSMO_TDEF_M:</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 60;</span><br><span style="color: hsl(120, 100%, 40%);">+            default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \return val in unit to_unit, rounded up to the next integer value and clamped to ULONG_MAX, or 0 if val == 0. */</span><br><span style="color: hsl(120, 100%, 40%);">+static unsigned long osmo_tdef_round(unsigned long val, enum osmo_tdef_unit from_unit, enum osmo_tdef_unit to_unit)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  unsigned long f;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!val)</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   f = osmo_tdef_factor(from_unit, to_unit);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (f == 1)</span><br><span style="color: hsl(120, 100%, 40%);">+           return val;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (f < 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               f = osmo_tdef_factor(to_unit, from_unit);</span><br><span style="color: hsl(120, 100%, 40%);">+             return (val / f) + (val % f? 1 : 0);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* range checking */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (f > (ULONG_MAX / val))</span><br><span style="color: hsl(120, 100%, 40%);">+         return ULONG_MAX;</span><br><span style="color: hsl(120, 100%, 40%);">+     return val * f;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Set all osmo_tdef values to the default_val.</span><br><span style="color: hsl(120, 100%, 40%);">+ * It is convenient to define a tdefs array by setting only the default_val, and calling osmo_tdefs_reset() once for</span><br><span style="color: hsl(120, 100%, 40%);">+ * program startup. (See also osmo_tdef_vty_init())</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timer definitions, last entry being fully zero.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdefs_reset(struct osmo_tdef *tdefs)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_tdef *t;</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_tdef_for_each(t, tdefs)</span><br><span style="color: hsl(120, 100%, 40%);">+          t->val = t->default_val;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return the value of a T timer from a list of osmo_tdef, in the given unit.</span><br><span style="color: hsl(120, 100%, 40%);">+ * If no such timer is defined, return the default value passed, or abort the program if default < 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Round up any value match as_unit: 1100 ms as OSMO_TDEF_S becomes 2 seconds, as OSMO_TDEF_M becomes one minute.</span><br><span style="color: hsl(120, 100%, 40%);">+ * However, always return a value of zero as zero (0 ms as OSMO_TDEF_M still is 0 m).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Range: even though the value range is unsigned long here, in practice, using ULONG_MAX as value for a timeout in</span><br><span style="color: hsl(120, 100%, 40%);">+ * seconds may actually wrap to negative or low timeout values (e.g. in struct timeval). It is recommended to stay below</span><br><span style="color: hsl(120, 100%, 40%);">+ * INT_MAX seconds. See also osmo_fsm_inst_state_chg().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Usage example:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  struct osmo_tdef global_T_defs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ *                { .T=7, .default_val=50, .desc="Water Boiling Timeout" },  // default is .unit=OSMO_TDEF_S == 0</span><br><span style="color: hsl(120, 100%, 40%);">+ *           { .T=8, .default_val=300, .desc="Tea brewing" },</span><br><span style="color: hsl(120, 100%, 40%);">+ *          { .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" },</span><br><span style="color: hsl(120, 100%, 40%);">+ *           { .T=10, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" },</span><br><span style="color: hsl(120, 100%, 40%);">+ *           {}  //  <-- important! last entry shall be zero</span><br><span style="color: hsl(120, 100%, 40%);">+ *  };</span><br><span style="color: hsl(120, 100%, 40%);">+ *  osmo_tdefs_reset(global_T_defs); // make all values the default</span><br><span style="color: hsl(120, 100%, 40%);">+ *     osmo_tdef_vty_init(global_T_defs, CONFIG_NODE);</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *         val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_S, -1); // -> 50</span><br><span style="color: hsl(120, 100%, 40%);">+ *         sleep(val);</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_M, -1); // 50 seconds becomes 1 minute -> 1</span><br><span style="color: hsl(120, 100%, 40%);">+ *      sleep_minutes(val);</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, 3); // not defined, returns 3</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *       val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, -1); // not defined, program aborts!</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timer definitions, last entry must be fully zero initialized.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] T  Timer number to get the value for.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] as_unit  Return timeout value in this unit.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] val_if_not_present  Fallback value to return if no timeout is defined.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Timeout value in the unit given by as_unit, rounded up if necessary, or val_if_not_present.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit, unsigned long val_if_not_present)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct osmo_tdef *t = osmo_tdef_get_entry((struct osmo_tdef*)tdefs, T);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!t) {</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_ASSERT(val_if_not_present >= 0);</span><br><span style="color: hsl(120, 100%, 40%);">+              return val_if_not_present;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return osmo_tdef_round(t->val, t->unit, as_unit);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Find tdef entry matching T.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is useful for manipulation, which is usually limited to the VTY configuration. To retrieve a timeout value,</span><br><span style="color: hsl(120, 100%, 40%);">+ * most callers probably should use osmo_tdef_get() instead.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timer definitions, last entry being fully zero.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] T  Timer number to get the entry for.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return osmo_tdef entry matching T in given array, or NULL if no match is found.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef *osmo_tdef_get_entry(struct osmo_tdef *tdefs, int T)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_tdef *t;</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_tdef_for_each(t, tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (t->T == T)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return t;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Using osmo_tdef for osmo_fsm_inst: find a given state's osmo_tdef_state_timeout entry.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The timeouts_array shall contain exactly 32 elements, regardless whether only some of them are actually populated</span><br><span style="color: hsl(120, 100%, 40%);">+ * with nonzero values. 32 corresponds to the number of states allowed by the osmo_fsm_* API. Lookup is by array index.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Not populated entries imply a state change invocation without timeout.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * For example:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *    struct osmo_tdef_state_timeout my_fsm_timeouts[32] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ *              [MY_FSM_STATE_3] = { .T = 423 }, // look up timeout configured for T423</span><br><span style="color: hsl(120, 100%, 40%);">+ *             [MY_FSM_STATE_7] = { .T = 235 },</span><br><span style="color: hsl(120, 100%, 40%);">+ *            [MY_FSM_STATE_8] = { .keep_timer = true }, // keep previous state's T number, continue timeout.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                 // any state that is omitted will remain zero == no timeout</span><br><span style="color: hsl(120, 100%, 40%);">+ * };</span><br><span style="color: hsl(120, 100%, 40%);">+ *  osmo_tdef_get_state_timeout(MY_FSM_STATE_0, &my_fsm_timeouts) -> NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ *       osmo_tdef_get_state_timeout(MY_FSM_STATE_7, &my_fsm_timeouts) -> { .T = 235 }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The intention is then to obtain the timer like osmo_tdef_get(global_T_defs, T=235); see also</span><br><span style="color: hsl(120, 100%, 40%);">+ * fsm_inst_state_chg_T() below.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] state  State constant to look up.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] timeouts_array  Array[32] of struct osmo_tdef_state_timeout defining which timer number to use per state.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return A struct osmo_tdef_state_timeout entry, or NULL if that entry is zero initialized.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state, const struct osmo_tdef_state_timeout *timeouts_array)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct osmo_tdef_state_timeout *t;</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(state < 32);</span><br><span style="color: hsl(120, 100%, 40%);">+   t = &timeouts_array[state];</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!t->keep_timer && !t->T)</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  return t;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! See invocation macro osmo_tdef_fsm_inst_state_chg() instead.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] file  Source file name, like __FILE__.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] line  Source file line number, like __LINE__.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_tdef_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t state,</span><br><span style="color: hsl(120, 100%, 40%);">+                           const struct osmo_tdef_state_timeout *timeouts_array,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 const struct osmo_tdef *tdefs, unsigned long default_timeout,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct osmo_tdef_state_timeout *t = osmo_tdef_get_state_timeout(state, timeouts_array);</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned long val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* No timeout defined for this state? */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!t)</span><br><span style="color: hsl(120, 100%, 40%);">+               return _osmo_fsm_inst_state_chg(fi, state, 0, 0, file, line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (t->keep_timer) {</span><br><span style="color: hsl(120, 100%, 40%);">+               int rc = _osmo_fsm_inst_state_chg_keep_timer(fi, state, file, line);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (t->T && !rc)</span><br><span style="color: hsl(120, 100%, 40%);">+                   fi->T = t->T;</span><br><span style="color: hsl(120, 100%, 40%);">+           return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   val = osmo_tdef_get(tdefs, t->T, OSMO_TDEF_S, default_timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+    return _osmo_fsm_inst_state_chg(fi, state, val, t->T, file, line);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct value_string osmo_tdef_unit_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+      { OSMO_TDEF_S, "s" },</span><br><span style="color: hsl(120, 100%, 40%);">+       { OSMO_TDEF_MS, "ms" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { OSMO_TDEF_M, "m" },</span><br><span style="color: hsl(120, 100%, 40%);">+       { OSMO_TDEF_CUSTOM, "custom-unit" },</span><br><span style="color: hsl(120, 100%, 40%);">+        {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am</span><br><span>index 2e49498..cdde0fa 100644</span><br><span>--- a/src/vty/Makefile.am</span><br><span>+++ b/src/vty/Makefile.am</span><br><span>@@ -11,7 +11,8 @@</span><br><span> </span><br><span> libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \</span><br><span>                  telnet_interface.c logging_vty.c stats_vty.c \</span><br><span style="color: hsl(0, 100%, 40%);">-                  fsm_vty.c talloc_ctx_vty.c</span><br><span style="color: hsl(120, 100%, 40%);">+                    fsm_vty.c talloc_ctx_vty.c \</span><br><span style="color: hsl(120, 100%, 40%);">+                  tdef_vty.c</span><br><span> libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined</span><br><span> libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS)</span><br><span> endif</span><br><span>diff --git a/src/vty/tdef_vty.c b/src/vty/tdef_vty.c</span><br><span>new file mode 100644</span><br><span>index 0000000..1c6af70</span><br><span>--- /dev/null</span><br><span>+++ b/src/vty/tdef_vty.c</span><br><span>@@ -0,0 +1,372 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file tdef_vty.c</span><br><span style="color: hsl(120, 100%, 40%);">+ * Implementation to configure osmo_tdef Tnnn timers from VTY configuration.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/tdef_vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \addtogroup Tdef_VTY</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * VTY API for \ref Tdef.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file tdef_vty.c</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Parse an argument like "T1234", "t1234" or "1234", as from OSMO_TDEF_VTY_ARG_T.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context for vty_out() of error messages.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timer definitions to look up T timer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] T_str  Argument string. It is not validated, expected to be checked by VTY input.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return the corresponding osmo_tdef entry from the tdefs array, or NULL if no such entry exists.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *tdefs, const char *T_str)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  long l;</span><br><span style="color: hsl(120, 100%, 40%);">+       int T;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_tdef *t;</span><br><span style="color: hsl(120, 100%, 40%);">+  char *endptr;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *T_nr_str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+         vty_out(vty, "%% Error: no timers found%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   T_nr_str = T_str;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (T_nr_str[0] == 't' || T_nr_str[0] == 'T')</span><br><span style="color: hsl(120, 100%, 40%);">+         T_nr_str++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ errno = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    l = strtol(T_nr_str, &endptr, 10);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (errno || *endptr || l > INT_MAX) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "%% No such timer: '%s'%s", T_str, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     T = l;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      t = osmo_tdef_get_entry(tdefs, T);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!t)</span><br><span style="color: hsl(120, 100%, 40%);">+               vty_out(vty, "%% No such timer: T%d%s", T, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+    return t;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Parse an argument of the form "(0-2147483647|default)", as from OSMO_TDEF_VTY_ARG_VAL.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] val_arg  Argument string (not format checked).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] default_val  Value to return in case of val_arg being "default".</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Parsed value or default_val.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned long osmo_tdef_vty_parse_val_arg(const char *val_arg, unsigned long default_val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!strcmp(val_arg, "default"))</span><br><span style="color: hsl(120, 100%, 40%);">+                return default_val;</span><br><span style="color: hsl(120, 100%, 40%);">+     return atoll(val_arg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Apply a timer configuration from VTY argument strings.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Employ both osmo_tdef_vty_parse_T_arg() and osmo_tdef_vty_parse_val_arg() to configure a T timer in an array of</span><br><span style="color: hsl(120, 100%, 40%);">+ * tdefs. Evaluate two arguments, a "T1234" argument and a "(0-2147483647|default)" argument, as from</span><br><span style="color: hsl(120, 100%, 40%);">+ * OSMO_TDEF_VTY_ARGS.  If the T timer given in the first argument is found in tdefs, set it to the value given in the</span><br><span style="color: hsl(120, 100%, 40%);">+ * second argument.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context for vty_out() of error messages.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timer definitions to look up T timer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] args  Array of string arguments like { "T1234", "23" }.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return CMD_SUCCESS, or CMD_WARNING if no such timer is found in tdefs.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tdef_vty_set_cmd(struct vty *vty, struct osmo_tdef *tdefs, const char **args)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *T_arg = args[0];</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *val_arg = args[1];</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_tdef *t = osmo_tdef_vty_parse_T_arg(vty, tdefs, T_arg);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!t)</span><br><span style="color: hsl(120, 100%, 40%);">+               return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   t->val = osmo_tdef_vty_parse_val_arg(val_arg, t->default_val);</span><br><span style="color: hsl(120, 100%, 40%);">+  return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Output one or all timers to the VTY, as for a VTY command like 'show timer [TNNNN]'.</span><br><span style="color: hsl(120, 100%, 40%);">+ * If T_arg is NULL, print all timers in tdefs to the VTY.</span><br><span style="color: hsl(120, 100%, 40%);">+ * If T_arg is not NULL, employ osmo_tdef_vty_parse_T_arg() to select one timer from tdefs and print only that to the</span><br><span style="color: hsl(120, 100%, 40%);">+ * VTY.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context for vty_out() of error messages.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timer definitions.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] T_arg  Argument string like "T1234", or NULL.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] prefix_fmt  Arbitrary string to start each line with, with variable printf like arguments.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return CMD_SUCCESS, or CMD_WARNING if no such timer is found in tdefs.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tdef_vty_show_cmd(struct vty *vty, struct osmo_tdef *tdefs, const char *T_arg,</span><br><span style="color: hsl(120, 100%, 40%);">+                     const char *prefix_fmt, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    va_list va;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (T_arg) {</span><br><span style="color: hsl(120, 100%, 40%);">+          struct osmo_tdef *t = osmo_tdef_vty_parse_T_arg(vty, tdefs, T_arg);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!t)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+           va_start(va, prefix_fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_tdef_vty_out_one_va(vty, t, prefix_fmt, va);</span><br><span style="color: hsl(120, 100%, 40%);">+             va_end(va);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              va_start(va, prefix_fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_tdef_vty_out_all_va(vty, tdefs, prefix_fmt, va);</span><br><span style="color: hsl(120, 100%, 40%);">+         va_end(va);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write to VTY the current status of one timer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context for vty_out().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] t  The timer to print.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] prefix_fmt  Arbitrary string to start each line with, with variable vprintf like arguments.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] va  va_list instance. As always, call va_start() before, and va_end() after this call.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_one_va(struct vty *vty, struct osmo_tdef *t, const char *prefix_fmt, va_list va)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!t) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "%% Error: no such timer%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (prefix_fmt)</span><br><span style="color: hsl(120, 100%, 40%);">+               vty_out_va(vty, prefix_fmt, va);</span><br><span style="color: hsl(120, 100%, 40%);">+      vty_out(vty, "T%d = %lu%s%s\t%s (default: %lu%s%s)%s",</span><br><span style="color: hsl(120, 100%, 40%);">+              t->T, t->val,</span><br><span style="color: hsl(120, 100%, 40%);">+           t->unit == OSMO_TDEF_CUSTOM ? "" : " ", t->unit == OSMO_TDEF_CUSTOM ? "" : osmo_tdef_unit_name(t->unit),</span><br><span style="color: hsl(120, 100%, 40%);">+               t->desc, t->default_val,</span><br><span style="color: hsl(120, 100%, 40%);">+                t->unit == OSMO_TDEF_CUSTOM ? "" : " ", t->unit == OSMO_TDEF_CUSTOM ? "" : osmo_tdef_unit_name(t->unit),</span><br><span style="color: hsl(120, 100%, 40%);">+               VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write to VTY the current status of one timer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context for vty_out().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] t  The timer to print.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] prefix_fmt  Arbitrary string to start each line with, with variable printf like arguments.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_one(struct vty *vty, struct osmo_tdef *t, const char *prefix_fmt, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   va_list va;</span><br><span style="color: hsl(120, 100%, 40%);">+   va_start(va, prefix_fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_tdef_vty_out_one_va(vty, t, prefix_fmt, va);</span><br><span style="color: hsl(120, 100%, 40%);">+     va_end(va);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write to VTY the current status of all given timers.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context for vty_out().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timers to print, ended with a fully zero-initialized entry.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] prefix_fmt  Arbitrary string to start each line with, with variable vprintf like arguments.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] va  va_list instance. As always, call va_start() before, and va_end() after this call.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_all_va(struct vty *vty, struct osmo_tdef *tdefs, const char *prefix_fmt, va_list va)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_tdef *t;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+         vty_out(vty, "%% Error: no such timers%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_tdef_for_each(t, tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+                va_list va2;</span><br><span style="color: hsl(120, 100%, 40%);">+          va_copy(va2, va);</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_tdef_vty_out_one_va(vty, t, prefix_fmt, va);</span><br><span style="color: hsl(120, 100%, 40%);">+             va_end(va2);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write to VTY the current status of all given timers.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context for vty_out().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timers to print, ended with a fully zero-initialized entry.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] prefix_fmt  Arbitrary string to start each line with, with variable printf like arguments.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_out_all(struct vty *vty, struct osmo_tdef *tdefs, const char *prefix_fmt, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       va_list va;</span><br><span style="color: hsl(120, 100%, 40%);">+   va_start(va, prefix_fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_tdef_vty_out_all_va(vty, tdefs, prefix_fmt, va);</span><br><span style="color: hsl(120, 100%, 40%);">+ va_end(va);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write current timer configuration arguments to the vty. Skip all entries that reflect their default value.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The passed prefix string must contain both necessary indent and the VTY command the specific implementation is using.</span><br><span style="color: hsl(120, 100%, 40%);">+ * See tdef_vty_test_config_subnode.c and tdef_vty_test_dynamic.c for examples.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tdefs  Array of timers to print, ended with a fully zero-initialized entry.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] prefix_fmt  Arbitrary string to start each line with, with variable printf like arguments.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_write(struct vty *vty, struct osmo_tdef *tdefs, const char *prefix_fmt, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    va_list va;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_tdef *t;</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_tdef_for_each(t, tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (t->val == t->default_val)</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (prefix_fmt && *prefix_fmt) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      va_start(va, prefix_fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+                     vty_out_va(vty, prefix_fmt, va);</span><br><span style="color: hsl(120, 100%, 40%);">+                      va_end(va);</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "T%d %lu%s", t->T, t->val, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Singleton Tnnn groups definition as set by osmo_tdef_vty_groups_init(). */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef_group *global_tdef_groups;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \return true iff the first characters of str fully match startswith_str or both are empty. */</span><br><span style="color: hsl(120, 100%, 40%);">+static bool startswith(const char *str, const char *startswith_str)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!startswith_str)</span><br><span style="color: hsl(120, 100%, 40%);">+          return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!str)</span><br><span style="color: hsl(120, 100%, 40%);">+             return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ return strncmp(str, startswith_str, strlen(startswith_str)) == 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(show_timer, show_timer_cmd, "DYNAMIC", "DYNAMIC")</span><br><span style="color: hsl(120, 100%, 40%);">+      /* show timer [(alpha|beta|gamma)] [TNNNN] */</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *group_arg = argc > 0 ? argv[0] : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *T_arg = argc > 1 ? argv[1] : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_tdef_group *g;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* The argument should be either "tea" or "software", but the VTY also allows partial arguments</span><br><span style="color: hsl(120, 100%, 40%);">+    * like "softw" or "t" (which can also be ambiguous). */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_tdef_groups_for_each(g, global_tdef_groups) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!group_arg || startswith(g->name, group_arg))</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_tdef_vty_show_cmd(vty, g->tdefs, T_arg, "%s: ", g->name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_timer, cfg_timer_cmd, "DYNAMIC", "DYNAMIC")</span><br><span style="color: hsl(120, 100%, 40%);">+      /* show timer [(alpha|beta|gamma)] [TNNNN] [(<0-2147483647>|default)] */</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *group_arg;</span><br><span style="color: hsl(120, 100%, 40%);">+        const char **timer_args;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_tdef *tdefs = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_tdef_group *g = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If any arguments are missing, redirect to 'show' */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (argc < 3)</span><br><span style="color: hsl(120, 100%, 40%);">+              return show_timer(self, vty, argc, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If all arguments are passed, this is configuring a timer. */</span><br><span style="color: hsl(120, 100%, 40%);">+       group_arg = argc > 0 ? argv[0] : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     timer_args = argv + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_tdef_groups_for_each(g, global_tdef_groups) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (strcmp(g->name, group_arg))</span><br><span style="color: hsl(120, 100%, 40%);">+                    continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  vty_out(vty, "%% Error: ambiguous timer group match%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                       return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+             tdefs = g->tdefs;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return osmo_tdef_vty_set_cmd(vty, tdefs, timer_args);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *add_group_args(void *talloc_ctx, char *dest)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_tdef_group *g;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_talloc_asprintf(talloc_ctx, dest, "[(");</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_tdef_groups_for_each(g, global_tdef_groups) {</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_talloc_asprintf(talloc_ctx, dest, "%s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                                   (g == global_tdef_groups) ? "" : "|",</span><br><span style="color: hsl(120, 100%, 40%);">+                                     g->name);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_talloc_asprintf(talloc_ctx, dest, ")]");</span><br><span style="color: hsl(120, 100%, 40%);">+       return dest;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *add_group_docs(void *talloc_ctx, char *dest)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_tdef_group *g;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_tdef_groups_for_each(g, global_tdef_groups) {</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_talloc_asprintf(talloc_ctx, dest, "%s\n", g->desc);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     return dest;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *timer_command_string(const char *prefix, const char *suffix)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       char *dest = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_talloc_asprintf(tall_vty_cmd_ctx, dest, "%s ", prefix);</span><br><span style="color: hsl(120, 100%, 40%);">+        dest = add_group_args(tall_vty_cmd_ctx, dest);</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_talloc_asprintf(tall_vty_cmd_ctx, dest, " %s", suffix);</span><br><span style="color: hsl(120, 100%, 40%);">+        return dest;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *timer_doc_string(const char *prefix, const char *suffix)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   char *dest = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_talloc_asprintf(tall_vty_cmd_ctx, dest, "%s ", prefix);</span><br><span style="color: hsl(120, 100%, 40%);">+        dest = add_group_docs(tall_vty_cmd_ctx, dest);</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_talloc_asprintf(tall_vty_cmd_ctx, dest, " %s", suffix);</span><br><span style="color: hsl(120, 100%, 40%);">+        return dest;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Convenience implementation for keeping a fixed set of timer groups in a program.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Install a 'timer [(group|names|...)] [TNNN] [(<val>|default)]' command under the given parent_node,</span><br><span style="color: hsl(120, 100%, 40%);">+ * and install a 'show timer...' command on VIEW_NODE and ENABLE_NODE.</span><br><span style="color: hsl(120, 100%, 40%);">+ * For a usage example, see \ref tdef_test_config_root.c.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The given timer definitions group is stored in a global pointer, so this can be done only once per main() scope.</span><br><span style="color: hsl(120, 100%, 40%);">+ * It would also be possible to have distinct timer groups on separate VTY subnodes, with a "manual" implementation, but</span><br><span style="color: hsl(120, 100%, 40%);">+ * not with this API.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] parent_node  VTY node id at which to add the timer group commands, e.g. CONFIG_NODE.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] groups  Global timer groups definition.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_group *groups)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_tdef_group *g;</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(!global_tdef_groups);</span><br><span style="color: hsl(120, 100%, 40%);">+     global_tdef_groups = groups;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_tdef_groups_for_each(g, global_tdef_groups)</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_tdefs_reset(g->tdefs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      show_timer_cmd.string = timer_command_string("show timer", OSMO_TDEF_VTY_ARG_T_OPTIONAL);</span><br><span style="color: hsl(120, 100%, 40%);">+   show_timer_cmd.doc = timer_doc_string(SHOW_STR "Show timers\n", OSMO_TDEF_VTY_DOC_T);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     cfg_timer_cmd.string = timer_command_string("timer", OSMO_TDEF_VTY_ARG_SET_OPTIONAL);</span><br><span style="color: hsl(120, 100%, 40%);">+       cfg_timer_cmd.doc = timer_doc_string("Configure or show timers\n", OSMO_TDEF_VTY_DOC_SET);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        install_element_ve(&show_timer_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+      install_element(parent_node, &cfg_timer_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write the global osmo_tdef_group configuration to VTY, as previously passed to osmo_tdef_vty_groups_init().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] vty  VTY context.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] indent  String to print before each line.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_tdef_vty_groups_write(struct vty *vty, const char *indent)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_tdef_group *g;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_tdef_groups_for_each(g, global_tdef_groups)</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_tdef_vty_write(vty, g->tdefs, "%stimer %s ", indent ? : "", g->name);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index 91f042e..54fb11f 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -27,6 +27,9 @@</span><br><span>             oap/oap_client_test gsm29205/gsm29205_test             \</span><br><span>             logging/logging_vty_test                               \</span><br><span>             vty/vty_transcript_test                                \</span><br><span style="color: hsl(120, 100%, 40%);">+              tdef/tdef_test tdef/tdef_vty_test_config_root          \</span><br><span style="color: hsl(120, 100%, 40%);">+              tdef/tdef_vty_test_config_subnode                      \</span><br><span style="color: hsl(120, 100%, 40%);">+              tdef/tdef_vty_test_dynamic                             \</span><br><span>             $(NULL)</span><br><span> </span><br><span> if ENABLE_MSGFILE</span><br><span>@@ -221,6 +224,18 @@</span><br><span> gsm23003_gsm23003_test_SOURCES = gsm23003/gsm23003_test.c</span><br><span> gsm23003_gsm23003_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_test_SOURCES = tdef/tdef_test.c</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_test_LDADD = $(LDADD)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_vty_test_config_root_SOURCES = tdef/tdef_vty_test_config_root.c</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_vty_test_config_root_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_vty_test_config_subnode_SOURCES = tdef/tdef_vty_test_config_subnode.c</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_vty_test_config_subnode_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_vty_test_dynamic_SOURCES = tdef/tdef_vty_test_dynamic.c</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_tdef_vty_test_dynamic_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # The `:;' works around a Bash 3.2 bug when the output is not writeable.</span><br><span> $(srcdir)/package.m4: $(top_srcdir)/configure.ac</span><br><span>        :;{ \</span><br><span>@@ -284,6 +299,10 @@</span><br><span>              timer/clk_override_test.ok                                 \</span><br><span>         oap/oap_client_test.ok oap/oap_client_test.err             \</span><br><span>         vty/vty_transcript_test.vty                                \</span><br><span style="color: hsl(120, 100%, 40%);">+          tdef/tdef_test.ok \</span><br><span style="color: hsl(120, 100%, 40%);">+           tdef/tdef_vty_test_config_root.vty \</span><br><span style="color: hsl(120, 100%, 40%);">+          tdef/tdef_vty_test_config_subnode.vty \</span><br><span style="color: hsl(120, 100%, 40%);">+       tdef/tdef_vty_test_dynamic.vty \</span><br><span>             $(NULL)</span><br><span> </span><br><span> DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c</span><br><span>@@ -328,6 +347,7 @@</span><br><span> # To update the VTY script from current application behavior,</span><br><span> # pass -u to osmo_verify_transcript_vty.py by doing:</span><br><span> #   make vty-test U=-u</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> vty-test-logging:</span><br><span>    osmo_verify_transcript_vty.py -v \</span><br><span>           -p 42042 \</span><br><span>@@ -340,9 +360,25 @@</span><br><span>            -r "$(top_builddir)/tests/vty/vty_transcript_test" \</span><br><span>               $(U) $(srcdir)/vty/*.vty</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+vty-test-tdef:</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_verify_transcript_vty.py -v \</span><br><span style="color: hsl(120, 100%, 40%);">+            -p 42042 \</span><br><span style="color: hsl(120, 100%, 40%);">+            -r "$(top_builddir)/tests/tdef/tdef_vty_test_config_root" \</span><br><span style="color: hsl(120, 100%, 40%);">+         $(U) $(srcdir)/tdef/tdef_vty_test_config_root.vty</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_verify_transcript_vty.py -v \</span><br><span style="color: hsl(120, 100%, 40%);">+            -p 42042 \</span><br><span style="color: hsl(120, 100%, 40%);">+            -r "$(top_builddir)/tests/tdef/tdef_vty_test_config_subnode" \</span><br><span style="color: hsl(120, 100%, 40%);">+              $(U) $(srcdir)/tdef/tdef_vty_test_config_subnode.vty</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_verify_transcript_vty.py -v \</span><br><span style="color: hsl(120, 100%, 40%);">+            -p 42042 \</span><br><span style="color: hsl(120, 100%, 40%);">+            -r "$(top_builddir)/tests/tdef/tdef_vty_test_dynamic" \</span><br><span style="color: hsl(120, 100%, 40%);">+             $(U) $(srcdir)/tdef/tdef_vty_test_dynamic.vty</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# don't run vty tests concurrently so that the ports don't conflict</span><br><span> vty-test:</span><br><span>    $(MAKE) vty-test-logging</span><br><span>     $(MAKE) vty-test-vty</span><br><span style="color: hsl(120, 100%, 40%);">+  $(MAKE) vty-test-tdef</span><br><span> </span><br><span> ctrl-test:</span><br><span>      echo "No CTRL tests exist currently"</span><br><span>diff --git a/tests/tdef/tdef_test.c b/tests/tdef/tdef_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..682c7ac</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_test.c</span><br><span>@@ -0,0 +1,445 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Test implementation for osmo_tdef API. */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void *ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef tdefs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=1, .default_val=100, .desc="100s" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="100ms" },</span><br><span style="color: hsl(120, 100%, 40%);">+      { .T=3, .default_val=100, .unit=OSMO_TDEF_M, .desc="100m" },</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="100 potatoes" },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ { .T=7, .default_val=50, .desc="Water Boiling Timeout" },  // default is .unit=OSMO_TDEF_S == 0</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=8, .default_val=300, .desc="Tea brewing" },</span><br><span style="color: hsl(120, 100%, 40%);">+    { .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=10, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* test conversions */</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=1000, .default_val=2*1000, .unit=OSMO_TDEF_MS, .desc="two seconds from ms" },</span><br><span style="color: hsl(120, 100%, 40%);">+  { .T=1001, .default_val=60*1000, .unit=OSMO_TDEF_MS, .desc="one minute from ms" },</span><br><span style="color: hsl(120, 100%, 40%);">+  { .T=1002, .default_val=(ULONG_MAX/60), .unit=OSMO_TDEF_M, .desc="almost too many seconds" },</span><br><span style="color: hsl(120, 100%, 40%);">+       { .T=1003, .default_val=ULONG_MAX, .unit=OSMO_TDEF_M, .desc="too many seconds" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=1004, .default_val=1, .unit=OSMO_TDEF_MS, .desc="one ms" },</span><br><span style="color: hsl(120, 100%, 40%);">+    { .T=1005, .default_val=0, .unit=OSMO_TDEF_MS, .desc="zero ms" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=1006, .default_val=0, .unit=OSMO_TDEF_S, .desc="zero s" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=1007, .default_val=0, .unit=OSMO_TDEF_M, .desc="zero m" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=1008, .default_val=0, .unit=OSMO_TDEF_CUSTOM, .desc="zero" },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* test range */</span><br><span style="color: hsl(120, 100%, 40%);">+      { .T=INT_MAX, .default_val=ULONG_MAX, .unit=OSMO_TDEF_S, .desc="very large" },</span><br><span style="color: hsl(120, 100%, 40%);">+      { .T=INT_MAX-1, .default_val=ULONG_MAX-1, .unit=OSMO_TDEF_S, .desc="very large" },</span><br><span style="color: hsl(120, 100%, 40%);">+  { .T=INT_MAX-2, .default_val=LONG_MAX, .unit=OSMO_TDEF_S, .desc="very large" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=INT_MAX-3, .default_val=ULONG_MAX, .unit=OSMO_TDEF_M, .desc="very large in minutes" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { .T=INT_MIN, .default_val=ULONG_MAX, .unit=OSMO_TDEF_S, .desc="negative" },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      { .T=0, .default_val=1, .unit=OSMO_TDEF_CUSTOM, .desc="zero" },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* no desc */</span><br><span style="color: hsl(120, 100%, 40%);">+ { .T=123, .default_val=1 },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ {}  //  <-- important! last entry shall be zero</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define print_tdef_get(T, AS_UNIT) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+           unsigned long val = osmo_tdef_get(tdefs, T, AS_UNIT, 999); \</span><br><span style="color: hsl(120, 100%, 40%);">+          printf("osmo_tdef_get(tdefs, %d, %s, 999)\t= %lu\n", T, osmo_tdef_unit_name(AS_UNIT), val); \</span><br><span style="color: hsl(120, 100%, 40%);">+       } while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define print_tdef_get_short(T, AS_UNIT) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+                unsigned long val = osmo_tdef_get(tdefs, T, AS_UNIT, 999); \</span><br><span style="color: hsl(120, 100%, 40%);">+          printf("osmo_tdef_get(%d, %s)\t= %lu\n", T, osmo_tdef_unit_name(AS_UNIT), val); \</span><br><span style="color: hsl(120, 100%, 40%);">+   } while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void print_tdef_info(unsigned int T)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, T);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!t) {</span><br><span style="color: hsl(120, 100%, 40%);">+             printf("T%d=NULL", T);</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("T%d=%lu%s", T, t->val, osmo_tdef_unit_name(t->unit));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (t->val != t->default_val)</span><br><span style="color: hsl(120, 100%, 40%);">+           printf("(def=%lu)", t->default_val);</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("\n");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_tdef_get()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        enum osmo_tdef_unit as_unit;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        printf("\n%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_tdefs_reset(tdefs); // make all values the default</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i = 0; i < ARRAY_SIZE(tdefs)-1; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                unsigned int T = tdefs[i].T;</span><br><span style="color: hsl(120, 100%, 40%);">+          print_tdef_info(T);</span><br><span style="color: hsl(120, 100%, 40%);">+           for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_CUSTOM; as_unit++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      print_tdef_get_short(T, as_unit);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_tdef_get_nonexisting()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    printf("\n%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     print_tdef_get(5, OSMO_TDEF_S);</span><br><span style="color: hsl(120, 100%, 40%);">+       print_tdef_get(5, OSMO_TDEF_MS);</span><br><span style="color: hsl(120, 100%, 40%);">+      print_tdef_get(5, OSMO_TDEF_M);</span><br><span style="color: hsl(120, 100%, 40%);">+       print_tdef_get(5, OSMO_TDEF_CUSTOM);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_tdef_set_and_get()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_tdef *t;</span><br><span style="color: hsl(120, 100%, 40%);">+  printf("\n%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     t = osmo_tdef_get_entry(tdefs, 7);</span><br><span style="color: hsl(120, 100%, 40%);">+    printf("setting 7 = 42\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ t->val = 42;</span><br><span style="color: hsl(120, 100%, 40%);">+       print_tdef_info(7);</span><br><span style="color: hsl(120, 100%, 40%);">+   print_tdef_get_short(7, OSMO_TDEF_MS);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_tdef_get_short(7, OSMO_TDEF_S);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_tdef_get_short(7, OSMO_TDEF_M);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_tdef_get_short(7, OSMO_TDEF_CUSTOM);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  printf("setting 7 = 420\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        t->val = 420;</span><br><span style="color: hsl(120, 100%, 40%);">+      print_tdef_info(7);</span><br><span style="color: hsl(120, 100%, 40%);">+   print_tdef_get_short(7, OSMO_TDEF_MS);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_tdef_get_short(7, OSMO_TDEF_S);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_tdef_get_short(7, OSMO_TDEF_M);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_tdef_get_short(7, OSMO_TDEF_CUSTOM);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  printf("resetting\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_tdefs_reset(tdefs);</span><br><span style="color: hsl(120, 100%, 40%);">+      print_tdef_info(7);</span><br><span style="color: hsl(120, 100%, 40%);">+   print_tdef_get_short(7, OSMO_TDEF_S);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum test_tdef_fsm_states {</span><br><span style="color: hsl(120, 100%, 40%);">+       S_A = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+      S_B,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_C,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_D,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_G,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_H,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_I,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_J,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_K,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_L,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_M,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_N,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_O,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_X,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_Y,</span><br><span style="color: hsl(120, 100%, 40%);">+  S_Z,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_tdef_state_timeout test_tdef_state_timeouts[32] = {</span><br><span style="color: hsl(120, 100%, 40%);">+      [S_A] = { .T = 1 },</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_B] = { .T = 2 },</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_C] = { .T = 3 },</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_D] = { .T = 4 },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ [S_G] = { .T = 7 },</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_H] = { .T = 8 },</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_I] = { .T = 9 },</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_J] = { .T = 10 },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* keep_timer: adopt whichever T was running before and continue the timeout. */</span><br><span style="color: hsl(120, 100%, 40%);">+      [S_K] = { .keep_timer = true },</span><br><span style="color: hsl(120, 100%, 40%);">+       /* S_F defines an undefined T, but should continue previous state's timeout. */</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_L] = { .T = 123, .keep_timer = true },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* range */</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_M] = { .T = INT_MAX },</span><br><span style="color: hsl(120, 100%, 40%);">+     [S_N] = { .T = INT_MIN },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* T0 is not addressable from osmo_tdef_state_timeout, since it is indistinguishable from an unset entry. Even</span><br><span style="color: hsl(120, 100%, 40%);">+         * though a timeout value is set for T=0, the transition to state S_O will show "no timer configured". */</span><br><span style="color: hsl(120, 100%, 40%);">+   [S_O] = { .T = 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* S_X undefined on purpose */</span><br><span style="color: hsl(120, 100%, 40%);">+        /* S_Y defines a T that does not exist */</span><br><span style="color: hsl(120, 100%, 40%);">+     [S_Y] = { .T = 666 },</span><br><span style="color: hsl(120, 100%, 40%);">+ /* S_Z undefined on purpose */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define S(x)      (1 << (x))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_fsm_state test_tdef_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEF_STATE(NAME) \</span><br><span style="color: hsl(120, 100%, 40%);">+  [S_##NAME] = { \</span><br><span style="color: hsl(120, 100%, 40%);">+              .name = #NAME, \</span><br><span style="color: hsl(120, 100%, 40%);">+              .out_state_mask = 0 \</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(S_A) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_B) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_C) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_D) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_G) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_H) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_I) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_J) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_K) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_L) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_M) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_N) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_O) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_X) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_Y) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(S_Z) \</span><br><span style="color: hsl(120, 100%, 40%);">+                    , \</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   DEF_STATE(A),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(B),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(C),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(D),</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       DEF_STATE(G),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(H),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(I),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(J),</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       DEF_STATE(K),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(L),</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       DEF_STATE(M),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(N),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(O),</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       DEF_STATE(X),</span><br><span style="color: hsl(120, 100%, 40%);">+ DEF_STATE(Y),</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Z: test not being allowed to transition to other states. */</span><br><span style="color: hsl(120, 100%, 40%);">+        [S_Z] = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "Z",</span><br><span style="color: hsl(120, 100%, 40%);">+                .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(S_A)</span><br><span style="color: hsl(120, 100%, 40%);">+                      ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct value_string test_tdef_fsm_event_names[] = { {} };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm test_tdef_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+  .name = "tdef_test",</span><br><span style="color: hsl(120, 100%, 40%);">+        .states = test_tdef_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+       .event_names = test_tdef_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+     .num_states = ARRAY_SIZE(test_tdef_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+       .log_subsys = DLGLOBAL,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct timeval fake_time_start_time = { 123, 456 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define fake_time_passes(secs, usecs) do \</span><br><span style="color: hsl(120, 100%, 40%);">+{ \</span><br><span style="color: hsl(120, 100%, 40%);">+   struct timeval diff; \</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_gettimeofday_override_add(secs, usecs); \</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_clock_override_add(CLOCK_MONOTONIC, secs, usecs * 1000); \</span><br><span style="color: hsl(120, 100%, 40%);">+       timersub(&osmo_gettimeofday_override_time, &fake_time_start_time, &diff); \</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("Total time passed: %ld.%06ld s\n", diff.tv_sec, diff.tv_usec); \</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_timers_prepare(); \</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_timers_update(); \</span><br><span style="color: hsl(120, 100%, 40%);">+} while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void fake_time_start()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct timespec *clock_override;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_gettimeofday_override_time = fake_time_start_time;</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_gettimeofday_override = true;</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_override = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(clock_override);</span><br><span style="color: hsl(120, 100%, 40%);">+  clock_override->tv_sec = fake_time_start_time.tv_sec;</span><br><span style="color: hsl(120, 100%, 40%);">+      clock_override->tv_nsec = fake_time_start_time.tv_usec * 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_clock_override_enable(CLOCK_MONOTONIC, true);</span><br><span style="color: hsl(120, 100%, 40%);">+    fake_time_passes(0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void print_fsm_state(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct timeval remaining;</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("state=%s T=%d", osmo_fsm_inst_state_name(fi), fi->T);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!osmo_timer_pending(&fi->timer)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         printf(", no timeout\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_timer_remaining(&fi->timer, &osmo_gettimeofday_override_time, &remaining);</span><br><span style="color: hsl(120, 100%, 40%);">+        printf(", %lu.%06lu s remaining\n", remaining.tv_sec, remaining.tv_usec);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define test_tdef_fsm_state_chg(NEXT_STATE) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+                const struct osmo_tdef_state_timeout *st = osmo_tdef_get_state_timeout(NEXT_STATE, \</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                                 test_tdef_state_timeouts); \</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!st) { \</span><br><span style="color: hsl(120, 100%, 40%);">+                  printf(" --> %s (no timer configured for this state)\n", \</span><br><span style="color: hsl(120, 100%, 40%);">+                              osmo_fsm_state_name(&test_tdef_fsm, NEXT_STATE)); \</span><br><span style="color: hsl(120, 100%, 40%);">+                } else { \</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, st->T); \</span><br><span style="color: hsl(120, 100%, 40%);">+                 int rc = osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, test_tdef_state_timeouts, tdefs, 999); \</span><br><span style="color: hsl(120, 100%, 40%);">+                        printf(" --> %s (configured as T%d%s %lu %s) rc=%d;\t", osmo_fsm_state_name(&test_tdef_fsm, \</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                                                    NEXT_STATE), \</span><br><span style="color: hsl(120, 100%, 40%);">+                           st->T, st->keep_timer ? "(keep_timer)" : "", \</span><br><span style="color: hsl(120, 100%, 40%);">+                              t? t->val : -1, t? osmo_tdef_unit_name(t->unit) : "-", \</span><br><span style="color: hsl(120, 100%, 40%);">+                              rc); \</span><br><span style="color: hsl(120, 100%, 40%);">+                 print_fsm_state(fi); \</span><br><span style="color: hsl(120, 100%, 40%);">+                } \</span><br><span style="color: hsl(120, 100%, 40%);">+   } while(0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_tdef_state_timeout(bool test_range)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_tdef *m = osmo_tdef_get_entry(tdefs, INT_MAX);</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned long m_secs;</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("\n%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_tdefs_reset(tdefs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    fake_time_start();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  fi = osmo_fsm_inst_alloc(&test_tdef_fsm, ctx, NULL, LOGL_DEBUG, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+      print_fsm_state(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        test_tdef_fsm_state_chg(S_A);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_B);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_C);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_D);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       test_tdef_fsm_state_chg(S_G);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_H);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_I);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_J);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("- test keep_timer:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     fake_time_passes(123, 45678);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_fsm_state(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+  test_tdef_fsm_state_chg(S_K);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_A);</span><br><span style="color: hsl(120, 100%, 40%);">+ fake_time_passes(23, 45678);</span><br><span style="color: hsl(120, 100%, 40%);">+  print_fsm_state(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+  test_tdef_fsm_state_chg(S_K);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       test_tdef_fsm_state_chg(S_A);</span><br><span style="color: hsl(120, 100%, 40%);">+ fake_time_passes(23, 45678);</span><br><span style="color: hsl(120, 100%, 40%);">+  print_fsm_state(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+  test_tdef_fsm_state_chg(S_L);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("- test large T:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        test_tdef_fsm_state_chg(S_M);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("- test T<0:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_N);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("- test T=0:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    test_tdef_fsm_state_chg(S_O);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("- test no timer:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       test_tdef_fsm_state_chg(S_X);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("- test undefined timer, using default_val arg of osmo_tdef_fsm_inst_state_chg(), here passed as 999:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   test_tdef_fsm_state_chg(S_Y);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* the range of unsigned long is architecture dependent. This test can be invoked manually to see whether</span><br><span style="color: hsl(120, 100%, 40%);">+      * clamping the timeout values works, but the output will be of varying lengths depending on the system's</span><br><span style="color: hsl(120, 100%, 40%);">+  * unsigned long range, and would cause differences in expected output. */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (test_range) {</span><br><span style="color: hsl(120, 100%, 40%);">+             printf("- test range:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          test_tdef_fsm_state_chg(S_M);</span><br><span style="color: hsl(120, 100%, 40%);">+         /* sweep through all the bits, shifting in 0xfffff.. from the right. */</span><br><span style="color: hsl(120, 100%, 40%);">+               m_secs = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           do {</span><br><span style="color: hsl(120, 100%, 40%);">+                  m_secs = (m_secs << 1) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                     switch (m_secs) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     case 0x7fff:</span><br><span style="color: hsl(120, 100%, 40%);">+                          printf("--- int32_t max ---\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case 0xffff:</span><br><span style="color: hsl(120, 100%, 40%);">+                          printf("--- uint32_t max ---\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                           break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case 0x7fffffff:</span><br><span style="color: hsl(120, 100%, 40%);">+                              printf("--- int64_t max ---\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case 0xffffffff:</span><br><span style="color: hsl(120, 100%, 40%);">+                              printf("--- uint64_t max ---\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                           break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        default:</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   m->val = m_secs - 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                       test_tdef_fsm_state_chg(S_M);</span><br><span style="color: hsl(120, 100%, 40%);">+                 m->val = m_secs;</span><br><span style="color: hsl(120, 100%, 40%);">+                   test_tdef_fsm_state_chg(S_M);</span><br><span style="color: hsl(120, 100%, 40%);">+                 m->val = m_secs + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                       test_tdef_fsm_state_chg(S_M);</span><br><span style="color: hsl(120, 100%, 40%);">+         } while (m_secs < ULONG_MAX);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   printf("- test disallowed transition:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+  test_tdef_fsm_state_chg(S_Z);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_B);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_C);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_tdef_fsm_state_chg(S_D);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx = talloc_named_const(NULL, 0, "tdef_test.c");</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_init_logging2(ctx, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      log_set_print_filename(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_category(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_fsm_register(&test_tdef_fsm);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      test_tdef_get();</span><br><span style="color: hsl(120, 100%, 40%);">+      test_tdef_get_nonexisting();</span><br><span style="color: hsl(120, 100%, 40%);">+  test_tdef_set_and_get();</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Run range test iff any argument is passed on the cmdline. For the rationale, see the comment in</span><br><span style="color: hsl(120, 100%, 40%);">+     * test_tdef_state_timeout(). */</span><br><span style="color: hsl(120, 100%, 40%);">+      test_tdef_state_timeout(argc > 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return EXIT_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/tdef/tdef_test.ok b/tests/tdef/tdef_test.ok</span><br><span>new file mode 100644</span><br><span>index 0000000..cf4b77f</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_test.ok</span><br><span>@@ -0,0 +1,184 @@</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test_tdef_get()</span><br><span style="color: hsl(120, 100%, 40%);">+T1=100s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1, s)   = 100</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1, ms)     = 100000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1, m)   = 2</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1, custom-unit)      = 100</span><br><span style="color: hsl(120, 100%, 40%);">+T2=100ms</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2, s)    = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2, ms)       = 100</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2, m)      = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2, custom-unit)      = 100</span><br><span style="color: hsl(120, 100%, 40%);">+T3=100m</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(3, s)     = 6000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(3, ms)    = 6000000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(3, m)  = 100</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(3, custom-unit)    = 100</span><br><span style="color: hsl(120, 100%, 40%);">+T4=100custom-unit</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(4, s)   = 100</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(4, ms)     = 100</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(4, m)      = 100</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(4, custom-unit)    = 100</span><br><span style="color: hsl(120, 100%, 40%);">+T7=50s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, s)      = 50</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, ms)      = 50000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, m)    = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, custom-unit)      = 50</span><br><span style="color: hsl(120, 100%, 40%);">+T8=300s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(8, s)      = 300</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(8, ms)     = 300000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(8, m)   = 5</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(8, custom-unit)      = 300</span><br><span style="color: hsl(120, 100%, 40%);">+T9=5m</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(9, s)       = 300</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(9, ms)     = 300000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(9, m)   = 5</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(9, custom-unit)      = 5</span><br><span style="color: hsl(120, 100%, 40%);">+T10=20m</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(10, s)      = 1200</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(10, ms)   = 1200000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(10, m) = 20</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(10, custom-unit)    = 20</span><br><span style="color: hsl(120, 100%, 40%);">+T1000=2000ms</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1000, s)      = 2</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1000, ms)    = 2000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1000, m)  = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1000, custom-unit)   = 2000</span><br><span style="color: hsl(120, 100%, 40%);">+T1001=60000ms</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1001, s)   = 60</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1001, ms)   = 60000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1001, m) = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1001, custom-unit)   = 60000</span><br><span style="color: hsl(120, 100%, 40%);">+T1002=307445734561825860m</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1002, s)      = 18446744073709551600</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1002, ms) = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1002, m)  = 307445734561825860</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1002, custom-unit)  = 307445734561825860</span><br><span style="color: hsl(120, 100%, 40%);">+T1003=18446744073709551615m</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1003, s)       = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1003, ms) = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1003, m)  = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1003, custom-unit)        = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+T1004=1ms</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1004, s)       = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1004, ms)    = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1004, m)     = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1004, custom-unit)   = 1</span><br><span style="color: hsl(120, 100%, 40%);">+T1005=0ms</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1005, s)  = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1005, ms)    = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1005, m)     = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1005, custom-unit)   = 0</span><br><span style="color: hsl(120, 100%, 40%);">+T1006=0s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1006, s)   = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1006, ms)    = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1006, m)     = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1006, custom-unit)   = 0</span><br><span style="color: hsl(120, 100%, 40%);">+T1007=0m</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1007, s)   = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1007, ms)    = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1007, m)     = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1007, custom-unit)   = 0</span><br><span style="color: hsl(120, 100%, 40%);">+T1008=0custom-unit</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1008, s) = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1008, ms)    = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1008, m)     = 0</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(1008, custom-unit)   = 0</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483647=18446744073709551615s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483647, s)    = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483647, ms)   = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483647, m)    = 307445734561825861</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483647, custom-unit)    = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483646=18446744073709551614s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483646, s) = 18446744073709551614</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483646, ms)   = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483646, m)    = 307445734561825861</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483646, custom-unit)    = 18446744073709551614</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483645=9223372036854775807s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483645, s)  = 9223372036854775807</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483645, ms)    = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483645, m)    = 153722867280912931</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483645, custom-unit)    = 9223372036854775807</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483644=18446744073709551615m</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483644, s)  = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483644, ms)   = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483644, m)    = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(2147483644, custom-unit)  = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+T-2147483648=18446744073709551615s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(-2147483648, s)       = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(-2147483648, ms)  = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(-2147483648, m)   = 307445734561825861</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(-2147483648, custom-unit)   = 18446744073709551615</span><br><span style="color: hsl(120, 100%, 40%);">+T0=1custom-unit</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(0, s)    = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(0, ms)       = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(0, m)        = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(0, custom-unit)      = 1</span><br><span style="color: hsl(120, 100%, 40%);">+T123=1s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(123, s)     = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(123, ms)     = 1000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(123, m)   = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(123, custom-unit)    = 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test_tdef_get_nonexisting()</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(tdefs, 5, s, 999)     = 999</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(tdefs, 5, ms, 999) = 999</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(tdefs, 5, m, 999)  = 999</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(tdefs, 5, custom-unit, 999)        = 999</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test_tdef_set_and_get()</span><br><span style="color: hsl(120, 100%, 40%);">+setting 7 = 42</span><br><span style="color: hsl(120, 100%, 40%);">+T7=42s(def=50)</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, ms)  = 42000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, s)    = 42</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, m)       = 1</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, custom-unit)      = 42</span><br><span style="color: hsl(120, 100%, 40%);">+setting 7 = 420</span><br><span style="color: hsl(120, 100%, 40%);">+T7=420s(def=50)</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, ms)    = 420000</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, s)   = 420</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, m)      = 7</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, custom-unit)      = 420</span><br><span style="color: hsl(120, 100%, 40%);">+resetting</span><br><span style="color: hsl(120, 100%, 40%);">+T7=50s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tdef_get(7, s)   = 50</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test_tdef_state_timeout()</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 0.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+state=A T=0, no timeout</span><br><span style="color: hsl(120, 100%, 40%);">+ --> A (configured as T1 100 s) rc=0;     state=A T=1, 100.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> B (configured as T2 100 ms) rc=0;  state=B T=2, 1.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> C (configured as T3 100 m) rc=0;     state=C T=3, 6000.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> D (configured as T4 100 custom-unit) rc=0;        state=D T=4, 100.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> G (configured as T7 50 s) rc=0;    state=G T=7, 50.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> H (configured as T8 300 s) rc=0;    state=H T=8, 300.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> I (configured as T9 5 m) rc=0;     state=I T=9, 300.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> J (configured as T10 20 m) rc=0;   state=J T=10, 1200.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+- test keep_timer:</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 123.045678 s</span><br><span style="color: hsl(120, 100%, 40%);">+state=J T=10, 1076.954322 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> K (configured as T0(keep_timer) 1 custom-unit) rc=0; state=K T=10, 1076.954322 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 146.091356 s</span><br><span style="color: hsl(120, 100%, 40%);">+state=A T=1, 76.954322 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> K (configured as T0(keep_timer) 1 custom-unit) rc=0;  state=K T=1, 76.954322 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> A (configured as T1 100 s) rc=0;    state=A T=1, 100.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 169.137034 s</span><br><span style="color: hsl(120, 100%, 40%);">+state=A T=1, 76.954322 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> L (configured as T123(keep_timer) 1 s) rc=0;  state=L T=123, 76.954322 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+- test large T:</span><br><span style="color: hsl(120, 100%, 40%);">+ --> M (configured as T2147483647 18446744073709551615 s) rc=0;       state=M T=2147483647, 2147483647.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+- test T<0:</span><br><span style="color: hsl(120, 100%, 40%);">+ --> N (configured as T-2147483648 18446744073709551615 s) rc=0;        state=N T=-2147483648, 2147483647.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+- test T=0:</span><br><span style="color: hsl(120, 100%, 40%);">+ --> O (no timer configured for this state)</span><br><span style="color: hsl(120, 100%, 40%);">+- test no timer:</span><br><span style="color: hsl(120, 100%, 40%);">+ --> X (no timer configured for this state)</span><br><span style="color: hsl(120, 100%, 40%);">+- test undefined timer, using default_val arg of osmo_tdef_fsm_inst_state_chg(), here passed as 999:</span><br><span style="color: hsl(120, 100%, 40%);">+ --> Y (configured as T666 18446744073709551615 -) rc=0;  state=Y T=666, 999.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+- test disallowed transition:</span><br><span style="color: hsl(120, 100%, 40%);">+ --> Z (no timer configured for this state)</span><br><span style="color: hsl(120, 100%, 40%);">+ --> B (configured as T2 100 ms) rc=0; state=B T=2, 1.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> C (configured as T3 100 m) rc=0;     state=C T=3, 6000.000000 s remaining</span><br><span style="color: hsl(120, 100%, 40%);">+ --> D (configured as T4 100 custom-unit) rc=0;        state=D T=4, 100.000000 s remaining</span><br><span>diff --git a/tests/tdef/tdef_vty_test_config_root.c b/tests/tdef/tdef_vty_test_config_root.c</span><br><span>new file mode 100644</span><br><span>index 0000000..138ac00</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_vty_test_config_root.c</span><br><span>@@ -0,0 +1,292 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Test implementation for osmo_tdef VTY configuration API. */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define _GNU_SOURCE</span><br><span style="color: hsl(120, 100%, 40%);">+#include <getopt.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <signal.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/misc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/telnet_interface.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/tdef_vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "config.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* ------------------- HERE IS THE INTERESTING TDEF RELEVANT PART ------------------- */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* This example keeps several separate timer groups and offers 'timer' VTY commands at the root of the config node. See</span><br><span style="color: hsl(120, 100%, 40%);">+ * the tdef_vty_test_config_root.vty transcript test.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef tdefs_test[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ { .T=1, .default_val=100, .desc="Testing a hundred seconds" },  // default is .unit=OSMO_TDEF_S == 0</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="Testing a hundred milliseconds" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=3, .default_val=100, .unit=OSMO_TDEF_M, .desc="Testing a hundred minutes" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="Testing a hundred potatoes" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=INT_MAX, .default_val=ULONG_MAX, .unit=OSMO_TDEF_M, .desc="Very large" },</span><br><span style="color: hsl(120, 100%, 40%);">+      { .T=-23, .default_val=-15, .desc="Negative T number" },</span><br><span style="color: hsl(120, 100%, 40%);">+    {}  //  <-- important! last entry shall be zero</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef tdefs_tea[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=1, .default_val=50, .desc="Water Boiling Timeout" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=2, .default_val=300, .desc="Tea brewing" },</span><br><span style="color: hsl(120, 100%, 40%);">+    { .T=3, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=4, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" },</span><br><span style="color: hsl(120, 100%, 40%);">+      {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef tdefs_software[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=1, .default_val=30, .unit=OSMO_TDEF_M, .desc="Write code" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=2, .default_val=20, .unit=OSMO_TDEF_MS, .desc="Hit segfault" },</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=3, .default_val=480, .unit=OSMO_TDEF_M, .desc="Fix bugs" },</span><br><span style="color: hsl(120, 100%, 40%);">+    {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef_group tdef_groups[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "tea",</span><br><span style="color: hsl(120, 100%, 40%);">+              .desc = "Tea time",</span><br><span style="color: hsl(120, 100%, 40%);">+         .tdefs = tdefs_tea,</span><br><span style="color: hsl(120, 100%, 40%);">+   },</span><br><span style="color: hsl(120, 100%, 40%);">+    {</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "test",</span><br><span style="color: hsl(120, 100%, 40%);">+             .desc = "Test timers",</span><br><span style="color: hsl(120, 100%, 40%);">+              .tdefs = tdefs_test,</span><br><span style="color: hsl(120, 100%, 40%);">+  },</span><br><span style="color: hsl(120, 100%, 40%);">+    {</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "software",</span><br><span style="color: hsl(120, 100%, 40%);">+         .desc = "Typical software development cycle",</span><br><span style="color: hsl(120, 100%, 40%);">+               .tdefs = tdefs_software,</span><br><span style="color: hsl(120, 100%, 40%);">+      },</span><br><span style="color: hsl(120, 100%, 40%);">+    {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum tdef_vty_test_nodes {</span><br><span style="color: hsl(120, 100%, 40%);">+  TIMER_NODE = _LAST_OSMOVTY_NODE + 1,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* This example puts 'timer' configuration commands directly at the root of the CONFIG_NODE.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This TIMER_NODE is merely needed as a hook for the vty_write() command, but becomes an empty node in the VTY docs.</span><br><span style="color: hsl(120, 100%, 40%);">+ * It is possible to cheat around needing this if you choose to config_write_timer() in another root nodes' write cb.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Another example using a 'network' subnode is \ref tdef_vty_test_config_subnode.c */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct cmd_node timer_node = {</span><br><span style="color: hsl(120, 100%, 40%);">+     TIMER_NODE,</span><br><span style="color: hsl(120, 100%, 40%);">+   "%s(config-timer)# ",</span><br><span style="color: hsl(120, 100%, 40%);">+       1,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int config_write_timer(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_tdef_vty_groups_write(vty, "");</span><br><span style="color: hsl(120, 100%, 40%);">+        return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void timer_init_vty()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Again, this is merely to get a vty write hook, see above. */</span><br><span style="color: hsl(120, 100%, 40%);">+       install_node(&timer_node, config_write_timer);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_tdef_vty_groups_init(CONFIG_NODE, tdef_groups);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* ------------------- THE REST is just boilerplate osmo main() ------------------- */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *root_ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void print_help()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    printf( "options:\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                "  -h      --help          this text\n"</span><br><span style="color: hsl(120, 100%, 40%);">+             "  -d      --debug MASK    Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+           "  -D      --daemonize     For the process into a background daemon\n"</span><br><span style="color: hsl(120, 100%, 40%);">+              "  -c      --config-file   Specify the filename of the config file\n"</span><br><span style="color: hsl(120, 100%, 40%);">+               "  -s      --disable-color Don't use colors in stderr log output\n"</span><br><span style="color: hsl(120, 100%, 40%);">+             "  -T      --timestamp     Prefix every log line with a timestamp\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                "  -V      --version       Print version information and exit\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            "  -e      --log-level     Set a global log-level\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                );</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct {</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *config_file;</span><br><span style="color: hsl(120, 100%, 40%);">+      int daemonize;</span><br><span style="color: hsl(120, 100%, 40%);">+} cmdline_config = {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_options(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+           int option_idx = 0, c;</span><br><span style="color: hsl(120, 100%, 40%);">+                static const struct option long_options[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "help", 0, 0, 'h' },</span><br><span style="color: hsl(120, 100%, 40%);">+                      { "debug", 1, 0, 'd' },</span><br><span style="color: hsl(120, 100%, 40%);">+                     { "daemonize", 0, 0, 'D' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "config-file", 1, 0, 'c' },</span><br><span style="color: hsl(120, 100%, 40%);">+                       { "disable-color", 0, 0, 's' },</span><br><span style="color: hsl(120, 100%, 40%);">+                     { "timestamp", 0, 0, 'T' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "version", 0, 0, 'V' },</span><br><span style="color: hsl(120, 100%, 40%);">+                   { "log-level", 1, 0, 'e' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 {}</span><br><span style="color: hsl(120, 100%, 40%);">+            };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          c = getopt_long(argc, argv, "hc:d:Dc:sTVe:",</span><br><span style="color: hsl(120, 100%, 40%);">+                                long_options, &option_idx);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (c == -1)</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              switch (c) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case 'h':</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_help();</span><br><span style="color: hsl(120, 100%, 40%);">+                 exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+              case 's':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'd':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_parse_category_mask(osmo_stderr_target, optarg);</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'D':</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmdline_config.daemonize = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'c':</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmdline_config.config_file = optarg;</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'T':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_print_timestamp(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+                       break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'e':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_log_level(osmo_stderr_target, atoi(optarg));</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'V':</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_version(1);</span><br><span style="color: hsl(120, 100%, 40%);">+                     exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* catch unknown options *as well as* missing arguments. */</span><br><span style="color: hsl(120, 100%, 40%);">+                   fprintf(stderr, "Error in command line options. Exiting.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                       exit(-1);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int quit = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void signal_handler(int signal)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     fprintf(stdout, "signal %u received\n", signal);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (signal) {</span><br><span style="color: hsl(120, 100%, 40%);">+     case SIGINT:</span><br><span style="color: hsl(120, 100%, 40%);">+  case SIGTERM:</span><br><span style="color: hsl(120, 100%, 40%);">+         quit++;</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case SIGABRT:</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_generate_backtrace();</span><br><span style="color: hsl(120, 100%, 40%);">+            /* in case of abort, we want to obtain a talloc report</span><br><span style="color: hsl(120, 100%, 40%);">+                 * and then return to the caller, who will abort the process */</span><br><span style="color: hsl(120, 100%, 40%);">+       case SIGUSR1:</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_report(tall_vty_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_report_full(root_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case SIGUSR2:</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_report_full(tall_vty_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct vty_app_info vty_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .name           = "tdef_vty_test",</span><br><span style="color: hsl(120, 100%, 40%);">+  .version        = PACKAGE_VERSION,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct log_info_cat default_categories[] = {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct log_info log_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .cat = default_categories,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_cat = ARRAY_SIZE(default_categories),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     root_ctx = talloc_named_const(NULL, 0, "tdef_vty_test");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_init_logging2(root_ctx, &log_info);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        vty_info.tall_ctx = root_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_init(&vty_info);</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_talloc_vty_add_cmds();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ timer_init_vty(); /* <---- the only tdef relevant init */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        handle_options(argc, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmdline_config.config_file) {</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = vty_read_config_file(cmdline_config.config_file, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      fprintf(stderr, "Failed to parse the config file: '%s'\n", cmdline_config.config_file);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   signal(SIGINT, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+  signal(SIGTERM, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGABRT, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGUSR1, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGUSR2, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_init_ignore_signals();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmdline_config.daemonize) {</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = osmo_daemonize();</span><br><span style="color: hsl(120, 100%, 40%);">+                if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      perror("Error during daemonize");</span><br><span style="color: hsl(120, 100%, 40%);">+                   return 6;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while (!quit) {</span><br><span style="color: hsl(120, 100%, 40%);">+               log_reset_context();</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_select_main(0);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(root_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(tall_vty_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/tdef/tdef_vty_test_config_root.vty b/tests/tdef/tdef_vty_test_config_root.vty</span><br><span>new file mode 100644</span><br><span>index 0000000..12876a6</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_vty_test_config_root.vty</span><br><span>@@ -0,0 +1,292 @@</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> list</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+  show timer [(tea|test|software)] [TNNNN]</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> show timer ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [tea]       Tea time</span><br><span style="color: hsl(120, 100%, 40%);">+  [test]      Test timers</span><br><span style="color: hsl(120, 100%, 40%);">+  [software]  Typical software development cycle</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> show timer test ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [TNNNN]  T-number, optionally preceded by 't' or 'T'.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> show timer</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s       Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 5 m     Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 30 m        Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 20 ms        Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> enable</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# show timer</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s        Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 5 m     Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 30 m        Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 20 ms        Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# configure terminal</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# list</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+  timer [(tea|test|software)] [TNNNN] [(<0-2147483647>|default)]</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [tea]       Tea time</span><br><span style="color: hsl(120, 100%, 40%);">+  [test]      Test timers</span><br><span style="color: hsl(120, 100%, 40%);">+  [software]  Typical software development cycle</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer sof T123 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [<0-2147483647>]  New timer value</span><br><span style="color: hsl(120, 100%, 40%);">+  [default]         Set to default timer value</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer sof T123 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [<0-2147483647>]  New timer value</span><br><span style="color: hsl(120, 100%, 40%);">+  [default]         Set to default timer value</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer test ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [TNNNN]  T-number, optionally preceded by 't' or 'T'.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer test t2 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [<0-2147483647>]  New timer value</span><br><span style="color: hsl(120, 100%, 40%);">+  [default]         Set to default timer value</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s       Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 5 m     Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 30 m        Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 20 ms        Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer tea</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s   Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 5 m     Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer tea 2</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer tea t2</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s    Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer tea T2</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s    Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer tea T5</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T5</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer tea T0</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer tea T-123</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T-123</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer t</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s        Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 5 m     Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer te</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s  Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 5 m     Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer te T2</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s      Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea 3 30</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T3</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 30 m        Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea t3 31</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T3</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 31 m Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T3 32</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T3</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 32 m Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T-123 99</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T-123</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T0 0</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T123 default</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T123</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 32 m    Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer t</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s       Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 32 m    Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer te T2</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s      Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer test T2 100</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tes T2 100</span><br><span style="color: hsl(120, 100%, 40%);">+% Error: no timers found</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer te T2 100</span><br><span style="color: hsl(120, 100%, 40%);">+% Error: no timers found</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer software</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 30 m   Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 20 ms        Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer software 1</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 30 m       Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer software t1</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 30 m     Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer software T1</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 30 m     Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer software T99</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T99</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer software T-123123</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T-123123</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer software T0</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software 1 11</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software T1</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 11 m        Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software t1 12</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software T1</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 12 m        Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software T1 13</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software T2 0</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 13 m       Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 0 ms Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer softw</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 13 m    Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 0 ms Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer softw T3</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m        Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer softw T3 23</span><br><span style="color: hsl(120, 100%, 40%);">+% Error: no timers found</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 32 m    Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 13 m        Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 0 ms Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# do show timer</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T1 = 50 s       Water Boiling Timeout (default: 50 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T2 = 300 s  Tea brewing (default: 300 s)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T3 = 32 m    Let tea cool down before drinking (default: 5 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tea: T4 = 20 m        Forgot to drink tea while it's warm (default: 20 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T1 = 100 s       Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2 = 100 ms   Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T4 = 100      Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T2147483647 = 18446744073709551615 m   Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+test: T-23 = 18446744073709551601 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T1 = 13 m        Write code (default: 30 m)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T2 = 0 ms Hit segfault (default: 20 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+software: T3 = 480 m     Fix bugs (default: 480 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+timer tea T3 32</span><br><span style="color: hsl(120, 100%, 40%);">+timer software T1 13</span><br><span style="color: hsl(120, 100%, 40%);">+timer software T2 0</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer tea T3 default</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software T1 default</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+timer software T2 0</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer softw 2 default</span><br><span style="color: hsl(120, 100%, 40%);">+% Error: no timers found</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# timer software 2 default</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span>diff --git a/tests/tdef/tdef_vty_test_config_subnode.c b/tests/tdef/tdef_vty_test_config_subnode.c</span><br><span>new file mode 100644</span><br><span>index 0000000..c371c8d</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_vty_test_config_subnode.c</span><br><span>@@ -0,0 +1,288 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Test implementation for osmo_tdef VTY configuration API. */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define _GNU_SOURCE</span><br><span style="color: hsl(120, 100%, 40%);">+#include <getopt.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <signal.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/misc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/telnet_interface.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/tdef_vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "config.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* ------------------- HERE IS THE INTERESTING TDEF RELEVANT PART ------------------- */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* This example keeps a single global timer group and offers a custom 'timer' VTY command in a 'network' subnode below</span><br><span style="color: hsl(120, 100%, 40%);">+ * the CONFIG_NODE.</span><br><span style="color: hsl(120, 100%, 40%);">+ * the tdef_vty_test_config_subnode.vty transcript test.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef global_tdefs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+  { .T=1, .default_val=100, .desc="Testing a hundred seconds" },  // default is .unit=OSMO_TDEF_S == 0</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="Testing a hundred milliseconds" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=3, .default_val=100, .unit=OSMO_TDEF_M, .desc="Testing a hundred minutes" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="Testing a hundred potatoes" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=INT_MAX, .default_val=ULONG_MAX, .unit=OSMO_TDEF_M, .desc="Very large" },</span><br><span style="color: hsl(120, 100%, 40%);">+      { .T=-23, .default_val=-15, .desc="Negative T number" },</span><br><span style="color: hsl(120, 100%, 40%);">+    {}  //  <-- important! last entry shall be zero</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum tdef_vty_test_nodes {</span><br><span style="color: hsl(120, 100%, 40%);">+  GSMNET_NODE = _LAST_OSMOVTY_NODE + 1,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* This example offers 'timer T123' commands within an "unrelated" already existing subnode. */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct cmd_node gsmnet_node = {</span><br><span style="color: hsl(120, 100%, 40%);">+        GSMNET_NODE,</span><br><span style="color: hsl(120, 100%, 40%);">+  "%s(config-net)# ",</span><br><span style="color: hsl(120, 100%, 40%);">+ 1,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(show_timer, show_timer_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "show timer " OSMO_TDEF_VTY_ARG_T_OPTIONAL,</span><br><span style="color: hsl(120, 100%, 40%);">+      SHOW_STR "Show timers\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_TDEF_VTY_DOC_T)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *T_arg = argc > 0 ? argv[0] : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     return osmo_tdef_vty_show_cmd(vty, global_tdefs, T_arg, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_net_timer, cfg_net_timer_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "timer " OSMO_TDEF_VTY_ARG_SET_OPTIONAL,</span><br><span style="color: hsl(120, 100%, 40%);">+      "Configure or show timers\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_TDEF_VTY_DOC_SET)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If any arguments are missing, redirect to 'show' */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (argc < 2)</span><br><span style="color: hsl(120, 100%, 40%);">+              return show_timer(self, vty, argc, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+     return osmo_tdef_vty_set_cmd(vty, global_tdefs, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_net, cfg_net_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "network", "Enter network node\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     vty->node = GSMNET_NODE;</span><br><span style="color: hsl(120, 100%, 40%);">+   return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int config_write_gsmnet(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  vty_out(vty, "net%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* usually, here would be the output of any other 'net' config items... */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_tdef_vty_write(vty, global_tdefs, " timer ");</span><br><span style="color: hsl(120, 100%, 40%);">+  return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void gsmnet_init_vty()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    install_node(&gsmnet_node, config_write_gsmnet);</span><br><span style="color: hsl(120, 100%, 40%);">+  install_element(CONFIG_NODE, &cfg_net_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_tdefs_reset(global_tdefs);</span><br><span style="color: hsl(120, 100%, 40%);">+       install_element_ve(&show_timer_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+      install_element(GSMNET_NODE, &cfg_net_timer_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* ------------------- THE REST is just boilerplate osmo main() ------------------- */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *root_ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void print_help()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   printf( "options:\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                "  -h      --help          this text\n"</span><br><span style="color: hsl(120, 100%, 40%);">+             "  -d      --debug MASK    Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+           "  -D      --daemonize     For the process into a background daemon\n"</span><br><span style="color: hsl(120, 100%, 40%);">+              "  -c      --config-file   Specify the filename of the config file\n"</span><br><span style="color: hsl(120, 100%, 40%);">+               "  -s      --disable-color Don't use colors in stderr log output\n"</span><br><span style="color: hsl(120, 100%, 40%);">+             "  -T      --timestamp     Prefix every log line with a timestamp\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                "  -V      --version       Print version information and exit\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            "  -e      --log-level     Set a global log-level\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                );</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct {</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *config_file;</span><br><span style="color: hsl(120, 100%, 40%);">+      int daemonize;</span><br><span style="color: hsl(120, 100%, 40%);">+} cmdline_config = {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_options(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+           int option_idx = 0, c;</span><br><span style="color: hsl(120, 100%, 40%);">+                static const struct option long_options[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "help", 0, 0, 'h' },</span><br><span style="color: hsl(120, 100%, 40%);">+                      { "debug", 1, 0, 'd' },</span><br><span style="color: hsl(120, 100%, 40%);">+                     { "daemonize", 0, 0, 'D' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "config-file", 1, 0, 'c' },</span><br><span style="color: hsl(120, 100%, 40%);">+                       { "disable-color", 0, 0, 's' },</span><br><span style="color: hsl(120, 100%, 40%);">+                     { "timestamp", 0, 0, 'T' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "version", 0, 0, 'V' },</span><br><span style="color: hsl(120, 100%, 40%);">+                   { "log-level", 1, 0, 'e' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 {}</span><br><span style="color: hsl(120, 100%, 40%);">+            };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          c = getopt_long(argc, argv, "hc:d:Dc:sTVe:",</span><br><span style="color: hsl(120, 100%, 40%);">+                                long_options, &option_idx);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (c == -1)</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              switch (c) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case 'h':</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_help();</span><br><span style="color: hsl(120, 100%, 40%);">+                 exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+              case 's':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'd':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_parse_category_mask(osmo_stderr_target, optarg);</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'D':</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmdline_config.daemonize = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'c':</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmdline_config.config_file = optarg;</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'T':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_print_timestamp(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+                       break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'e':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_log_level(osmo_stderr_target, atoi(optarg));</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'V':</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_version(1);</span><br><span style="color: hsl(120, 100%, 40%);">+                     exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* catch unknown options *as well as* missing arguments. */</span><br><span style="color: hsl(120, 100%, 40%);">+                   fprintf(stderr, "Error in command line options. Exiting.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                       exit(-1);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int quit = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void signal_handler(int signal)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     fprintf(stdout, "signal %u received\n", signal);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (signal) {</span><br><span style="color: hsl(120, 100%, 40%);">+     case SIGINT:</span><br><span style="color: hsl(120, 100%, 40%);">+  case SIGTERM:</span><br><span style="color: hsl(120, 100%, 40%);">+         quit++;</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case SIGABRT:</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_generate_backtrace();</span><br><span style="color: hsl(120, 100%, 40%);">+            /* in case of abort, we want to obtain a talloc report</span><br><span style="color: hsl(120, 100%, 40%);">+                 * and then return to the caller, who will abort the process */</span><br><span style="color: hsl(120, 100%, 40%);">+       case SIGUSR1:</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_report(tall_vty_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_report_full(root_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case SIGUSR2:</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_report_full(tall_vty_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct vty_app_info vty_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .name           = "tdef_vty_test",</span><br><span style="color: hsl(120, 100%, 40%);">+  .version        = PACKAGE_VERSION,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct log_info_cat default_categories[] = {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct log_info log_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .cat = default_categories,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_cat = ARRAY_SIZE(default_categories),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     root_ctx = talloc_named_const(NULL, 0, "tdef_vty_test");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_init_logging2(root_ctx, &log_info);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        vty_info.tall_ctx = root_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_init(&vty_info);</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_talloc_vty_add_cmds();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ gsmnet_init_vty(); /* <--- relevant init for this example */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     handle_options(argc, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmdline_config.config_file) {</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = vty_read_config_file(cmdline_config.config_file, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      fprintf(stderr, "Failed to parse the config file: '%s'\n", cmdline_config.config_file);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   signal(SIGINT, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+  signal(SIGTERM, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGABRT, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGUSR1, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGUSR2, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_init_ignore_signals();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmdline_config.daemonize) {</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = osmo_daemonize();</span><br><span style="color: hsl(120, 100%, 40%);">+                if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      perror("Error during daemonize");</span><br><span style="color: hsl(120, 100%, 40%);">+                   return 6;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while (!quit) {</span><br><span style="color: hsl(120, 100%, 40%);">+               log_reset_context();</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_select_main(0);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(root_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(tall_vty_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/tdef/tdef_vty_test_config_subnode.vty b/tests/tdef/tdef_vty_test_config_subnode.vty</span><br><span>new file mode 100644</span><br><span>index 0000000..6cfd3bf</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_vty_test_config_subnode.vty</span><br><span>@@ -0,0 +1,107 @@</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> list</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+  show timer [TNNNN]</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> show timer ?</span><br><span style="color: hsl(120, 100%, 40%);">+  [TNNNN]  T-number, optionally preceded by 't' or 'T'.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> show timer</span><br><span style="color: hsl(120, 100%, 40%);">+T1 = 100 s      Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+T2 = 100 ms Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+T3 = 100 m    Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T4 = 100    Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483647 = 18446744073709551615 m Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T-23 = 18446744073709551601 s     Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> enable</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# show timer</span><br><span style="color: hsl(120, 100%, 40%);">+T1 = 100 s  Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+T2 = 100 ms Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+T3 = 100 m    Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T4 = 100    Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483647 = 18446744073709551615 m Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T-23 = 18446744073709551601 s     Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# configure terminal</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# network</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer</span><br><span style="color: hsl(120, 100%, 40%);">+T1 = 100 s      Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+T2 = 100 ms Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+T3 = 100 m    Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T4 = 100    Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483647 = 18446744073709551615 m Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T-23 = 18446744073709551601 s     Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer T3</span><br><span style="color: hsl(120, 100%, 40%);">+T3 = 100 m  Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer 3</span><br><span style="color: hsl(120, 100%, 40%);">+T3 = 100 m      Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer t3</span><br><span style="color: hsl(120, 100%, 40%);">+T3 = 100 m     Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T1 5</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T1</span><br><span style="color: hsl(120, 100%, 40%);">+T1 = 5 s      Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer t1 678</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T1</span><br><span style="color: hsl(120, 100%, 40%);">+T1 = 678 s  Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer 1 9012345</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T1</span><br><span style="color: hsl(120, 100%, 40%);">+T1 = 9012345 s   Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer T666</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T666</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer t666</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T666</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer 666</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T666</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T666</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T666</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer t666</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T666</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer 666</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T666</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T666 5</span><br><span style="color: hsl(120, 100%, 40%);">+% No such timer: T666</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T-23 42</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T-23</span><br><span style="color: hsl(120, 100%, 40%);">+T-23 = 42 s      Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer t-23 43</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T-23</span><br><span style="color: hsl(120, 100%, 40%);">+T-23 = 43 s     Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer -23 44</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T-23</span><br><span style="color: hsl(120, 100%, 40%);">+T-23 = 44 s      Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# do show timer</span><br><span style="color: hsl(120, 100%, 40%);">+T1 = 9012345 s Testing a hundred seconds (default: 100 s)</span><br><span style="color: hsl(120, 100%, 40%);">+T2 = 100 ms Testing a hundred milliseconds (default: 100 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+T3 = 100 m    Testing a hundred minutes (default: 100 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T4 = 100    Testing a hundred potatoes (default: 100)</span><br><span style="color: hsl(120, 100%, 40%);">+T2147483647 = 18446744073709551615 m Very large (default: 18446744073709551615 m)</span><br><span style="color: hsl(120, 100%, 40%);">+T-23 = 44 s       Negative T number (default: 18446744073709551601 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+net</span><br><span style="color: hsl(120, 100%, 40%);">+ timer T1 9012345</span><br><span style="color: hsl(120, 100%, 40%);">+ timer T-23 44</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T1 default</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# timer T-23 default</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-net)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span>diff --git a/tests/tdef/tdef_vty_test_dynamic.c b/tests/tdef/tdef_vty_test_dynamic.c</span><br><span>new file mode 100644</span><br><span>index 0000000..20dae53</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_vty_test_dynamic.c</span><br><span>@@ -0,0 +1,362 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Test implementation for osmo_tdef VTY configuration API. */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define _GNU_SOURCE</span><br><span style="color: hsl(120, 100%, 40%);">+#include <getopt.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <signal.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/misc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/telnet_interface.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/tdef_vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "config.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *root_ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* ------------------- HERE IS THE INTERESTING TDEF RELEVANT PART ------------------- */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* This example keeps a separate list of timers for each instance of a dynamically allocated instance of a VTY node,</span><br><span style="color: hsl(120, 100%, 40%);">+ * for example of keeping separate timers for each BTS in a BSC.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_tdef bts_default_tdefs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=1111, .default_val=2, .desc="Dynamic Duo" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { .T=2222, .default_val=1, .desc="BATMAN" },</span><br><span style="color: hsl(120, 100%, 40%);">+        { .T=3333, .default_val=12, .desc="Dadadadadadadadadadadada" },</span><br><span style="color: hsl(120, 100%, 40%);">+     { .T=4444, .default_val=500, .unit=OSMO_TDEF_MS, .desc="POW!" },</span><br><span style="color: hsl(120, 100%, 40%);">+    {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Boilerplate dynamic VTY node ... */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum tdef_vty_test_nodes {</span><br><span style="color: hsl(120, 100%, 40%);">+      MEMBER_NODE = _LAST_OSMOVTY_NODE + 1,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct cmd_node member_node = {</span><br><span style="color: hsl(120, 100%, 40%);">+   MEMBER_NODE,</span><br><span style="color: hsl(120, 100%, 40%);">+  "%s(config-member)# ",</span><br><span style="color: hsl(120, 100%, 40%);">+      1,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct member {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      char name[23];</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_tdef *tdefs;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+LLIST_HEAD(all_members);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct member *member_alloc(const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct member *m = talloc_zero(root_ctx, struct member);</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_strlcpy(m->name, name, sizeof(m->name));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* DYNAMIC TDEF COPIES */</span><br><span style="color: hsl(120, 100%, 40%);">+     m->tdefs = (struct osmo_tdef*)talloc_size(m, sizeof(bts_default_tdefs));</span><br><span style="color: hsl(120, 100%, 40%);">+   memcpy((char*)m->tdefs, (char*)&bts_default_tdefs, sizeof(bts_default_tdefs));</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_tdefs_reset(m->tdefs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_add_tail(&m->entry, &all_members);</span><br><span style="color: hsl(120, 100%, 40%);">+   return m;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct member *member_find(const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct member *m;</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(m, &all_members, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!strcmp(m->name, name))</span><br><span style="color: hsl(120, 100%, 40%);">+                        return m;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_member, cfg_member_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "member NAME",</span><br><span style="color: hsl(120, 100%, 40%);">+      "Enter member node\n" "Existing or new member node name\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *name = argv[0];</span><br><span style="color: hsl(120, 100%, 40%);">+   struct member *m = member_find(name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!m)</span><br><span style="color: hsl(120, 100%, 40%);">+               m = member_alloc(name);</span><br><span style="color: hsl(120, 100%, 40%);">+       vty->index = m;</span><br><span style="color: hsl(120, 100%, 40%);">+    vty->node = MEMBER_NODE;</span><br><span style="color: hsl(120, 100%, 40%);">+   return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* TDEF SPECIFIC VTY */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static bool startswith(const char *str, const char *startswith_str)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!startswith_str)</span><br><span style="color: hsl(120, 100%, 40%);">+          return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!str)</span><br><span style="color: hsl(120, 100%, 40%);">+             return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ return strncmp(str, startswith_str, strlen(startswith_str)) == 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(show_timer, show_member_timer_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "show member-timer [NAME] " OSMO_TDEF_VTY_ARG_T_OPTIONAL,</span><br><span style="color: hsl(120, 100%, 40%);">+      SHOW_STR "Show timers for a specific member" "member name\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_TDEF_VTY_DOC_T)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *name = argc > 0 ? argv[0] : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct member *m;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *T_arg = argc > 1 ? argv[1] : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     int shown = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_for_each_entry(m, &all_members, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!name || startswith(m->name, name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_tdef_vty_show_cmd(vty, m->tdefs, T_arg, "%11s: ", m->name);</span><br><span style="color: hsl(120, 100%, 40%);">+                      shown ++;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!shown) {</span><br><span style="color: hsl(120, 100%, 40%);">+         vty_out(vty, "%% No such member: %s%s", name ? : "(none)", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+          return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_member_timer, cfg_member_timer_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "timer " OSMO_TDEF_VTY_ARG_SET_OPTIONAL,</span><br><span style="color: hsl(120, 100%, 40%);">+      "Configure or show timers for this member\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_TDEF_VTY_DOC_SET)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct member *m = vty->index;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!m || !m->tdefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "%% No timers here%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+           return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If any arguments are missing, redirect to 'show' */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (argc < 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+            const char *T_arg = argc > 0 ? argv[0] : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+             return osmo_tdef_vty_show_cmd(vty, m->tdefs, T_arg, "%11s: ", m->name);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return osmo_tdef_vty_set_cmd(vty, m->tdefs, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int config_write_member(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct member *m;</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(m, &all_members, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+            vty_out(vty, "member %s%s", m->name, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_tdef_vty_write(vty, m->tdefs, " timer ");</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void member_init_vty()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    install_node(&member_node, config_write_member);</span><br><span style="color: hsl(120, 100%, 40%);">+  install_element(CONFIG_NODE, &cfg_member_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  install_element_ve(&show_member_timer_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+       install_element(MEMBER_NODE, &cfg_member_timer_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* ------------------- THE REST is just boilerplate osmo main() ------------------- */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void print_help()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  printf( "options:\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                "  -h      --help          this text\n"</span><br><span style="color: hsl(120, 100%, 40%);">+             "  -d      --debug MASK    Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+           "  -D      --daemonize     For the process into a background daemon\n"</span><br><span style="color: hsl(120, 100%, 40%);">+              "  -c      --config-file   Specify the filename of the config file\n"</span><br><span style="color: hsl(120, 100%, 40%);">+               "  -s      --disable-color Don't use colors in stderr log output\n"</span><br><span style="color: hsl(120, 100%, 40%);">+             "  -T      --timestamp     Prefix every log line with a timestamp\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                "  -V      --version       Print version information and exit\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            "  -e      --log-level     Set a global log-level\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                );</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct {</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *config_file;</span><br><span style="color: hsl(120, 100%, 40%);">+      int daemonize;</span><br><span style="color: hsl(120, 100%, 40%);">+} cmdline_config = {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_options(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+           int option_idx = 0, c;</span><br><span style="color: hsl(120, 100%, 40%);">+                static const struct option long_options[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "help", 0, 0, 'h' },</span><br><span style="color: hsl(120, 100%, 40%);">+                      { "debug", 1, 0, 'd' },</span><br><span style="color: hsl(120, 100%, 40%);">+                     { "daemonize", 0, 0, 'D' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "config-file", 1, 0, 'c' },</span><br><span style="color: hsl(120, 100%, 40%);">+                       { "disable-color", 0, 0, 's' },</span><br><span style="color: hsl(120, 100%, 40%);">+                     { "timestamp", 0, 0, 'T' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 { "version", 0, 0, 'V' },</span><br><span style="color: hsl(120, 100%, 40%);">+                   { "log-level", 1, 0, 'e' },</span><br><span style="color: hsl(120, 100%, 40%);">+                 {}</span><br><span style="color: hsl(120, 100%, 40%);">+            };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          c = getopt_long(argc, argv, "hc:d:Dc:sTVe:",</span><br><span style="color: hsl(120, 100%, 40%);">+                                long_options, &option_idx);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (c == -1)</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              switch (c) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case 'h':</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_help();</span><br><span style="color: hsl(120, 100%, 40%);">+                 exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+              case 's':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'd':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_parse_category_mask(osmo_stderr_target, optarg);</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'D':</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmdline_config.daemonize = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'c':</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmdline_config.config_file = optarg;</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'T':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_print_timestamp(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+                       break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'e':</span><br><span style="color: hsl(120, 100%, 40%);">+                     log_set_log_level(osmo_stderr_target, atoi(optarg));</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case 'V':</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_version(1);</span><br><span style="color: hsl(120, 100%, 40%);">+                     exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* catch unknown options *as well as* missing arguments. */</span><br><span style="color: hsl(120, 100%, 40%);">+                   fprintf(stderr, "Error in command line options. Exiting.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                       exit(-1);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int quit = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void signal_handler(int signal)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     fprintf(stdout, "signal %u received\n", signal);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (signal) {</span><br><span style="color: hsl(120, 100%, 40%);">+     case SIGINT:</span><br><span style="color: hsl(120, 100%, 40%);">+  case SIGTERM:</span><br><span style="color: hsl(120, 100%, 40%);">+         quit++;</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case SIGABRT:</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_generate_backtrace();</span><br><span style="color: hsl(120, 100%, 40%);">+            /* in case of abort, we want to obtain a talloc report</span><br><span style="color: hsl(120, 100%, 40%);">+                 * and then return to the caller, who will abort the process */</span><br><span style="color: hsl(120, 100%, 40%);">+       case SIGUSR1:</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_report(tall_vty_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_report_full(root_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case SIGUSR2:</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_report_full(tall_vty_ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct vty_app_info vty_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .name           = "tdef_vty_test",</span><br><span style="color: hsl(120, 100%, 40%);">+  .version        = PACKAGE_VERSION,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct log_info_cat default_categories[] = {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct log_info log_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .cat = default_categories,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_cat = ARRAY_SIZE(default_categories),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     root_ctx = talloc_named_const(NULL, 0, "tdef_vty_test");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_init_logging2(root_ctx, &log_info);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        vty_info.tall_ctx = root_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_init(&vty_info);</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_talloc_vty_add_cmds();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ member_init_vty(); /* <--- relevant init for this example */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     handle_options(argc, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmdline_config.config_file) {</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = vty_read_config_file(cmdline_config.config_file, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      fprintf(stderr, "Failed to parse the config file: '%s'\n", cmdline_config.config_file);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   signal(SIGINT, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+  signal(SIGTERM, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGABRT, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGUSR1, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ signal(SIGUSR2, &signal_handler);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_init_ignore_signals();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmdline_config.daemonize) {</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = osmo_daemonize();</span><br><span style="color: hsl(120, 100%, 40%);">+                if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      perror("Error during daemonize");</span><br><span style="color: hsl(120, 100%, 40%);">+                   return 6;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while (!quit) {</span><br><span style="color: hsl(120, 100%, 40%);">+               log_reset_context();</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_select_main(0);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(root_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(tall_vty_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/tdef/tdef_vty_test_dynamic.vty b/tests/tdef/tdef_vty_test_dynamic.vty</span><br><span>new file mode 100644</span><br><span>index 0000000..6aae746</span><br><span>--- /dev/null</span><br><span>+++ b/tests/tdef/tdef_vty_test_dynamic.vty</span><br><span>@@ -0,0 +1,83 @@</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> list</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+  show member-timer [NAME] [TNNNN]</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test> enable</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# configure terminal</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# member robin</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# timer</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T1111 = 2 s      Dynamic Duo (default: 2 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T2222 = 1 s    BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T3333 = 12 s        Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T4444 = 500 ms   POW! (default: 500 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# timer T2222 423</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# timer T2222</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T2222 = 423 s  BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# timer</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T1111 = 2 s  Dynamic Duo (default: 2 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T2222 = 423 s  BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T3333 = 12 s        Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T4444 = 500 ms   POW! (default: 500 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# do show member-timer</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T1111 = 2 s  Dynamic Duo (default: 2 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T2222 = 423 s  BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T3333 = 12 s        Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T4444 = 500 ms   POW! (default: 500 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# exit</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# member batman</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# timer 3333 17</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# timer 3333</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T3333 = 17 s  Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Current configuration:</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+member robin</span><br><span style="color: hsl(120, 100%, 40%);">+ timer T2222 423</span><br><span style="color: hsl(120, 100%, 40%);">+member batman</span><br><span style="color: hsl(120, 100%, 40%);">+ timer T3333 17</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# timer 3333 default</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+member robin</span><br><span style="color: hsl(120, 100%, 40%);">+ timer T2222 423</span><br><span style="color: hsl(120, 100%, 40%);">+member batman</span><br><span style="color: hsl(120, 100%, 40%);">+... !timer</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config-member)# exit</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test(config)# exit</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# show member-timer</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T1111 = 2 s       Dynamic Duo (default: 2 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T2222 = 423 s  BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T3333 = 12 s        Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T4444 = 500 ms   POW! (default: 500 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T1111 = 2 s        Dynamic Duo (default: 2 s)</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T2222 = 1 s    BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T3333 = 12 s        Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T4444 = 500 ms   POW! (default: 500 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# show member-timer batman</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T1111 = 2 s     Dynamic Duo (default: 2 s)</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T2222 = 1 s    BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T3333 = 12 s        Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+     batman: T4444 = 500 ms   POW! (default: 500 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# show member-timer robin</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T1111 = 2 s      Dynamic Duo (default: 2 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T2222 = 423 s  BATMAN (default: 1 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T3333 = 12 s        Dadadadadadadadadadadada (default: 12 s)</span><br><span style="color: hsl(120, 100%, 40%);">+      robin: T4444 = 500 ms   POW! (default: 500 ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tdef_vty_test# show member-timer joker</span><br><span style="color: hsl(120, 100%, 40%);">+% No such member: joker</span><br><span>diff --git a/tests/testsuite.at b/tests/testsuite.at</span><br><span>index 6aaaa78..0093403 100644</span><br><span>--- a/tests/testsuite.at</span><br><span>+++ b/tests/testsuite.at</span><br><span>@@ -325,3 +325,9 @@</span><br><span> cat $abs_srcdir/gsm23003/gsm23003_test.ok > expout</span><br><span> AT_CHECK([$abs_top_builddir/tests/gsm23003/gsm23003_test], [0], [expout], [ignore])</span><br><span> AT_CLEANUP</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AT_SETUP([tdef])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_KEYWORDS([tdef])</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/tdef/tdef_test.ok > expout</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CHECK([$abs_top_builddir/tests/tdef/tdef_test], [0], [expout], [ignore])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CLEANUP</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12717">change 12717</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/12717"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ibd6b1ed7f1bd6e1f2e0fde53352055a4468f23e5 </div>
<div style="display:none"> Gerrit-Change-Number: 12717 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>