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