<p>neels <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/18345">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, but someone else must approve
  pespin: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">enable vty xml dumping to stdout<br><br>Allow dumping the VTY XML reference (for generating manuals) to a normal FILE*<br>stream instead of a vty output buffer.<br><br>We currently generate the VTY reference by starting the client program,<br>connecting to the VTY telnet and dumping the reference. That is weirdly<br>convoluted, especially since there has to be a valid config file that<br>successfully starts up a minimal set of external links before the reference can<br>be generated. IMO we should have dumped the XML reference to stdout from the<br>start, and never to a VTY session.<br><br>With this patch, it is trivial to generate the XML VTY reference by a<br>commandline switch. The client program will set up the entire VTY, and<br>immediately after parsing the cmdline options but before parsing a config file,<br>just dumps the reference and doesn't even start establishing local ports. That<br>would allow generating the XML reference on the fly during the build process of<br>the manuals, without the need of a docker container or somesuch.<br><br>A first implementation of such a commandline switch is `osmo-bsc -X`, added in<br>I316efedb2c1652791434ecf14a1e261367cd2fb7<br><br>This patch jumps through a bit of a hoop to still allow dumping to a VTY buffer<br>without code dup, to still allow dumping the XML reference through telnet VTY,<br>until all our programs have implemented an -X switch (TM).<br><br>Change-Id: Ic74bbdb6dc5ea05f03c791cc70184861e39cd492<br>---<br>M include/osmocom/vty/command.h<br>M src/vty/command.c<br>2 files changed, 62 insertions(+), 25 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h</span><br><span>index d63dbde..88490f7 100644</span><br><span>--- a/include/osmocom/vty/command.h</span><br><span>+++ b/include/osmocom/vty/command.h</span><br><span>@@ -401,4 +401,6 @@</span><br><span> </span><br><span> extern void *tall_vty_cmd_ctx;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int vty_dump_xml_ref(FILE *stream);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span>diff --git a/src/vty/command.c b/src/vty/command.c</span><br><span>index daee5c5..9b32d22 100644</span><br><span>--- a/src/vty/command.c</span><br><span>+++ b/src/vty/command.c</span><br><span>@@ -619,15 +619,17 @@</span><br><span>  return out;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+typedef int (*print_func_t)(void *data, const char *fmt, ...);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*</span><br><span style="color: hsl(0, 100%, 40%);">- * Write one cmd_element as XML to the given VTY.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Write one cmd_element as XML via a print_func_t.</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, void *data, const char *newline)</span><br><span> {</span><br><span>  char *xml_string = xml_escape(cmd->string);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      vty_out(vty, "    <command id='%s'>%s", xml_string, VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-       vty_out(vty, "      <params>%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_func(data, "    <command id='%s'>%s", xml_string, newline);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_func(data, "      <params>%s", newline);</span><br><span> </span><br><span>   int j;</span><br><span>       for (j = 0; j < vector_count(cmd->strvec); ++j) {</span><br><span>@@ -641,15 +643,15 @@</span><br><span> </span><br><span>                  xml_param = xml_escape(desc->cmd);</span><br><span>                        xml_doc = xml_escape(desc->str);</span><br><span style="color: hsl(0, 100%, 40%);">-                     vty_out(vty, "        <param name='%s' doc='%s' />%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                          xml_param, xml_doc, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_func(data, "        <param name='%s' doc='%s' />%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                            xml_param, xml_doc, newline);</span><br><span>                        talloc_free(xml_param);</span><br><span>                      talloc_free(xml_doc);</span><br><span>                }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   vty_out(vty, "      </params>%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">- vty_out(vty, "    </command>%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_func(data, "      </params>%s", newline);</span><br><span style="color: hsl(120, 100%, 40%);">+       print_func(data, "    </command>%s", newline);</span><br><span> </span><br><span>   talloc_free(xml_string);</span><br><span>     return 0;</span><br><span>@@ -658,20 +660,20 @@</span><br><span> static bool vty_command_is_common(struct cmd_element *cmd);</span><br><span> </span><br><span> /*</span><br><span style="color: hsl(0, 100%, 40%);">- * Dump all nodes and commands associated with a given node as XML to the VTY.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Dump all nodes and commands associated with a given node as XML via a print_func_t.</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-static int vty_dump_nodes(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)</span><br><span> {</span><br><span>  int i, j;</span><br><span>    int same_name_count;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+      print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);</span><br><span> </span><br><span>         /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */</span><br><span style="color: hsl(0, 100%, 40%);">-       vty_out(vty, "  <node id='_common_cmds_'>%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-     vty_out(vty, "    <name>Common Commands</name>%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-  vty_out(vty, "    <description>These commands are available on all VTY nodes. They are listed"</span><br><span style="color: hsl(0, 100%, 40%);">-          " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_func(data, "  <node id='_common_cmds_'>%s", newline);</span><br><span style="color: hsl(120, 100%, 40%);">+   print_func(data, "    <name>Common Commands</name>%s", newline);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_func(data, "    <description>These commands are available on all VTY nodes. They are listed"</span><br><span style="color: hsl(120, 100%, 40%);">+            " here only once, to unclutter the VTY reference.</description>%s", newline);</span><br><span>        for (i = 0; i < vector_active(cmdvec); ++i) {</span><br><span>             struct cmd_node *cnode;</span><br><span>              cnode = vector_slot(cmdvec, i);</span><br><span>@@ -686,10 +688,10 @@</span><br><span>                      if (!vty_command_is_common(elem))</span><br><span>                            continue;</span><br><span>                    if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))</span><br><span style="color: hsl(0, 100%, 40%);">-                             vty_dump_element(elem, vty);</span><br><span style="color: hsl(120, 100%, 40%);">+                          vty_dump_element(elem, print_func, data, newline);</span><br><span>           }</span><br><span>    }</span><br><span style="color: hsl(0, 100%, 40%);">-       vty_out(vty, "  </node>%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_func(data, "  </node>%s", newline);</span><br><span> </span><br><span>        for (i = 0; i < vector_active(cmdvec); ++i) {</span><br><span>             struct cmd_node *cnode;</span><br><span>@@ -712,11 +714,11 @@</span><br><span>                              same_name_count ++;</span><br><span>          }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           vty_out(vty, "  <node id='%s", cnode->name);</span><br><span style="color: hsl(120, 100%, 40%);">+              print_func(data, "  <node id='%s", cnode->name);</span><br><span>                 if (same_name_count > 1 || !*cnode->name)</span><br><span style="color: hsl(0, 100%, 40%);">-                 vty_out(vty, "_%d", same_name_count);</span><br><span style="color: hsl(0, 100%, 40%);">-         vty_out(vty, "'>%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-             vty_out(vty, "    <name>%s</name>%s", cnode->name, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                     print_func(data, "_%d", same_name_count);</span><br><span style="color: hsl(120, 100%, 40%);">+           print_func(data, "'>%s", newline);</span><br><span style="color: hsl(120, 100%, 40%);">+           print_func(data, "    <name>%s</name>%s", cnode->name, newline);</span><br><span> </span><br><span>                for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {</span><br><span>                       struct cmd_element *elem;</span><br><span>@@ -724,17 +726,50 @@</span><br><span>                    if (vty_command_is_common(elem))</span><br><span>                             continue;</span><br><span>                    if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))</span><br><span style="color: hsl(0, 100%, 40%);">-                             vty_dump_element(elem, vty);</span><br><span style="color: hsl(120, 100%, 40%);">+                          vty_dump_element(elem, print_func, data, newline);</span><br><span>           }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           vty_out(vty, "  </node>%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+             print_func(data, "  </node>%s", newline);</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_func(data, "</vtydoc>%s", newline);</span><br><span> </span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int print_func_vty(void *data, const char *format, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct vty *vty = data;</span><br><span style="color: hsl(120, 100%, 40%);">+       va_list args;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       va_start(args, format);</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = vty_out_va(vty, format, args);</span><br><span style="color: hsl(120, 100%, 40%);">+   va_end(args);</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%);">+static int vty_dump_xml_ref_to_vty(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return vty_dump_nodes(print_func_vty, vty, 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%);">+static int print_func_stream(void *data, const char *format, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   va_list args;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       va_start(args, format);</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = vfprintf((FILE*)data, format, args);</span><br><span style="color: hsl(120, 100%, 40%);">+     va_end(args);</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%);">+/*! Print the XML reference of all VTY nodes to the given stream.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int vty_dump_xml_ref(FILE *stream)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        return vty_dump_nodes(print_func_stream, stream, "\n");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Check if a command with given string exists at given node */</span><br><span> static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)</span><br><span> {</span><br><span>@@ -2772,7 +2807,7 @@</span><br><span> DEFUN(show_online_help,</span><br><span>       show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       vty_dump_nodes(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+  vty_dump_xml_ref_to_vty(vty);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/18345">change 18345</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/c/libosmocore/+/18345"/><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-Change-Id: Ic74bbdb6dc5ea05f03c791cc70184861e39cd492 </div>
<div style="display:none"> Gerrit-Change-Number: 18345 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>