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