Commit | Line | Data |
---|---|---|
021622df SK |
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 \ | |
0f6588b3 | 11 | # $(git grep -l register_sysctl) |
021622df SK |
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: | |
021622df SK |
23 | # documented: maps documented entries (each key is an entry) |
24 | # entries: maps ctl_table names and procnames to counts (so | |
25 | # enumerating the subkeys for a given ctl_table lists its | |
26 | # procnames) | |
021622df SK |
27 | # curtable: the name of the current ctl_table struct |
28 | # curentry: the name of the current proc entry (procname when parsing | |
29 | # a ctl_table, constructed path when parsing a ctl_path) | |
30 | ||
31 | ||
32 | # Remove punctuation from the given value | |
33 | function trimpunct(value) { | |
34 | while (value ~ /^["&]/) { | |
35 | value = substr(value, 2) | |
36 | } | |
37 | while (value ~ /[]["&,}]$/) { | |
38 | value = substr(value, 1, length(value) - 1) | |
39 | } | |
40 | return value | |
41 | } | |
42 | ||
43 | # Print the information for the given entry | |
44 | function printentry(entry) { | |
45 | seen[entry]++ | |
46 | printf "* %s from %s", entry, file[entry] | |
47 | if (documented[entry]) { | |
48 | printf " (documented)" | |
49 | } | |
50 | print "" | |
51 | } | |
52 | ||
53 | ||
54 | # Stage 1: build the list of documented entries | |
55 | FNR == NR && /^=+$/ { | |
56 | if (prevline ~ /Documentation for/) { | |
57 | # This is the main title | |
58 | next | |
59 | } | |
60 | ||
61 | # The previous line is a section title, parse it | |
62 | $0 = prevline | |
63 | if (debug) print "Parsing " $0 | |
64 | inbrackets = 0 | |
65 | for (i = 1; i <= NF; i++) { | |
66 | if (length($i) == 0) { | |
67 | continue | |
68 | } | |
69 | if (!inbrackets && substr($i, 1, 1) == "(") { | |
70 | inbrackets = 1 | |
71 | } | |
72 | if (!inbrackets) { | |
73 | token = trimpunct($i) | |
74 | if (length(token) > 0 && token != "and") { | |
75 | if (debug) print trimpunct($i) | |
76 | documented[trimpunct($i)]++ | |
77 | } | |
78 | } | |
79 | if (inbrackets && substr($i, length($i), 1) == ")") { | |
80 | inbrackets = 0 | |
81 | } | |
82 | } | |
83 | } | |
84 | ||
85 | FNR == NR { | |
86 | prevline = $0 | |
87 | next | |
88 | } | |
89 | ||
90 | ||
91 | # Stage 2: process each file and find all sysctl tables | |
92 | BEGINFILE { | |
021622df | 93 | delete entries |
021622df SK |
94 | curtable = "" |
95 | curentry = "" | |
4f1136a5 | 96 | delete vars |
021622df SK |
97 | if (debug) print "Processing file " FILENAME |
98 | } | |
99 | ||
0f6588b3 TW |
100 | /^static( const)? struct ctl_table/ { |
101 | match($0, /static( const)? struct ctl_table ([^][]+)/, tables) | |
102 | curtable = tables[2] | |
021622df SK |
103 | if (debug) print "Processing table " curtable |
104 | } | |
105 | ||
106 | /^};$/ { | |
021622df SK |
107 | curtable = "" |
108 | curentry = "" | |
4f1136a5 | 109 | delete vars |
021622df SK |
110 | } |
111 | ||
021622df SK |
112 | curtable && /\.procname[\t ]*=[\t ]*".+"/ { |
113 | match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) | |
114 | curentry = names[1] | |
115 | if (debug) print "Adding entry " curentry " to table " curtable | |
116 | entries[curtable][curentry]++ | |
117 | file[curentry] = FILENAME | |
118 | } | |
119 | ||
0f6588b3 TW |
120 | /register_sysctl.*/ { |
121 | match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) | |
122 | if (debug) print "Registering table " tables[3] " at " tables[2] | |
123 | if (tables[2] == table) { | |
124 | for (entry in entries[tables[3]]) { | |
125 | printentry(entry) | |
126 | } | |
127 | } | |
021622df SK |
128 | } |
129 | ||
4f1136a5 TW |
130 | /kmemdup.*/ { |
131 | match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) | |
132 | if (debug) print "Found variable " names[1] " for table " names[2] | |
133 | if (names[2] in entries) { | |
134 | vars[names[1]] = names[2] | |
135 | } | |
136 | } | |
137 | ||
138 | /__register_sysctl_table.*/ { | |
139 | match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) | |
140 | if (debug) print "Registering variable table " tables[2] " at " tables[1] | |
141 | if (tables[1] == table && tables[2] in vars) { | |
142 | for (entry in entries[vars[tables[2]]]) { | |
143 | printentry(entry) | |
144 | } | |
145 | } | |
146 | } | |
147 | ||
021622df SK |
148 | END { |
149 | for (entry in documented) { | |
150 | if (!seen[entry]) { | |
151 | print "No implementation for " entry | |
152 | } | |
153 | } | |
154 | } |