check-sysctl-docs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #!/usr/bin/env -S gawk -f
  2. # SPDX-License-Identifier: GPL-2.0
  3. # Script to check sysctl documentation against source files
  4. #
  5. # Copyright (c) 2020 Stephen Kitt
  6. # Example invocation:
  7. # scripts/check-sysctl-docs -vtable="kernel" \
  8. # Documentation/admin-guide/sysctl/kernel.rst \
  9. # $(git grep -l register_sysctl)
  10. #
  11. # Specify -vdebug=1 to see debugging information
  12. BEGIN {
  13. if (!table) {
  14. print "Please specify the table to look for using the table variable" > "/dev/stderr"
  15. exit 1
  16. }
  17. # Documentation title skiplist
  18. skiplist[0] = "^Documentation for"
  19. skiplist[1] = "Network core options$"
  20. skiplist[2] = "POSIX message queues filesystem$"
  21. skiplist[3] = "Configuration options"
  22. skiplist[4] = ". /proc/sys/fs"
  23. skiplist[5] = "^Introduction$"
  24. skiplist[6] = "^seccomp$"
  25. skiplist[7] = "^pty$"
  26. skiplist[8] = "^firmware_config$"
  27. skiplist[9] = "^random$"
  28. }
  29. # The following globals are used:
  30. # documented: maps documented entries (each key is an entry)
  31. # entries: maps ctl_table names and procnames to counts (so
  32. # enumerating the subkeys for a given ctl_table lists its
  33. # procnames)
  34. # curtable: the name of the current ctl_table struct
  35. # curentry: the name of the current proc entry (procname when parsing
  36. # a ctl_table, constructed path when parsing a ctl_path)
  37. # Remove punctuation from the given value
  38. function trimpunct(value) {
  39. while (value ~ /^["&]/) {
  40. value = substr(value, 2)
  41. }
  42. while (value ~ /[]["&,}]$/) {
  43. value = substr(value, 1, length(value) - 1)
  44. }
  45. return value
  46. }
  47. # Print the information for the given entry
  48. function 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. # Stage 1: build the list of documented entries
  57. FNR == NR && /^=+$/ {
  58. for (i in skiplist) {
  59. if (prevline ~ skiplist[i]) {
  60. next
  61. }
  62. }
  63. # The previous line is a section title, parse it
  64. $0 = prevline
  65. if (debug) print "Parsing " $0
  66. inbrackets = 0
  67. for (i = 1; i <= NF; i++) {
  68. if (length($i) == 0) {
  69. continue
  70. }
  71. if (!inbrackets && substr($i, 1, 1) == "(") {
  72. inbrackets = 1
  73. }
  74. if (!inbrackets) {
  75. token = trimpunct($i)
  76. if (length(token) > 0 && token != "and") {
  77. if (debug) print trimpunct($i)
  78. documented[trimpunct($i)]++
  79. }
  80. }
  81. if (inbrackets && substr($i, length($i), 1) == ")") {
  82. inbrackets = 0
  83. }
  84. }
  85. }
  86. FNR == NR {
  87. prevline = $0
  88. next
  89. }
  90. # Stage 2: process each file and find all sysctl tables
  91. BEGINFILE {
  92. delete entries
  93. curtable = ""
  94. curentry = ""
  95. delete vars
  96. if (debug) print "Processing file " FILENAME
  97. }
  98. /^static( const)? struct ctl_table/ {
  99. match($0, /static( const)? struct ctl_table ([^][]+)/, tables)
  100. curtable = tables[2]
  101. if (debug) print "Processing table " curtable
  102. }
  103. /^};$/ {
  104. curtable = ""
  105. curentry = ""
  106. delete vars
  107. }
  108. curtable && /\.procname[\t ]*=[\t ]*".+"/ {
  109. match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
  110. curentry = names[1]
  111. if (debug) print "Adding entry " curentry " to table " curtable
  112. entries[curtable][curentry]++
  113. file[curentry] = FILENAME
  114. }
  115. curtable && /UCOUNT_ENTRY.*/ {
  116. match($0, /UCOUNT_ENTRY\("([^"]+)"\)/, names)
  117. curentry = names[1]
  118. if (debug) print "Adding entry " curentry " to table " curtable
  119. entries[curtable][curentry]++
  120. file[curentry] = FILENAME
  121. }
  122. /register_sysctl.*/ {
  123. match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables)
  124. if (debug) print "Registering table " tables[3] " at " tables[2]
  125. if (tables[2] == table) {
  126. for (entry in entries[tables[3]]) {
  127. printentry(entry)
  128. }
  129. }
  130. }
  131. /kmemdup.*/ {
  132. match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names)
  133. if (debug) print "Found variable " names[1] " for table " names[2]
  134. if (names[2] in entries) {
  135. vars[names[1]] = names[2]
  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. END {
  148. for (entry in documented) {
  149. if (!seen[entry])
  150. print "No implementation for " entry
  151. }
  152. }