189b491bcSJoel Granados#!/usr/bin/env -S 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 } 20*be0aef10SJoel Granados 21*be0aef10SJoel Granados # Documentation title skiplist 22*be0aef10SJoel Granados skiplist[0] = "^Documentation for" 23*be0aef10SJoel Granados skiplist[1] = "Network core options$" 24*be0aef10SJoel Granados skiplist[2] = "POSIX message queues filesystem$" 25*be0aef10SJoel Granados skiplist[3] = "Configuration options" 26*be0aef10SJoel Granados skiplist[4] = ". /proc/sys/fs" 27*be0aef10SJoel Granados skiplist[5] = "^Introduction$" 28*be0aef10SJoel Granados skiplist[6] = "^seccomp$" 29*be0aef10SJoel Granados skiplist[7] = "^pty$" 30*be0aef10SJoel Granados skiplist[8] = "^firmware_config$" 31*be0aef10SJoel Granados skiplist[9] = "^random$" 32021622dfSStephen Kitt} 33021622dfSStephen Kitt 34021622dfSStephen Kitt# The following globals are used: 35021622dfSStephen Kitt# documented: maps documented entries (each key is an entry) 36021622dfSStephen Kitt# entries: maps ctl_table names and procnames to counts (so 37021622dfSStephen Kitt# enumerating the subkeys for a given ctl_table lists its 38021622dfSStephen Kitt# procnames) 39021622dfSStephen Kitt# curtable: the name of the current ctl_table struct 40021622dfSStephen Kitt# curentry: the name of the current proc entry (procname when parsing 41021622dfSStephen Kitt# a ctl_table, constructed path when parsing a ctl_path) 42021622dfSStephen Kitt 43021622dfSStephen Kitt 44021622dfSStephen Kitt# Remove punctuation from the given value 45021622dfSStephen Kittfunction trimpunct(value) { 46021622dfSStephen Kitt while (value ~ /^["&]/) { 47021622dfSStephen Kitt value = substr(value, 2) 48021622dfSStephen Kitt } 49021622dfSStephen Kitt while (value ~ /[]["&,}]$/) { 50021622dfSStephen Kitt value = substr(value, 1, length(value) - 1) 51021622dfSStephen Kitt } 52021622dfSStephen Kitt return value 53021622dfSStephen Kitt} 54021622dfSStephen Kitt 55021622dfSStephen Kitt# Print the information for the given entry 56021622dfSStephen Kittfunction printentry(entry) { 57021622dfSStephen Kitt seen[entry]++ 58021622dfSStephen Kitt printf "* %s from %s", entry, file[entry] 59021622dfSStephen Kitt if (documented[entry]) { 60021622dfSStephen Kitt printf " (documented)" 61021622dfSStephen Kitt } 62021622dfSStephen Kitt print "" 63021622dfSStephen Kitt} 64021622dfSStephen Kitt 65021622dfSStephen Kitt 66021622dfSStephen Kitt# Stage 1: build the list of documented entries 67021622dfSStephen KittFNR == NR && /^=+$/ { 68*be0aef10SJoel Granados for (i in skiplist) { 69*be0aef10SJoel Granados if (prevline ~ skiplist[i]) { 70021622dfSStephen Kitt next 71021622dfSStephen Kitt } 72*be0aef10SJoel Granados } 73021622dfSStephen Kitt 74021622dfSStephen Kitt # The previous line is a section title, parse it 75021622dfSStephen Kitt $0 = prevline 76021622dfSStephen Kitt if (debug) print "Parsing " $0 77021622dfSStephen Kitt inbrackets = 0 78021622dfSStephen Kitt for (i = 1; i <= NF; i++) { 79021622dfSStephen Kitt if (length($i) == 0) { 80021622dfSStephen Kitt continue 81021622dfSStephen Kitt } 82021622dfSStephen Kitt if (!inbrackets && substr($i, 1, 1) == "(") { 83021622dfSStephen Kitt inbrackets = 1 84021622dfSStephen Kitt } 85021622dfSStephen Kitt if (!inbrackets) { 86021622dfSStephen Kitt token = trimpunct($i) 87021622dfSStephen Kitt if (length(token) > 0 && token != "and") { 88021622dfSStephen Kitt if (debug) print trimpunct($i) 89021622dfSStephen Kitt documented[trimpunct($i)]++ 90021622dfSStephen Kitt } 91021622dfSStephen Kitt } 92021622dfSStephen Kitt if (inbrackets && substr($i, length($i), 1) == ")") { 93021622dfSStephen Kitt inbrackets = 0 94021622dfSStephen Kitt } 95021622dfSStephen Kitt } 96021622dfSStephen Kitt} 97021622dfSStephen Kitt 98021622dfSStephen KittFNR == NR { 99021622dfSStephen Kitt prevline = $0 100021622dfSStephen Kitt next 101021622dfSStephen Kitt} 102021622dfSStephen Kitt 103021622dfSStephen Kitt 104021622dfSStephen Kitt# Stage 2: process each file and find all sysctl tables 105021622dfSStephen KittBEGINFILE { 106021622dfSStephen Kitt delete entries 107021622dfSStephen Kitt curtable = "" 108021622dfSStephen Kitt curentry = "" 1094f1136a5SThomas Weißschuh delete vars 110021622dfSStephen Kitt if (debug) print "Processing file " FILENAME 111021622dfSStephen Kitt} 112021622dfSStephen Kitt 1130f6588b3SThomas Weißschuh/^static( const)? struct ctl_table/ { 1140f6588b3SThomas Weißschuh match($0, /static( const)? struct ctl_table ([^][]+)/, tables) 1150f6588b3SThomas Weißschuh curtable = tables[2] 116021622dfSStephen Kitt if (debug) print "Processing table " curtable 117021622dfSStephen Kitt} 118021622dfSStephen Kitt 119021622dfSStephen Kitt/^};$/ { 120021622dfSStephen Kitt curtable = "" 121021622dfSStephen Kitt curentry = "" 1224f1136a5SThomas Weißschuh delete vars 123021622dfSStephen Kitt} 124021622dfSStephen Kitt 125021622dfSStephen Kittcurtable && /\.procname[\t ]*=[\t ]*".+"/ { 126021622dfSStephen Kitt match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) 127021622dfSStephen Kitt curentry = names[1] 128021622dfSStephen Kitt if (debug) print "Adding entry " curentry " to table " curtable 129021622dfSStephen Kitt entries[curtable][curentry]++ 130021622dfSStephen Kitt file[curentry] = FILENAME 131021622dfSStephen Kitt} 132021622dfSStephen Kitt 1330f6588b3SThomas Weißschuh/register_sysctl.*/ { 1340f6588b3SThomas Weißschuh match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) 1350f6588b3SThomas Weißschuh if (debug) print "Registering table " tables[3] " at " tables[2] 1360f6588b3SThomas Weißschuh if (tables[2] == table) { 1370f6588b3SThomas Weißschuh for (entry in entries[tables[3]]) { 1380f6588b3SThomas Weißschuh printentry(entry) 1390f6588b3SThomas Weißschuh } 1400f6588b3SThomas Weißschuh } 141021622dfSStephen Kitt} 142021622dfSStephen Kitt 1434f1136a5SThomas Weißschuh/kmemdup.*/ { 1444f1136a5SThomas Weißschuh match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) 1454f1136a5SThomas Weißschuh if (debug) print "Found variable " names[1] " for table " names[2] 1464f1136a5SThomas Weißschuh if (names[2] in entries) { 1474f1136a5SThomas Weißschuh vars[names[1]] = names[2] 1484f1136a5SThomas Weißschuh } 1494f1136a5SThomas Weißschuh} 1504f1136a5SThomas Weißschuh 1514f1136a5SThomas Weißschuh/__register_sysctl_table.*/ { 1524f1136a5SThomas Weißschuh match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) 1534f1136a5SThomas Weißschuh if (debug) print "Registering variable table " tables[2] " at " tables[1] 1544f1136a5SThomas Weißschuh if (tables[1] == table && tables[2] in vars) { 1554f1136a5SThomas Weißschuh for (entry in entries[vars[tables[2]]]) { 1564f1136a5SThomas Weißschuh printentry(entry) 1574f1136a5SThomas Weißschuh } 1584f1136a5SThomas Weißschuh } 1594f1136a5SThomas Weißschuh} 1604f1136a5SThomas Weißschuh 161021622dfSStephen KittEND { 162021622dfSStephen Kitt for (entry in documented) { 163021622dfSStephen Kitt if (!seen[entry]) { 164021622dfSStephen Kitt print "No implementation for " entry 165021622dfSStephen Kitt } 166021622dfSStephen Kitt } 167021622dfSStephen Kitt} 168