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