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 # documented: maps documented entries (each ke 23 # documented: maps documented entries (each key is an entry) 24 # entries: maps ctl_table names and procnames 24 # entries: maps ctl_table names and procnames to counts (so 25 # enumerating the subkeys for a given 25 # enumerating the subkeys for a given ctl_table lists its 26 # procnames) 26 # procnames) 27 # curtable: the name of the current ctl_table 27 # curtable: the name of the current ctl_table struct 28 # curentry: the name of the current proc entry 28 # curentry: the name of the current proc entry (procname when parsing 29 # a ctl_table, constructed path when 29 # a ctl_table, constructed path when parsing a ctl_path) 30 30 31 31 32 # Remove punctuation from the given value 32 # Remove punctuation from the given value 33 function trimpunct(value) { 33 function trimpunct(value) { 34 while (value ~ /^["&]/) { 34 while (value ~ /^["&]/) { 35 value = substr(value, 2) 35 value = substr(value, 2) 36 } 36 } 37 while (value ~ /[]["&,}]$/) { 37 while (value ~ /[]["&,}]$/) { 38 value = substr(value, 1, length(value) 38 value = substr(value, 1, length(value) - 1) 39 } 39 } 40 return value 40 return value 41 } 41 } 42 42 43 # Print the information for the given entry 43 # Print the information for the given entry 44 function printentry(entry) { 44 function printentry(entry) { 45 seen[entry]++ 45 seen[entry]++ 46 printf "* %s from %s", entry, file[entry] 46 printf "* %s from %s", entry, file[entry] 47 if (documented[entry]) { 47 if (documented[entry]) { 48 printf " (documented)" 48 printf " (documented)" 49 } 49 } 50 print "" 50 print "" 51 } 51 } 52 52 53 53 54 # Stage 1: build the list of documented entrie 54 # Stage 1: build the list of documented entries 55 FNR == NR && /^=+$/ { 55 FNR == NR && /^=+$/ { 56 if (prevline ~ /Documentation for/) { 56 if (prevline ~ /Documentation for/) { 57 # This is the main title 57 # This is the main title 58 next 58 next 59 } 59 } 60 60 61 # The previous line is a section title, pa 61 # The previous line is a section title, parse it 62 $0 = prevline 62 $0 = prevline 63 if (debug) print "Parsing " $0 63 if (debug) print "Parsing " $0 64 inbrackets = 0 64 inbrackets = 0 65 for (i = 1; i <= NF; i++) { 65 for (i = 1; i <= NF; i++) { 66 if (length($i) == 0) { 66 if (length($i) == 0) { 67 continue 67 continue 68 } 68 } 69 if (!inbrackets && substr($i, 1, 1) == 69 if (!inbrackets && substr($i, 1, 1) == "(") { 70 inbrackets = 1 70 inbrackets = 1 71 } 71 } 72 if (!inbrackets) { 72 if (!inbrackets) { 73 token = trimpunct($i) 73 token = trimpunct($i) 74 if (length(token) > 0 && token != 74 if (length(token) > 0 && token != "and") { 75 if (debug) print trimpunct($i) 75 if (debug) print trimpunct($i) 76 documented[trimpunct($i)]++ 76 documented[trimpunct($i)]++ 77 } 77 } 78 } 78 } 79 if (inbrackets && substr($i, length($i 79 if (inbrackets && substr($i, length($i), 1) == ")") { 80 inbrackets = 0 80 inbrackets = 0 81 } 81 } 82 } 82 } 83 } 83 } 84 84 85 FNR == NR { 85 FNR == NR { 86 prevline = $0 86 prevline = $0 87 next 87 next 88 } 88 } 89 89 90 90 91 # Stage 2: process each file and find all sysc 91 # Stage 2: process each file and find all sysctl tables 92 BEGINFILE { 92 BEGINFILE { 93 delete entries 93 delete entries 94 curtable = "" 94 curtable = "" 95 curentry = "" 95 curentry = "" 96 delete vars 96 delete vars 97 if (debug) print "Processing file " FILENA 97 if (debug) print "Processing file " FILENAME 98 } 98 } 99 99 100 /^static( const)? struct ctl_table/ { 100 /^static( const)? struct ctl_table/ { 101 match($0, /static( const)? struct ctl_tabl 101 match($0, /static( const)? struct ctl_table ([^][]+)/, tables) 102 curtable = tables[2] 102 curtable = tables[2] 103 if (debug) print "Processing table " curta 103 if (debug) print "Processing table " curtable 104 } 104 } 105 105 106 /^};$/ { 106 /^};$/ { 107 curtable = "" 107 curtable = "" 108 curentry = "" 108 curentry = "" 109 delete vars 109 delete vars 110 } 110 } 111 111 112 curtable && /\.procname[\t ]*=[\t ]*".+"/ { 112 curtable && /\.procname[\t ]*=[\t ]*".+"/ { 113 match($0, /.procname[\t ]*=[\t ]*"([^"]+)" 113 match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) 114 curentry = names[1] 114 curentry = names[1] 115 if (debug) print "Adding entry " curentry 115 if (debug) print "Adding entry " curentry " to table " curtable 116 entries[curtable][curentry]++ 116 entries[curtable][curentry]++ 117 file[curentry] = FILENAME 117 file[curentry] = FILENAME 118 } 118 } 119 119 120 /register_sysctl.*/ { 120 /register_sysctl.*/ { 121 match($0, /register_sysctl(|_init|_sz)\("( 121 match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) 122 if (debug) print "Registering table " tabl 122 if (debug) print "Registering table " tables[3] " at " tables[2] 123 if (tables[2] == table) { 123 if (tables[2] == table) { 124 for (entry in entries[tables[3]]) { 124 for (entry in entries[tables[3]]) { 125 printentry(entry) 125 printentry(entry) 126 } 126 } 127 } 127 } 128 } 128 } 129 129 130 /kmemdup.*/ { 130 /kmemdup.*/ { 131 match($0, /([^ \t]+) *= *kmemdup\(([^,]+) 131 match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) 132 if (debug) print "Found variable " names[1 132 if (debug) print "Found variable " names[1] " for table " names[2] 133 if (names[2] in entries) { 133 if (names[2] in entries) { 134 vars[names[1]] = names[2] 134 vars[names[1]] = names[2] 135 } 135 } 136 } 136 } 137 137 138 /__register_sysctl_table.*/ { 138 /__register_sysctl_table.*/ { 139 match($0, /__register_sysctl_table\([^,]+, 139 match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) 140 if (debug) print "Registering variable tab 140 if (debug) print "Registering variable table " tables[2] " at " tables[1] 141 if (tables[1] == table && tables[2] in var 141 if (tables[1] == table && tables[2] in vars) { 142 for (entry in entries[vars[tables[2]]] 142 for (entry in entries[vars[tables[2]]]) { 143 printentry(entry) 143 printentry(entry) 144 } 144 } 145 } 145 } 146 } 146 } 147 147 148 END { 148 END { 149 for (entry in documented) { 149 for (entry in documented) { 150 if (!seen[entry]) { 150 if (!seen[entry]) { 151 print "No implementation for " ent 151 print "No implementation for " entry 152 } 152 } 153 } 153 } 154 } 154 }
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.