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