lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Wed, 13 Sep 2023 17:28:54 +0800
From: Hangbin Liu <liuhangbin@...il.com>
To: netdev@...r.kernel.org
Cc: "David S . Miller" <davem@...emloft.net>,
	David Ahern <dsahern@...nel.org>,
	Eric Dumazet <edumazet@...gle.com>,
	Jakub Kicinski <kuba@...nel.org>,
	Paolo Abeni <pabeni@...hat.com>,
	Ido Schimmel <idosch@...sch.org>,
	Nikolay Aleksandrov <razor@...ckwall.org>,
	Roopa Prabhu <roopa@...dia.com>,
	Stephen Hemminger <stephen@...workplumber.org>,
	Hangbin Liu <liuhangbin@...il.com>
Subject: [RFC Draft PATCH iproute2-next] tools: add a tool to generate bridge man doc

This tool will generate the ip link bridge parameters and find related
attr description in if_link.h. And convert to the new man doc.

To use it. You need to run the script first before your patch. With this
we can update the man page to latest version. e.g.

./generate_bridge_man.py -i iproute2_dir -n net-next_dir

The script will generate a ip-link.8.out page. You can check if it's OK
to move to ip-link.8.in.

After adding new parameter. You need to re-run the script to generate the man
doc from net-next header file.

The script check the existing parameter. So if the attr in net-next has not
added to iproute2, it will not generate the man doc.

Signed-off-by: Hangbin Liu <liuhangbin@...il.com>
---
 man/man8/ip-link.8.in        |   2 +
 tools/generate_bridge_man.py | 188 +++++++++++++++++++++++++++++++++++
 2 files changed, 190 insertions(+)
 create mode 100755 tools/generate_bridge_man.py

diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
index 8f07de9a8a25..cbf6d9a9d9c4 100644
--- a/man/man8/ip-link.8.in
+++ b/man/man8/ip-link.8.in
@@ -1611,6 +1611,7 @@ For a link of type
 the following additional arguments are supported:
 
 .BI "ip link add " DEVICE " type bridge "
+.\" bridge man doc starts
 [
 .BI ageing_time " AGEING_TIME "
 ] [
@@ -1887,6 +1888,7 @@ arptables hooks on the bridge.
 
 
 .in -8
+.\" bridge man doc ends
 
 .TP
 MACsec Type Support
diff --git a/tools/generate_bridge_man.py b/tools/generate_bridge_man.py
new file mode 100755
index 000000000000..45947abda382
--- /dev/null
+++ b/tools/generate_bridge_man.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+
+import re, sys, os
+import argparse
+from docutils import core
+
+man_bridge = ""
+
+def handle_args():
+    parser = argparse.ArgumentParser(description="""Convert bridge header
+                                     comments to iproute2 man doc.""")
+    parser.add_argument('-i', '--iproute2-dir', required=True,
+                        help='iproute code path')
+    parser.add_argument('-n', '--net-dir', required=True,
+                        help='net-next code path')
+    args = parser.parse_args()
+    return args
+
+def write_man_doc(input_file, output_file):
+    try:
+        with open(input_file, 'r') as infile, open(output_file, 'w') as outfile:
+            remove_flag = False
+            for line in infile:
+                if line.find("bridge man doc starts") != -1:
+                    remove_flag = True
+                    outfile.write(line)
+                    outfile.write(man_bridge + '\n')
+                elif remove_flag and line.find("bridge man doc ends") != -1:
+                    remove_flag = False
+                elif not remove_flag:
+                    outfile.write(line)
+    except Exception as e:
+        sys.exit(e)
+
+def append_man_line(line, end = '\n'):
+    global man_bridge
+    man_bridge = man_bridge + line + end
+
+def rst2man(node):
+    if not hasattr(node, 'astext'):
+        return
+
+    if node.tagname == 'bullet_list':
+        append_man_line('.in +8\n.sp')
+    if node.tagname == 'list_item':
+        append_man_line('')
+    elif node.tagname == 'emphasis':
+        append_man_line('.I ', end = '')
+    elif node.tagname == 'strong':
+        append_man_line('.B ', end = '')
+
+    for child_node in node.children:
+        rst2man(child_node)
+
+    if len(node.children) == 0:
+        text = node.astext().strip()
+        append_man_line(text)
+
+    if node.tagname == 'bullet_list':
+        append_man_line('.in -8')
+
+def convert2man(params):
+    first = True
+    for p in params:
+        if first:
+            first = False
+            append_man_line('[')
+        else:
+            append_man_line(' [')
+        append_man_line('.BI ' + p + ' " ' + params[p]['param'] + ' "')
+        append_man_line(']', end = '')
+
+    append_man_line('\n\n.in +8\n.sp')
+
+    for p in params:
+        if 'alias' in params[p]:
+            # TODO: fix the vlan_protocol output, e.g. {|}
+            append_man_line('\n.BR ' + p + ' " ' + params[p]['alias'] + ' "')
+        else:
+            append_man_line('\n.BI ' + p + ' " ' + params[p]['param'] + ' "')
+        # Add the '- ' at begining of each description
+        append_man_line('- ', end = '')
+        if 'doc' in params[p]:
+            rst_nodes = core.publish_doctree(params[p]['doc'])
+            rst2man(rst_nodes)
+
+def strip_doc_line(line, param):
+    # Remove the first " *   "
+    line = line[5:-1]
+    # replace attr with param, e.g. IFLA_BR_STP_STATE -> STP_STATE
+    line = line.replace(param['attr'], param['param'])
+    return line
+
+def get_attr(line):
+    match = re.search(r'@(\w+):', line)
+    if match:
+        return match.group(1)
+    return None
+
+# Deal some irregular namings
+def param2attr(param):
+    param = param.replace('count', 'cnt')
+    param = param.replace('interval', 'intvl')
+    param = param.replace('group_address', 'group_addr')
+    return param.upper()
+
+def get_bridge_doc(bridge_header, params):
+    find_attr = False
+    p = None
+    try:
+        with open(bridge_header, 'r') as f:
+            line = f.readline()
+            # Find the start of the doc
+            while line.find("DOC: The bridge emum defination") == -1:
+                if not line:
+                    print("Unable to find bridge DOC");
+                    return None
+                line = f.readline()
+            # Till the end of the doc
+            while line.find("*/") == -1:
+                line = f.readline()
+                # Start of a parameter
+                if line.find(' * @') == 0:
+                    find_attr = False
+                    for p in params:
+                        if line.find(param2attr(p) + ':') != -1:
+                            attr = get_attr(line)
+                            if attr is not None:
+                                params[p]['attr'] = attr
+                                find_attr = True
+                            break
+                elif find_attr and p:
+                    if 'doc' in params[p]:
+                        params[p]['doc'] = params[p]['doc'] + '\n' + strip_doc_line(line, params[p])
+                    else:
+                        params[p]['doc'] = strip_doc_line(line, params[p])
+
+    except Exception as e:
+        sys.exit(e)
+
+# replace multi text
+def strip_line(line):
+    return line.replace(' ', '').replace('"', '').replace('\\n', '').strip()
+
+def get_bridge_parameter(bridge_file):
+    params = {}
+    try:
+        with open(bridge_file, 'r') as f:
+            line = f.readline()
+            # Find the start of parameters
+            while line.find("Usage: ... bridge") == -1:
+                line = f.readline()
+            # Till the end of the parameters
+            while line.find("Where:") == -1:
+                line = f.readline()
+                parts = line.strip().split()
+                if len(parts) >= 4:
+                    if parts[1] == '[':
+                        params[parts[2]] = {}
+                        params[parts[2]]['param'] = parts[3]
+            # Till the end of usage function to get the alias
+            while line.find(");") == -1:
+                parts = line.strip().replace('Where:', '').split(':=')
+                alias = strip_line(parts[0])
+                for p in params:
+                    if params[p]['param'] == alias:
+                        params[p]['alias'] = strip_line(parts[1])
+                line = f.readline()
+    except Exception as e:
+        sys.exit(e)
+
+    return params
+
+def main():
+    args = handle_args()
+    bridge_file = args.iproute2_dir.rstrip('/') + "/ip/iplink_bridge.c"
+    ip_link_in = args.iproute2_dir.rstrip('/') + "/man/man8/ip-link.8.in"
+    ip_link_out = args.iproute2_dir.rstrip('/') + "/man/man8/ip-link.8.out"
+    bridge_header = args.net_dir.rstrip('/') + '/include/uapi/linux/if_link.h'
+
+    bridge_params = get_bridge_parameter(bridge_file)
+    get_bridge_doc(bridge_header, bridge_params)
+    convert2man(bridge_params)
+    write_man_doc(ip_link_in, ip_link_out)
+
+if __name__ == '__main__':
+    main()
-- 
2.41.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ