diff options
Diffstat (limited to 'scripts/check-sysctl-docs')
| -rwxr-xr-x | scripts/check-sysctl-docs | 181 | 
1 files changed, 181 insertions, 0 deletions
diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs new file mode 100755 index 000000000000..8bcb9e26c7bc --- /dev/null +++ b/scripts/check-sysctl-docs @@ -0,0 +1,181 @@ +#!/usr/bin/gawk -f +# SPDX-License-Identifier: GPL-2.0 + +# Script to check sysctl documentation against source files +# +# Copyright (c) 2020 Stephen Kitt + +# Example invocation: +#	scripts/check-sysctl-docs -vtable="kernel" \ +#		Documentation/admin-guide/sysctl/kernel.rst \ +#		$(git grep -l register_sysctl_) +# +# Specify -vdebug=1 to see debugging information + +BEGIN { +    if (!table) { +	print "Please specify the table to look for using the table variable" > "/dev/stderr" +	exit 1 +    } +} + +# The following globals are used: +# children: maps ctl_table names and procnames to child ctl_table names +# documented: maps documented entries (each key is an entry) +# entries: maps ctl_table names and procnames to counts (so +#          enumerating the subkeys for a given ctl_table lists its +#          procnames) +# files: maps procnames to source file names +# paths: maps ctl_path names to paths +# curpath: the name of the current ctl_path struct +# curtable: the name of the current ctl_table struct +# curentry: the name of the current proc entry (procname when parsing +#           a ctl_table, constructed path when parsing a ctl_path) + + +# Remove punctuation from the given value +function trimpunct(value) { +    while (value ~ /^["&]/) { +	value = substr(value, 2) +    } +    while (value ~ /[]["&,}]$/) { +	value = substr(value, 1, length(value) - 1) +    } +    return value +} + +# Print the information for the given entry +function printentry(entry) { +    seen[entry]++ +    printf "* %s from %s", entry, file[entry] +    if (documented[entry]) { +	printf " (documented)" +    } +    print "" +} + + +# Stage 1: build the list of documented entries +FNR == NR && /^=+$/ { +    if (prevline ~ /Documentation for/) { +	# This is the main title +	next +    } + +    # The previous line is a section title, parse it +    $0 = prevline +    if (debug) print "Parsing " $0 +    inbrackets = 0 +    for (i = 1; i <= NF; i++) { +	if (length($i) == 0) { +	    continue +	} +	if (!inbrackets && substr($i, 1, 1) == "(") { +	    inbrackets = 1 +	} +	if (!inbrackets) { +	    token = trimpunct($i) +	    if (length(token) > 0 && token != "and") { +		if (debug) print trimpunct($i) +		documented[trimpunct($i)]++ +	    } +	} +	if (inbrackets && substr($i, length($i), 1) == ")") { +	    inbrackets = 0 +	} +    } +} + +FNR == NR { +    prevline = $0 +    next +} + + +# Stage 2: process each file and find all sysctl tables +BEGINFILE { +    delete children +    delete entries +    delete paths +    curpath = "" +    curtable = "" +    curentry = "" +    if (debug) print "Processing file " FILENAME +} + +/^static struct ctl_path/ { +    match($0, /static struct ctl_path ([^][]+)/, tables) +    curpath = tables[1] +    if (debug) print "Processing path " curpath +} + +/^static struct ctl_table/ { +    match($0, /static struct ctl_table ([^][]+)/, tables) +    curtable = tables[1] +    if (debug) print "Processing table " curtable +} + +/^};$/ { +    curpath = "" +    curtable = "" +    curentry = "" +} + +curpath && /\.procname[\t ]*=[\t ]*".+"/ { +    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) +    if (curentry) { +	curentry = curentry "/" names[1] +    } else { +	curentry = names[1] +    } +    if (debug) print "Setting path " curpath " to " curentry +    paths[curpath] = curentry +} + +curtable && /\.procname[\t ]*=[\t ]*".+"/ { +    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) +    curentry = names[1] +    if (debug) print "Adding entry " curentry " to table " curtable +    entries[curtable][curentry]++ +    file[curentry] = FILENAME +} + +/\.child[\t ]*=/ { +    child = trimpunct($NF) +    if (debug) print "Linking child " child " to table " curtable " entry " curentry +    children[curtable][curentry] = child +} + +/register_sysctl_table\(.*\)/ { +    match($0, /register_sysctl_table\(([^)]+)\)/, tables) +    if (debug) print "Registering table " tables[1] +    if (children[tables[1]][table]) { +	for (entry in entries[children[tables[1]][table]]) { +	    printentry(entry) +	} +    } +} + +/register_sysctl_paths\(.*\)/ { +    match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables) +    if (debug) print "Attaching table " tables[2] " to path " tables[1] +    if (paths[tables[1]] == table) { +	for (entry in entries[tables[2]]) { +	    printentry(entry) +	} +    } +    split(paths[tables[1]], components, "/") +    if (length(components) > 1 && components[1] == table) { +	# Count the first subdirectory as seen +	seen[components[2]]++ +    } +} + + +END { +    for (entry in documented) { +	if (!seen[entry]) { +	    print "No implementation for " entry +	} +    } +}  |