1021622dfSStephen Kitt#!/usr/bin/gawk -f 2021622dfSStephen Kitt# SPDX-License-Identifier: GPL-2.0 3021622dfSStephen Kitt 4021622dfSStephen Kitt# Script to check sysctl documentation against source files 5021622dfSStephen Kitt# 6021622dfSStephen Kitt# Copyright (c) 2020 Stephen Kitt 7021622dfSStephen Kitt 8021622dfSStephen Kitt# Example invocation: 9021622dfSStephen Kitt# scripts/check-sysctl-docs -vtable="kernel" \ 10021622dfSStephen Kitt# Documentation/admin-guide/sysctl/kernel.rst \ 110f6588b3SThomas Weißschuh# $(git grep -l register_sysctl) 12021622dfSStephen Kitt# 13021622dfSStephen Kitt# Specify -vdebug=1 to see debugging information 14021622dfSStephen Kitt 15021622dfSStephen KittBEGIN { 16021622dfSStephen Kitt if (!table) { 17021622dfSStephen Kitt print "Please specify the table to look for using the table variable" > "/dev/stderr" 18021622dfSStephen Kitt exit 1 19021622dfSStephen Kitt } 20021622dfSStephen Kitt} 21021622dfSStephen Kitt 22021622dfSStephen Kitt# The following globals are used: 23021622dfSStephen Kitt# documented: maps documented entries (each key is an entry) 24021622dfSStephen Kitt# entries: maps ctl_table names and procnames to counts (so 25021622dfSStephen Kitt# enumerating the subkeys for a given ctl_table lists its 26021622dfSStephen Kitt# procnames) 27021622dfSStephen Kitt# curtable: the name of the current ctl_table struct 28021622dfSStephen Kitt# curentry: the name of the current proc entry (procname when parsing 29021622dfSStephen Kitt# a ctl_table, constructed path when parsing a ctl_path) 30021622dfSStephen Kitt 31021622dfSStephen Kitt 32021622dfSStephen Kitt# Remove punctuation from the given value 33021622dfSStephen Kittfunction trimpunct(value) { 34021622dfSStephen Kitt while (value ~ /^["&]/) { 35021622dfSStephen Kitt value = substr(value, 2) 36021622dfSStephen Kitt } 37021622dfSStephen Kitt while (value ~ /[]["&,}]$/) { 38021622dfSStephen Kitt value = substr(value, 1, length(value) - 1) 39021622dfSStephen Kitt } 40021622dfSStephen Kitt return value 41021622dfSStephen Kitt} 42021622dfSStephen Kitt 43021622dfSStephen Kitt# Print the information for the given entry 44021622dfSStephen Kittfunction printentry(entry) { 45021622dfSStephen Kitt seen[entry]++ 46021622dfSStephen Kitt printf "* %s from %s", entry, file[entry] 47021622dfSStephen Kitt if (documented[entry]) { 48021622dfSStephen Kitt printf " (documented)" 49021622dfSStephen Kitt } 50021622dfSStephen Kitt print "" 51021622dfSStephen Kitt} 52021622dfSStephen Kitt 53021622dfSStephen Kitt 54021622dfSStephen Kitt# Stage 1: build the list of documented entries 55021622dfSStephen KittFNR == NR && /^=+$/ { 56021622dfSStephen Kitt if (prevline ~ /Documentation for/) { 57021622dfSStephen Kitt # This is the main title 58021622dfSStephen Kitt next 59021622dfSStephen Kitt } 60021622dfSStephen Kitt 61021622dfSStephen Kitt # The previous line is a section title, parse it 62021622dfSStephen Kitt $0 = prevline 63021622dfSStephen Kitt if (debug) print "Parsing " $0 64021622dfSStephen Kitt inbrackets = 0 65021622dfSStephen Kitt for (i = 1; i <= NF; i++) { 66021622dfSStephen Kitt if (length($i) == 0) { 67021622dfSStephen Kitt continue 68021622dfSStephen Kitt } 69021622dfSStephen Kitt if (!inbrackets && substr($i, 1, 1) == "(") { 70021622dfSStephen Kitt inbrackets = 1 71021622dfSStephen Kitt } 72021622dfSStephen Kitt if (!inbrackets) { 73021622dfSStephen Kitt token = trimpunct($i) 74021622dfSStephen Kitt if (length(token) > 0 && token != "and") { 75021622dfSStephen Kitt if (debug) print trimpunct($i) 76021622dfSStephen Kitt documented[trimpunct($i)]++ 77021622dfSStephen Kitt } 78021622dfSStephen Kitt } 79021622dfSStephen Kitt if (inbrackets && substr($i, length($i), 1) == ")") { 80021622dfSStephen Kitt inbrackets = 0 81021622dfSStephen Kitt } 82021622dfSStephen Kitt } 83021622dfSStephen Kitt} 84021622dfSStephen Kitt 85021622dfSStephen KittFNR == NR { 86021622dfSStephen Kitt prevline = $0 87021622dfSStephen Kitt next 88021622dfSStephen Kitt} 89021622dfSStephen Kitt 90021622dfSStephen Kitt 91021622dfSStephen Kitt# Stage 2: process each file and find all sysctl tables 92021622dfSStephen KittBEGINFILE { 93021622dfSStephen Kitt delete entries 94021622dfSStephen Kitt curtable = "" 95021622dfSStephen Kitt curentry = "" 96*4f1136a5SThomas Weißschuh delete vars 97021622dfSStephen Kitt if (debug) print "Processing file " FILENAME 98021622dfSStephen Kitt} 99021622dfSStephen Kitt 1000f6588b3SThomas Weißschuh/^static( const)? struct ctl_table/ { 1010f6588b3SThomas Weißschuh match($0, /static( const)? struct ctl_table ([^][]+)/, tables) 1020f6588b3SThomas Weißschuh curtable = tables[2] 103021622dfSStephen Kitt if (debug) print "Processing table " curtable 104021622dfSStephen Kitt} 105021622dfSStephen Kitt 106021622dfSStephen Kitt/^};$/ { 107021622dfSStephen Kitt curtable = "" 108021622dfSStephen Kitt curentry = "" 109*4f1136a5SThomas Weißschuh delete vars 110021622dfSStephen Kitt} 111021622dfSStephen Kitt 112021622dfSStephen Kittcurtable && /\.procname[\t ]*=[\t ]*".+"/ { 113021622dfSStephen Kitt match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) 114021622dfSStephen Kitt curentry = names[1] 115021622dfSStephen Kitt if (debug) print "Adding entry " curentry " to table " curtable 116021622dfSStephen Kitt entries[curtable][curentry]++ 117021622dfSStephen Kitt file[curentry] = FILENAME 118021622dfSStephen Kitt} 119021622dfSStephen Kitt 1200f6588b3SThomas Weißschuh/register_sysctl.*/ { 1210f6588b3SThomas Weißschuh match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) 1220f6588b3SThomas Weißschuh if (debug) print "Registering table " tables[3] " at " tables[2] 1230f6588b3SThomas Weißschuh if (tables[2] == table) { 1240f6588b3SThomas Weißschuh for (entry in entries[tables[3]]) { 1250f6588b3SThomas Weißschuh printentry(entry) 1260f6588b3SThomas Weißschuh } 1270f6588b3SThomas Weißschuh } 128021622dfSStephen Kitt} 129021622dfSStephen Kitt 130*4f1136a5SThomas Weißschuh/kmemdup.*/ { 131*4f1136a5SThomas Weißschuh match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) 132*4f1136a5SThomas Weißschuh if (debug) print "Found variable " names[1] " for table " names[2] 133*4f1136a5SThomas Weißschuh if (names[2] in entries) { 134*4f1136a5SThomas Weißschuh vars[names[1]] = names[2] 135*4f1136a5SThomas Weißschuh } 136*4f1136a5SThomas Weißschuh} 137*4f1136a5SThomas Weißschuh 138*4f1136a5SThomas Weißschuh/__register_sysctl_table.*/ { 139*4f1136a5SThomas Weißschuh match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) 140*4f1136a5SThomas Weißschuh if (debug) print "Registering variable table " tables[2] " at " tables[1] 141*4f1136a5SThomas Weißschuh if (tables[1] == table && tables[2] in vars) { 142*4f1136a5SThomas Weißschuh for (entry in entries[vars[tables[2]]]) { 143*4f1136a5SThomas Weißschuh printentry(entry) 144*4f1136a5SThomas Weißschuh } 145*4f1136a5SThomas Weißschuh } 146*4f1136a5SThomas Weißschuh} 147*4f1136a5SThomas Weißschuh 148021622dfSStephen KittEND { 149021622dfSStephen Kitt for (entry in documented) { 150021622dfSStephen Kitt if (!seen[entry]) { 151021622dfSStephen Kitt print "No implementation for " entry 152021622dfSStephen Kitt } 153021622dfSStephen Kitt } 154021622dfSStephen Kitt} 155