xref: /linux/scripts/check-sysctl-docs (revision 021622df556b7213cffec1c0713f093fc7d045e3)
1*021622dfSStephen Kitt#!/usr/bin/gawk -f
2*021622dfSStephen Kitt# SPDX-License-Identifier: GPL-2.0
3*021622dfSStephen Kitt
4*021622dfSStephen Kitt# Script to check sysctl documentation against source files
5*021622dfSStephen Kitt#
6*021622dfSStephen Kitt# Copyright (c) 2020 Stephen Kitt
7*021622dfSStephen Kitt
8*021622dfSStephen Kitt# Example invocation:
9*021622dfSStephen Kitt#	scripts/check-sysctl-docs -vtable="kernel" \
10*021622dfSStephen Kitt#		Documentation/admin-guide/sysctl/kernel.rst \
11*021622dfSStephen Kitt#		$(git grep -l register_sysctl_)
12*021622dfSStephen Kitt#
13*021622dfSStephen Kitt# Specify -vdebug=1 to see debugging information
14*021622dfSStephen Kitt
15*021622dfSStephen KittBEGIN {
16*021622dfSStephen Kitt    if (!table) {
17*021622dfSStephen Kitt	print "Please specify the table to look for using the table variable" > "/dev/stderr"
18*021622dfSStephen Kitt	exit 1
19*021622dfSStephen Kitt    }
20*021622dfSStephen Kitt}
21*021622dfSStephen Kitt
22*021622dfSStephen Kitt# The following globals are used:
23*021622dfSStephen Kitt# children: maps ctl_table names and procnames to child ctl_table names
24*021622dfSStephen Kitt# documented: maps documented entries (each key is an entry)
25*021622dfSStephen Kitt# entries: maps ctl_table names and procnames to counts (so
26*021622dfSStephen Kitt#          enumerating the subkeys for a given ctl_table lists its
27*021622dfSStephen Kitt#          procnames)
28*021622dfSStephen Kitt# files: maps procnames to source file names
29*021622dfSStephen Kitt# paths: maps ctl_path names to paths
30*021622dfSStephen Kitt# curpath: the name of the current ctl_path struct
31*021622dfSStephen Kitt# curtable: the name of the current ctl_table struct
32*021622dfSStephen Kitt# curentry: the name of the current proc entry (procname when parsing
33*021622dfSStephen Kitt#           a ctl_table, constructed path when parsing a ctl_path)
34*021622dfSStephen Kitt
35*021622dfSStephen Kitt
36*021622dfSStephen Kitt# Remove punctuation from the given value
37*021622dfSStephen Kittfunction trimpunct(value) {
38*021622dfSStephen Kitt    while (value ~ /^["&]/) {
39*021622dfSStephen Kitt	value = substr(value, 2)
40*021622dfSStephen Kitt    }
41*021622dfSStephen Kitt    while (value ~ /[]["&,}]$/) {
42*021622dfSStephen Kitt	value = substr(value, 1, length(value) - 1)
43*021622dfSStephen Kitt    }
44*021622dfSStephen Kitt    return value
45*021622dfSStephen Kitt}
46*021622dfSStephen Kitt
47*021622dfSStephen Kitt# Print the information for the given entry
48*021622dfSStephen Kittfunction printentry(entry) {
49*021622dfSStephen Kitt    seen[entry]++
50*021622dfSStephen Kitt    printf "* %s from %s", entry, file[entry]
51*021622dfSStephen Kitt    if (documented[entry]) {
52*021622dfSStephen Kitt	printf " (documented)"
53*021622dfSStephen Kitt    }
54*021622dfSStephen Kitt    print ""
55*021622dfSStephen Kitt}
56*021622dfSStephen Kitt
57*021622dfSStephen Kitt
58*021622dfSStephen Kitt# Stage 1: build the list of documented entries
59*021622dfSStephen KittFNR == NR && /^=+$/ {
60*021622dfSStephen Kitt    if (prevline ~ /Documentation for/) {
61*021622dfSStephen Kitt	# This is the main title
62*021622dfSStephen Kitt	next
63*021622dfSStephen Kitt    }
64*021622dfSStephen Kitt
65*021622dfSStephen Kitt    # The previous line is a section title, parse it
66*021622dfSStephen Kitt    $0 = prevline
67*021622dfSStephen Kitt    if (debug) print "Parsing " $0
68*021622dfSStephen Kitt    inbrackets = 0
69*021622dfSStephen Kitt    for (i = 1; i <= NF; i++) {
70*021622dfSStephen Kitt	if (length($i) == 0) {
71*021622dfSStephen Kitt	    continue
72*021622dfSStephen Kitt	}
73*021622dfSStephen Kitt	if (!inbrackets && substr($i, 1, 1) == "(") {
74*021622dfSStephen Kitt	    inbrackets = 1
75*021622dfSStephen Kitt	}
76*021622dfSStephen Kitt	if (!inbrackets) {
77*021622dfSStephen Kitt	    token = trimpunct($i)
78*021622dfSStephen Kitt	    if (length(token) > 0 && token != "and") {
79*021622dfSStephen Kitt		if (debug) print trimpunct($i)
80*021622dfSStephen Kitt		documented[trimpunct($i)]++
81*021622dfSStephen Kitt	    }
82*021622dfSStephen Kitt	}
83*021622dfSStephen Kitt	if (inbrackets && substr($i, length($i), 1) == ")") {
84*021622dfSStephen Kitt	    inbrackets = 0
85*021622dfSStephen Kitt	}
86*021622dfSStephen Kitt    }
87*021622dfSStephen Kitt}
88*021622dfSStephen Kitt
89*021622dfSStephen KittFNR == NR {
90*021622dfSStephen Kitt    prevline = $0
91*021622dfSStephen Kitt    next
92*021622dfSStephen Kitt}
93*021622dfSStephen Kitt
94*021622dfSStephen Kitt
95*021622dfSStephen Kitt# Stage 2: process each file and find all sysctl tables
96*021622dfSStephen KittBEGINFILE {
97*021622dfSStephen Kitt    delete children
98*021622dfSStephen Kitt    delete entries
99*021622dfSStephen Kitt    delete paths
100*021622dfSStephen Kitt    curpath = ""
101*021622dfSStephen Kitt    curtable = ""
102*021622dfSStephen Kitt    curentry = ""
103*021622dfSStephen Kitt    if (debug) print "Processing file " FILENAME
104*021622dfSStephen Kitt}
105*021622dfSStephen Kitt
106*021622dfSStephen Kitt/^static struct ctl_path/ {
107*021622dfSStephen Kitt    match($0, /static struct ctl_path ([^][]+)/, tables)
108*021622dfSStephen Kitt    curpath = tables[1]
109*021622dfSStephen Kitt    if (debug) print "Processing path " curpath
110*021622dfSStephen Kitt}
111*021622dfSStephen Kitt
112*021622dfSStephen Kitt/^static struct ctl_table/ {
113*021622dfSStephen Kitt    match($0, /static struct ctl_table ([^][]+)/, tables)
114*021622dfSStephen Kitt    curtable = tables[1]
115*021622dfSStephen Kitt    if (debug) print "Processing table " curtable
116*021622dfSStephen Kitt}
117*021622dfSStephen Kitt
118*021622dfSStephen Kitt/^};$/ {
119*021622dfSStephen Kitt    curpath = ""
120*021622dfSStephen Kitt    curtable = ""
121*021622dfSStephen Kitt    curentry = ""
122*021622dfSStephen Kitt}
123*021622dfSStephen Kitt
124*021622dfSStephen Kittcurpath && /\.procname[\t ]*=[\t ]*".+"/ {
125*021622dfSStephen Kitt    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
126*021622dfSStephen Kitt    if (curentry) {
127*021622dfSStephen Kitt	curentry = curentry "/" names[1]
128*021622dfSStephen Kitt    } else {
129*021622dfSStephen Kitt	curentry = names[1]
130*021622dfSStephen Kitt    }
131*021622dfSStephen Kitt    if (debug) print "Setting path " curpath " to " curentry
132*021622dfSStephen Kitt    paths[curpath] = curentry
133*021622dfSStephen Kitt}
134*021622dfSStephen Kitt
135*021622dfSStephen Kittcurtable && /\.procname[\t ]*=[\t ]*".+"/ {
136*021622dfSStephen Kitt    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
137*021622dfSStephen Kitt    curentry = names[1]
138*021622dfSStephen Kitt    if (debug) print "Adding entry " curentry " to table " curtable
139*021622dfSStephen Kitt    entries[curtable][curentry]++
140*021622dfSStephen Kitt    file[curentry] = FILENAME
141*021622dfSStephen Kitt}
142*021622dfSStephen Kitt
143*021622dfSStephen Kitt/\.child[\t ]*=/ {
144*021622dfSStephen Kitt    child = trimpunct($NF)
145*021622dfSStephen Kitt    if (debug) print "Linking child " child " to table " curtable " entry " curentry
146*021622dfSStephen Kitt    children[curtable][curentry] = child
147*021622dfSStephen Kitt}
148*021622dfSStephen Kitt
149*021622dfSStephen Kitt/register_sysctl_table\(.*\)/ {
150*021622dfSStephen Kitt    match($0, /register_sysctl_table\(([^)]+)\)/, tables)
151*021622dfSStephen Kitt    if (debug) print "Registering table " tables[1]
152*021622dfSStephen Kitt    if (children[tables[1]][table]) {
153*021622dfSStephen Kitt	for (entry in entries[children[tables[1]][table]]) {
154*021622dfSStephen Kitt	    printentry(entry)
155*021622dfSStephen Kitt	}
156*021622dfSStephen Kitt    }
157*021622dfSStephen Kitt}
158*021622dfSStephen Kitt
159*021622dfSStephen Kitt/register_sysctl_paths\(.*\)/ {
160*021622dfSStephen Kitt    match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables)
161*021622dfSStephen Kitt    if (debug) print "Attaching table " tables[2] " to path " tables[1]
162*021622dfSStephen Kitt    if (paths[tables[1]] == table) {
163*021622dfSStephen Kitt	for (entry in entries[tables[2]]) {
164*021622dfSStephen Kitt	    printentry(entry)
165*021622dfSStephen Kitt	}
166*021622dfSStephen Kitt    }
167*021622dfSStephen Kitt    split(paths[tables[1]], components, "/")
168*021622dfSStephen Kitt    if (length(components) > 1 && components[1] == table) {
169*021622dfSStephen Kitt	# Count the first subdirectory as seen
170*021622dfSStephen Kitt	seen[components[2]]++
171*021622dfSStephen Kitt    }
172*021622dfSStephen Kitt}
173*021622dfSStephen Kitt
174*021622dfSStephen Kitt
175*021622dfSStephen KittEND {
176*021622dfSStephen Kitt    for (entry in documented) {
177*021622dfSStephen Kitt	if (!seen[entry]) {
178*021622dfSStephen Kitt	    print "No implementation for " entry
179*021622dfSStephen Kitt	}
180*021622dfSStephen Kitt    }
181*021622dfSStephen Kitt}
182