xref: /linux/scripts/check-sysctl-docs (revision 4f1136a55dc8e2c27d51e934d0675e12331c7291)
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