Skip to content

Commit 021622d

Browse files
skittJonathan Corbet
authored and
Jonathan Corbet
committed
docs: add a script to check sysctl docs
This script allows sysctl documentation to be checked against the kernel source code, to identify missing or obsolete entries. Running it against 5.5 shows for example that sysctl/kernel.rst has two obsolete entries and is missing 52 entries. Signed-off-by: Stephen Kitt <[email protected]> Signed-off-by: Jonathan Corbet <[email protected]>
1 parent bf347b9 commit 021622d

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed

Documentation/admin-guide/sysctl/kernel.rst

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
Documentation for /proc/sys/kernel/
33
===================================
44

5+
.. See scripts/check-sysctl-docs to keep this up to date
6+
7+
58
Copyright (c) 1998, 1999, Rik van Riel <[email protected]>
69

710
Copyright (c) 2009, Shen Feng<[email protected]>

scripts/check-sysctl-docs

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
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+
15+
BEGIN {
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+
# children: maps ctl_table names and procnames to child ctl_table names
24+
# documented: maps documented entries (each key is an entry)
25+
# entries: maps ctl_table names and procnames to counts (so
26+
# enumerating the subkeys for a given ctl_table lists its
27+
# procnames)
28+
# files: maps procnames to source file names
29+
# paths: maps ctl_path names to paths
30+
# curpath: the name of the current ctl_path struct
31+
# curtable: the name of the current ctl_table struct
32+
# curentry: the name of the current proc entry (procname when parsing
33+
# a ctl_table, constructed path when parsing a ctl_path)
34+
35+
36+
# Remove punctuation from the given value
37+
function trimpunct(value) {
38+
while (value ~ /^["&]/) {
39+
value = substr(value, 2)
40+
}
41+
while (value ~ /[]["&,}]$/) {
42+
value = substr(value, 1, length(value) - 1)
43+
}
44+
return value
45+
}
46+
47+
# Print the information for the given entry
48+
function printentry(entry) {
49+
seen[entry]++
50+
printf "* %s from %s", entry, file[entry]
51+
if (documented[entry]) {
52+
printf " (documented)"
53+
}
54+
print ""
55+
}
56+
57+
58+
# Stage 1: build the list of documented entries
59+
FNR == NR && /^=+$/ {
60+
if (prevline ~ /Documentation for/) {
61+
# This is the main title
62+
next
63+
}
64+
65+
# The previous line is a section title, parse it
66+
$0 = prevline
67+
if (debug) print "Parsing " $0
68+
inbrackets = 0
69+
for (i = 1; i <= NF; i++) {
70+
if (length($i) == 0) {
71+
continue
72+
}
73+
if (!inbrackets && substr($i, 1, 1) == "(") {
74+
inbrackets = 1
75+
}
76+
if (!inbrackets) {
77+
token = trimpunct($i)
78+
if (length(token) > 0 && token != "and") {
79+
if (debug) print trimpunct($i)
80+
documented[trimpunct($i)]++
81+
}
82+
}
83+
if (inbrackets && substr($i, length($i), 1) == ")") {
84+
inbrackets = 0
85+
}
86+
}
87+
}
88+
89+
FNR == NR {
90+
prevline = $0
91+
next
92+
}
93+
94+
95+
# Stage 2: process each file and find all sysctl tables
96+
BEGINFILE {
97+
delete children
98+
delete entries
99+
delete paths
100+
curpath = ""
101+
curtable = ""
102+
curentry = ""
103+
if (debug) print "Processing file " FILENAME
104+
}
105+
106+
/^static struct ctl_path/ {
107+
match($0, /static struct ctl_path ([^][]+)/, tables)
108+
curpath = tables[1]
109+
if (debug) print "Processing path " curpath
110+
}
111+
112+
/^static struct ctl_table/ {
113+
match($0, /static struct ctl_table ([^][]+)/, tables)
114+
curtable = tables[1]
115+
if (debug) print "Processing table " curtable
116+
}
117+
118+
/^};$/ {
119+
curpath = ""
120+
curtable = ""
121+
curentry = ""
122+
}
123+
124+
curpath && /\.procname[\t ]*=[\t ]*".+"/ {
125+
match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
126+
if (curentry) {
127+
curentry = curentry "/" names[1]
128+
} else {
129+
curentry = names[1]
130+
}
131+
if (debug) print "Setting path " curpath " to " curentry
132+
paths[curpath] = curentry
133+
}
134+
135+
curtable && /\.procname[\t ]*=[\t ]*".+"/ {
136+
match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
137+
curentry = names[1]
138+
if (debug) print "Adding entry " curentry " to table " curtable
139+
entries[curtable][curentry]++
140+
file[curentry] = FILENAME
141+
}
142+
143+
/\.child[\t ]*=/ {
144+
child = trimpunct($NF)
145+
if (debug) print "Linking child " child " to table " curtable " entry " curentry
146+
children[curtable][curentry] = child
147+
}
148+
149+
/register_sysctl_table\(.*\)/ {
150+
match($0, /register_sysctl_table\(([^)]+)\)/, tables)
151+
if (debug) print "Registering table " tables[1]
152+
if (children[tables[1]][table]) {
153+
for (entry in entries[children[tables[1]][table]]) {
154+
printentry(entry)
155+
}
156+
}
157+
}
158+
159+
/register_sysctl_paths\(.*\)/ {
160+
match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables)
161+
if (debug) print "Attaching table " tables[2] " to path " tables[1]
162+
if (paths[tables[1]] == table) {
163+
for (entry in entries[tables[2]]) {
164+
printentry(entry)
165+
}
166+
}
167+
split(paths[tables[1]], components, "/")
168+
if (length(components) > 1 && components[1] == table) {
169+
# Count the first subdirectory as seen
170+
seen[components[2]]++
171+
}
172+
}
173+
174+
175+
END {
176+
for (entry in documented) {
177+
if (!seen[entry]) {
178+
print "No implementation for " entry
179+
}
180+
}
181+
}

0 commit comments

Comments
 (0)