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 \ 11*0f6588b3SThomas 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 = "" 96021622dfSStephen Kitt if (debug) print "Processing file " FILENAME 97021622dfSStephen Kitt} 98021622dfSStephen Kitt 99*0f6588b3SThomas Weißschuh/^static( const)? struct ctl_table/ { 100*0f6588b3SThomas Weißschuh match($0, /static( const)? struct ctl_table ([^][]+)/, tables) 101*0f6588b3SThomas Weißschuh curtable = tables[2] 102021622dfSStephen Kitt if (debug) print "Processing table " curtable 103021622dfSStephen Kitt} 104021622dfSStephen Kitt 105021622dfSStephen Kitt/^};$/ { 106021622dfSStephen Kitt curtable = "" 107021622dfSStephen Kitt curentry = "" 108021622dfSStephen Kitt} 109021622dfSStephen Kitt 110021622dfSStephen Kittcurtable && /\.procname[\t ]*=[\t ]*".+"/ { 111021622dfSStephen Kitt match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) 112021622dfSStephen Kitt curentry = names[1] 113021622dfSStephen Kitt if (debug) print "Adding entry " curentry " to table " curtable 114021622dfSStephen Kitt entries[curtable][curentry]++ 115021622dfSStephen Kitt file[curentry] = FILENAME 116021622dfSStephen Kitt} 117021622dfSStephen Kitt 118*0f6588b3SThomas Weißschuh/register_sysctl.*/ { 119*0f6588b3SThomas Weißschuh match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) 120*0f6588b3SThomas Weißschuh if (debug) print "Registering table " tables[3] " at " tables[2] 121*0f6588b3SThomas Weißschuh if (tables[2] == table) { 122*0f6588b3SThomas Weißschuh for (entry in entries[tables[3]]) { 123*0f6588b3SThomas Weißschuh printentry(entry) 124*0f6588b3SThomas Weißschuh } 125*0f6588b3SThomas Weißschuh } 126021622dfSStephen Kitt} 127021622dfSStephen Kitt 128021622dfSStephen KittEND { 129021622dfSStephen Kitt for (entry in documented) { 130021622dfSStephen Kitt if (!seen[entry]) { 131021622dfSStephen Kitt print "No implementation for " entry 132021622dfSStephen Kitt } 133021622dfSStephen Kitt } 134021622dfSStephen Kitt} 135