Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | #!/usr/bin/env -S gawk -f # SPDX-License-Identifier: GPL-2.0 # Script to check sysctl documentation against source files # # Copyright (c) 2020 Stephen Kitt # Example invocation: # scripts/check-sysctl-docs -vtable="kernel" \ # Documentation/admin-guide/sysctl/kernel.rst \ # $(git grep -l register_sysctl) # # Specify -vdebug=1 to see debugging information BEGIN { if (!table) { print "Please specify the table to look for using the table variable" > "/dev/stderr" exit 1 } # Documentation title skiplist skiplist[0] = "^Documentation for" skiplist[1] = "Network core options$" skiplist[2] = "POSIX message queues filesystem$" skiplist[3] = "Configuration options" skiplist[4] = ". /proc/sys/fs" skiplist[5] = "^Introduction$" skiplist[6] = "^seccomp$" skiplist[7] = "^pty$" skiplist[8] = "^firmware_config$" skiplist[9] = "^random$" } # The following globals are used: # documented: maps documented entries (each key is an entry) # entries: maps ctl_table names and procnames to counts (so # enumerating the subkeys for a given ctl_table lists its # procnames) # curtable: the name of the current ctl_table struct # curentry: the name of the current proc entry (procname when parsing # a ctl_table, constructed path when parsing a ctl_path) # Remove punctuation from the given value function trimpunct(value) { while (value ~ /^["&]/) { value = substr(value, 2) } while (value ~ /[]["&,}]$/) { value = substr(value, 1, length(value) - 1) } return value } # Print the information for the given entry function printentry(entry) { seen[entry]++ printf "* %s from %s", entry, file[entry] if (documented[entry]) { printf " (documented)" } print "" } # Stage 1: build the list of documented entries FNR == NR && /^=+$/ { for (i in skiplist) { if (prevline ~ skiplist[i]) { next } } # The previous line is a section title, parse it $0 = prevline if (debug) print "Parsing " $0 inbrackets = 0 for (i = 1; i <= NF; i++) { if (length($i) == 0) { continue } if (!inbrackets && substr($i, 1, 1) == "(") { inbrackets = 1 } if (!inbrackets) { token = trimpunct($i) if (length(token) > 0 && token != "and") { if (debug) print trimpunct($i) documented[trimpunct($i)]++ } } if (inbrackets && substr($i, length($i), 1) == ")") { inbrackets = 0 } } } FNR == NR { prevline = $0 next } # Stage 2: process each file and find all sysctl tables BEGINFILE { delete entries curtable = "" curentry = "" delete vars if (debug) print "Processing file " FILENAME } /^static( const)? struct ctl_table/ { match($0, /static( const)? struct ctl_table ([^][]+)/, tables) curtable = tables[2] if (debug) print "Processing table " curtable } /^};$/ { curtable = "" curentry = "" delete vars } curtable && /\.procname[\t ]*=[\t ]*".+"/ { match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) curentry = names[1] if (debug) print "Adding entry " curentry " to table " curtable entries[curtable][curentry]++ file[curentry] = FILENAME } curtable && /UCOUNT_ENTRY.*/ { match($0, /UCOUNT_ENTRY\("([^"]+)"\)/, names) curentry = names[1] if (debug) print "Adding entry " curentry " to table " curtable entries[curtable][curentry]++ file[curentry] = FILENAME } /register_sysctl.*/ { match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) if (debug) print "Registering table " tables[3] " at " tables[2] if (tables[2] == table) { for (entry in entries[tables[3]]) { printentry(entry) } } } /kmemdup.*/ { match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) if (debug) print "Found variable " names[1] " for table " names[2] if (names[2] in entries) { vars[names[1]] = names[2] } } /__register_sysctl_table.*/ { match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) if (debug) print "Registering variable table " tables[2] " at " tables[1] if (tables[1] == table && tables[2] in vars) { for (entry in entries[vars[tables[2]]]) { printentry(entry) } } } END { for (entry in documented) { if (!seen[entry]) print "No implementation for " entry } } |