xref: /openbmc/linux/scripts/check-sysctl-docs (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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 KittEND {
150*021622dfSStephen Kitt    for (entry in documented) {
151*021622dfSStephen Kitt	if (!seen[entry]) {
152*021622dfSStephen Kitt	    print "No implementation for " entry
153*021622dfSStephen Kitt	}
154*021622dfSStephen Kitt    }
155*021622dfSStephen Kitt}
156*021622dfSStephen Kitt