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