Back to home page

OSCL-LXR

 
 

    


0001 #!/usr/bin/gawk -f
0002 # SPDX-License-Identifier: GPL-2.0
0003 
0004 # Script to check sysctl documentation against source files
0005 #
0006 # Copyright (c) 2020 Stephen Kitt
0007 
0008 # Example invocation:
0009 #       scripts/check-sysctl-docs -vtable="kernel" \
0010 #               Documentation/admin-guide/sysctl/kernel.rst \
0011 #               $(git grep -l register_sysctl_)
0012 #
0013 # Specify -vdebug=1 to see debugging information
0014 
0015 BEGIN {
0016     if (!table) {
0017         print "Please specify the table to look for using the table variable" > "/dev/stderr"
0018         exit 1
0019     }
0020 }
0021 
0022 # The following globals are used:
0023 # children: maps ctl_table names and procnames to child ctl_table names
0024 # documented: maps documented entries (each key is an entry)
0025 # entries: maps ctl_table names and procnames to counts (so
0026 #          enumerating the subkeys for a given ctl_table lists its
0027 #          procnames)
0028 # files: maps procnames to source file names
0029 # paths: maps ctl_path names to paths
0030 # curpath: the name of the current ctl_path struct
0031 # curtable: the name of the current ctl_table struct
0032 # curentry: the name of the current proc entry (procname when parsing
0033 #           a ctl_table, constructed path when parsing a ctl_path)
0034 
0035 
0036 # Remove punctuation from the given value
0037 function trimpunct(value) {
0038     while (value ~ /^["&]/) {
0039         value = substr(value, 2)
0040     }
0041     while (value ~ /[]["&,}]$/) {
0042         value = substr(value, 1, length(value) - 1)
0043     }
0044     return value
0045 }
0046 
0047 # Print the information for the given entry
0048 function printentry(entry) {
0049     seen[entry]++
0050     printf "* %s from %s", entry, file[entry]
0051     if (documented[entry]) {
0052         printf " (documented)"
0053     }
0054     print ""
0055 }
0056 
0057 
0058 # Stage 1: build the list of documented entries
0059 FNR == NR && /^=+$/ {
0060     if (prevline ~ /Documentation for/) {
0061         # This is the main title
0062         next
0063     }
0064 
0065     # The previous line is a section title, parse it
0066     $0 = prevline
0067     if (debug) print "Parsing " $0
0068     inbrackets = 0
0069     for (i = 1; i <= NF; i++) {
0070         if (length($i) == 0) {
0071             continue
0072         }
0073         if (!inbrackets && substr($i, 1, 1) == "(") {
0074             inbrackets = 1
0075         }
0076         if (!inbrackets) {
0077             token = trimpunct($i)
0078             if (length(token) > 0 && token != "and") {
0079                 if (debug) print trimpunct($i)
0080                 documented[trimpunct($i)]++
0081             }
0082         }
0083         if (inbrackets && substr($i, length($i), 1) == ")") {
0084             inbrackets = 0
0085         }
0086     }
0087 }
0088 
0089 FNR == NR {
0090     prevline = $0
0091     next
0092 }
0093 
0094 
0095 # Stage 2: process each file and find all sysctl tables
0096 BEGINFILE {
0097     delete children
0098     delete entries
0099     delete paths
0100     curpath = ""
0101     curtable = ""
0102     curentry = ""
0103     if (debug) print "Processing file " FILENAME
0104 }
0105 
0106 /^static struct ctl_path/ {
0107     match($0, /static struct ctl_path ([^][]+)/, tables)
0108     curpath = tables[1]
0109     if (debug) print "Processing path " curpath
0110 }
0111 
0112 /^static struct ctl_table/ {
0113     match($0, /static struct ctl_table ([^][]+)/, tables)
0114     curtable = tables[1]
0115     if (debug) print "Processing table " curtable
0116 }
0117 
0118 /^};$/ {
0119     curpath = ""
0120     curtable = ""
0121     curentry = ""
0122 }
0123 
0124 curpath && /\.procname[\t ]*=[\t ]*".+"/ {
0125     match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
0126     if (curentry) {
0127         curentry = curentry "/" names[1]
0128     } else {
0129         curentry = names[1]
0130     }
0131     if (debug) print "Setting path " curpath " to " curentry
0132     paths[curpath] = curentry
0133 }
0134 
0135 curtable && /\.procname[\t ]*=[\t ]*".+"/ {
0136     match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
0137     curentry = names[1]
0138     if (debug) print "Adding entry " curentry " to table " curtable
0139     entries[curtable][curentry]++
0140     file[curentry] = FILENAME
0141 }
0142 
0143 /\.child[\t ]*=/ {
0144     child = trimpunct($NF)
0145     if (debug) print "Linking child " child " to table " curtable " entry " curentry
0146     children[curtable][curentry] = child
0147 }
0148 
0149 /register_sysctl_table\(.*\)/ {
0150     match($0, /register_sysctl_table\(([^)]+)\)/, tables)
0151     if (debug) print "Registering table " tables[1]
0152     if (children[tables[1]][table]) {
0153         for (entry in entries[children[tables[1]][table]]) {
0154             printentry(entry)
0155         }
0156     }
0157 }
0158 
0159 /register_sysctl_paths\(.*\)/ {
0160     match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables)
0161     if (debug) print "Attaching table " tables[2] " to path " tables[1]
0162     if (paths[tables[1]] == table) {
0163         for (entry in entries[tables[2]]) {
0164             printentry(entry)
0165         }
0166     }
0167     split(paths[tables[1]], components, "/")
0168     if (length(components) > 1 && components[1] == table) {
0169         # Count the first subdirectory as seen
0170         seen[components[2]]++
0171     }
0172 }
0173 
0174 
0175 END {
0176     for (entry in documented) {
0177         if (!seen[entry]) {
0178             print "No implementation for " entry
0179         }
0180     }
0181 }