| 1 | #!/usr/bin/perl -w |
|---|
| 2 | #- |
|---|
| 3 | # Copyright (c) 2002-2003 Networks Associates Technology, Inc. |
|---|
| 4 | # Copyright (c) 2004-2011 Dag-Erling Smørgrav |
|---|
| 5 | # All rights reserved. |
|---|
| 6 | # |
|---|
| 7 | # This software was developed for the FreeBSD Project by ThinkSec AS and |
|---|
| 8 | # Network Associates Laboratories, the Security Research Division of |
|---|
| 9 | # Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 |
|---|
| 10 | # ("CBOSS"), as part of the DARPA CHATS research program. |
|---|
| 11 | # |
|---|
| 12 | # Redistribution and use in source and binary forms, with or without |
|---|
| 13 | # modification, are permitted provided that the following conditions |
|---|
| 14 | # are met: |
|---|
| 15 | # 1. Redistributions of source code must retain the above copyright |
|---|
| 16 | # notice, this list of conditions and the following disclaimer. |
|---|
| 17 | # 2. Redistributions in binary form must reproduce the above copyright |
|---|
| 18 | # notice, this list of conditions and the following disclaimer in the |
|---|
| 19 | # documentation and/or other materials provided with the distribution. |
|---|
| 20 | # 3. The name of the author may not be used to endorse or promote |
|---|
| 21 | # products derived from this software without specific prior written |
|---|
| 22 | # permission. |
|---|
| 23 | # |
|---|
| 24 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|---|
| 25 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|---|
| 26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|---|
| 27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
|---|
| 28 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|---|
| 29 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|---|
| 30 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|---|
| 31 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|---|
| 32 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|---|
| 33 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|---|
| 34 | # SUCH DAMAGE. |
|---|
| 35 | # |
|---|
| 36 | # $Id$ |
|---|
| 37 | # |
|---|
| 38 | |
|---|
| 39 | use strict; |
|---|
| 40 | use locale; |
|---|
| 41 | use Fcntl; |
|---|
| 42 | use Getopt::Std; |
|---|
| 43 | use POSIX qw(locale_h strftime); |
|---|
| 44 | use vars qw($COPYRIGHT %AUTHORS $TODAY %FUNCTIONS %PAMERR); |
|---|
| 45 | |
|---|
| 46 | $COPYRIGHT = ".\\\"- |
|---|
| 47 | .\\\" Copyright (c) 2001-2003 Networks Associates Technology, Inc. |
|---|
| 48 | .\\\" Copyright (c) 2004-2011 Dag-Erling Smørgrav |
|---|
| 49 | .\\\" All rights reserved. |
|---|
| 50 | .\\\" |
|---|
| 51 | .\\\" This software was developed for the FreeBSD Project by ThinkSec AS and |
|---|
| 52 | .\\\" Network Associates Laboratories, the Security Research Division of |
|---|
| 53 | .\\\" Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 |
|---|
| 54 | .\\\" (\"CBOSS\"), as part of the DARPA CHATS research program. |
|---|
| 55 | .\\\" |
|---|
| 56 | .\\\" Redistribution and use in source and binary forms, with or without |
|---|
| 57 | .\\\" modification, are permitted provided that the following conditions |
|---|
| 58 | .\\\" are met: |
|---|
| 59 | .\\\" 1. Redistributions of source code must retain the above copyright |
|---|
| 60 | .\\\" notice, this list of conditions and the following disclaimer. |
|---|
| 61 | .\\\" 2. Redistributions in binary form must reproduce the above copyright |
|---|
| 62 | .\\\" notice, this list of conditions and the following disclaimer in the |
|---|
| 63 | .\\\" documentation and/or other materials provided with the distribution. |
|---|
| 64 | .\\\" 3. The name of the author may not be used to endorse or promote |
|---|
| 65 | .\\\" products derived from this software without specific prior written |
|---|
| 66 | .\\\" permission. |
|---|
| 67 | .\\\" |
|---|
| 68 | .\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|---|
| 69 | .\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|---|
| 70 | .\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|---|
| 71 | .\\\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
|---|
| 72 | .\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|---|
| 73 | .\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|---|
| 74 | .\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|---|
| 75 | .\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|---|
| 76 | .\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|---|
| 77 | .\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|---|
| 78 | .\\\" SUCH DAMAGE. |
|---|
| 79 | .\\\" |
|---|
| 80 | .\\\" \$" . "Id" . "\$ |
|---|
| 81 | .\\\""; |
|---|
| 82 | |
|---|
| 83 | %AUTHORS = ( |
|---|
| 84 | THINKSEC => "ThinkSec AS and Network Associates Laboratories, the |
|---|
| 85 | Security Research Division of Network Associates, Inc.\\& under |
|---|
| 86 | DARPA/SPAWAR contract N66001-01-C-8035 |
|---|
| 87 | .Pq Dq CBOSS , |
|---|
| 88 | as part of the DARPA CHATS research program.", |
|---|
| 89 | DES => ".An Dag-Erling Sm\\(/orgrav Aq des\@FreeBSD.org .", |
|---|
| 90 | ); |
|---|
| 91 | |
|---|
| 92 | %PAMERR = ( |
|---|
| 93 | PAM_SUCCESS => "Success", |
|---|
| 94 | PAM_OPEN_ERR => "Failed to load module", |
|---|
| 95 | PAM_SYMBOL_ERR => "Invalid symbol", |
|---|
| 96 | PAM_SERVICE_ERR => "Error in service module", |
|---|
| 97 | PAM_SYSTEM_ERR => "System error", |
|---|
| 98 | PAM_BUF_ERR => "Memory buffer error", |
|---|
| 99 | PAM_CONV_ERR => "Conversation failure", |
|---|
| 100 | PAM_PERM_DENIED => "Permission denied", |
|---|
| 101 | PAM_MAXTRIES => "Maximum number of tries exceeded", |
|---|
| 102 | PAM_AUTH_ERR => "Authentication error", |
|---|
| 103 | PAM_NEW_AUTHTOK_REQD => "New authentication token required", |
|---|
| 104 | PAM_CRED_INSUFFICIENT => "Insufficient credentials", |
|---|
| 105 | PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable", |
|---|
| 106 | PAM_USER_UNKNOWN => "Unknown user", |
|---|
| 107 | PAM_CRED_UNAVAIL => "Failed to retrieve user credentials", |
|---|
| 108 | PAM_CRED_EXPIRED => "User credentials have expired", |
|---|
| 109 | PAM_CRED_ERR => "Failed to set user credentials", |
|---|
| 110 | PAM_ACCT_EXPIRED => "User account has expired", |
|---|
| 111 | PAM_AUTHTOK_EXPIRED => "Password has expired", |
|---|
| 112 | PAM_SESSION_ERR => "Session failure", |
|---|
| 113 | PAM_AUTHTOK_ERR => "Authentication token failure", |
|---|
| 114 | PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token", |
|---|
| 115 | PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy", |
|---|
| 116 | PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled", |
|---|
| 117 | PAM_NO_MODULE_DATA => "Module data not found", |
|---|
| 118 | PAM_IGNORE => "Ignore this module", |
|---|
| 119 | PAM_ABORT => "General failure", |
|---|
| 120 | PAM_TRY_AGAIN => "Try again", |
|---|
| 121 | PAM_MODULE_UNKNOWN => "Unknown module type", |
|---|
| 122 | PAM_DOMAIN_UNKNOWN => "Unknown authentication domain", |
|---|
| 123 | ); |
|---|
| 124 | |
|---|
| 125 | sub parse_source($) { |
|---|
| 126 | my $fn = shift; |
|---|
| 127 | |
|---|
| 128 | local *FILE; |
|---|
| 129 | my $source; |
|---|
| 130 | my $func; |
|---|
| 131 | my $descr; |
|---|
| 132 | my $type; |
|---|
| 133 | my $args; |
|---|
| 134 | my $argnames; |
|---|
| 135 | my $man; |
|---|
| 136 | my $inlist; |
|---|
| 137 | my $intaglist; |
|---|
| 138 | my $inliteral; |
|---|
| 139 | my %xref; |
|---|
| 140 | my @errors; |
|---|
| 141 | my $author; |
|---|
| 142 | |
|---|
| 143 | if ($fn !~ m,\.c$,) { |
|---|
| 144 | warn("$fn: not C source, ignoring\n"); |
|---|
| 145 | return undef; |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | open(FILE, "<", "$fn") |
|---|
| 149 | or die("$fn: open(): $!\n"); |
|---|
| 150 | $source = join('', <FILE>); |
|---|
| 151 | close(FILE); |
|---|
| 152 | |
|---|
| 153 | return undef |
|---|
| 154 | if ($source =~ m/^ \* NOPARSE\s*$/m); |
|---|
| 155 | |
|---|
| 156 | $author = 'THINKSEC'; |
|---|
| 157 | if ($source =~ s/^ \* AUTHOR\s+(.*?)\s*$//m) { |
|---|
| 158 | $author = $1; |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | $func = $fn; |
|---|
| 162 | $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,; |
|---|
| 163 | if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) { |
|---|
| 164 | warn("$fn: can't find $func\n"); |
|---|
| 165 | return undef; |
|---|
| 166 | } |
|---|
| 167 | ($descr, $type, $args) = ($1, $2, $3); |
|---|
| 168 | $descr =~ s,^([A-Z][a-z]),lc($1),e; |
|---|
| 169 | $descr =~ s,[\.\s]*$,,; |
|---|
| 170 | while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) { |
|---|
| 171 | # nothing |
|---|
| 172 | } |
|---|
| 173 | $args =~ s/,\s+/, /gs; |
|---|
| 174 | $args = "\"$args\""; |
|---|
| 175 | |
|---|
| 176 | %xref = ( |
|---|
| 177 | 3 => { 'pam' => 1 }, |
|---|
| 178 | ); |
|---|
| 179 | |
|---|
| 180 | if ($type eq "int") { |
|---|
| 181 | foreach (split("\n", $source)) { |
|---|
| 182 | next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/); |
|---|
| 183 | push(@errors, $1); |
|---|
| 184 | } |
|---|
| 185 | ++$xref{3}->{'pam_strerror'}; |
|---|
| 186 | } |
|---|
| 187 | |
|---|
| 188 | $argnames = $args; |
|---|
| 189 | # extract names of regular arguments |
|---|
| 190 | $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g; |
|---|
| 191 | # extract names of function pointer arguments |
|---|
| 192 | $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g; |
|---|
| 193 | # escape metacharacters (there shouldn't be any, but...) |
|---|
| 194 | $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g; |
|---|
| 195 | # separate argument names with | |
|---|
| 196 | $argnames =~ s/\" \"/|/g; |
|---|
| 197 | # and surround with () |
|---|
| 198 | $argnames =~ s/^\"(.*)\"$/$1/; |
|---|
| 199 | # $argnames is now a regexp that matches argument names |
|---|
| 200 | $inliteral = $inlist = $intaglist = 0; |
|---|
| 201 | foreach (split("\n", $source)) { |
|---|
| 202 | s/\s*$//; |
|---|
| 203 | if (!defined($man)) { |
|---|
| 204 | if (m/^\/\*\*$/) { |
|---|
| 205 | $man = ""; |
|---|
| 206 | } |
|---|
| 207 | next; |
|---|
| 208 | } |
|---|
| 209 | last if (m/^ \*\/$/); |
|---|
| 210 | s/^ \* ?//; |
|---|
| 211 | s/\\(.)/$1/gs; |
|---|
| 212 | if (m/^$/) { |
|---|
| 213 | # paragraph separator |
|---|
| 214 | if ($man ne "" && $man !~ m/\.Pp\n$/s) { |
|---|
| 215 | if ($inliteral) { |
|---|
| 216 | $man .= "\0\n"; |
|---|
| 217 | } elsif ($inlist || $intaglist) { |
|---|
| 218 | $man .= ".El\n.Pp\n"; |
|---|
| 219 | $inlist = $intaglist = 0; |
|---|
| 220 | } else { |
|---|
| 221 | $man .= ".Pp\n"; |
|---|
| 222 | } |
|---|
| 223 | } |
|---|
| 224 | next; |
|---|
| 225 | } |
|---|
| 226 | if (m/^>(\w+)(\s+\d)?$/) { |
|---|
| 227 | # "see also" cross-reference |
|---|
| 228 | my ($page, $sect) = ($1, $2 ? int($2) : 3); |
|---|
| 229 | ++$xref{$sect}->{$page}; |
|---|
| 230 | next; |
|---|
| 231 | } |
|---|
| 232 | if (s/^\s+-\s+//) { |
|---|
| 233 | # item in bullet list |
|---|
| 234 | if ($inliteral) { |
|---|
| 235 | $man .= ".Ed\n"; |
|---|
| 236 | $inliteral = 0; |
|---|
| 237 | } |
|---|
| 238 | if ($intaglist) { |
|---|
| 239 | $man .= ".El\n.Pp\n"; |
|---|
| 240 | $intaglist = 0; |
|---|
| 241 | } |
|---|
| 242 | if (!$inlist) { |
|---|
| 243 | $man =~ s/\.Pp\n$//s; |
|---|
| 244 | $man .= ".Bl -bullet\n"; |
|---|
| 245 | $inlist = 1; |
|---|
| 246 | } |
|---|
| 247 | $man .= ".It\n"; |
|---|
| 248 | # fall through |
|---|
| 249 | } elsif (s/^\s+(\S+):\s*/.It $1/) { |
|---|
| 250 | # item in tag list |
|---|
| 251 | if ($inliteral) { |
|---|
| 252 | $man .= ".Ed\n"; |
|---|
| 253 | $inliteral = 0; |
|---|
| 254 | } |
|---|
| 255 | if ($inlist) { |
|---|
| 256 | $man .= ".El\n.Pp\n"; |
|---|
| 257 | $inlist = 0; |
|---|
| 258 | } |
|---|
| 259 | if (!$intaglist) { |
|---|
| 260 | $man =~ s/\.Pp\n$//s; |
|---|
| 261 | $man .= ".Bl -tag -width 18n\n"; |
|---|
| 262 | $intaglist = 1; |
|---|
| 263 | } |
|---|
| 264 | s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs; |
|---|
| 265 | $man .= "$_\n"; |
|---|
| 266 | next; |
|---|
| 267 | } elsif (($inlist || $intaglist) && m/^\S/) { |
|---|
| 268 | # regular text after list |
|---|
| 269 | $man .= ".El\n.Pp\n"; |
|---|
| 270 | $inlist = $intaglist = 0; |
|---|
| 271 | } elsif ($inliteral && m/^\S/) { |
|---|
| 272 | # regular text after literal section |
|---|
| 273 | $man .= ".Ed\n"; |
|---|
| 274 | $inliteral = 0; |
|---|
| 275 | } elsif ($inliteral) { |
|---|
| 276 | # additional text within literal section |
|---|
| 277 | $man .= "$_\n"; |
|---|
| 278 | next; |
|---|
| 279 | } elsif ($inlist || $intaglist) { |
|---|
| 280 | # additional text within list |
|---|
| 281 | s/^\s+//; |
|---|
| 282 | } elsif (m/^\s+/) { |
|---|
| 283 | # new literal section |
|---|
| 284 | $man .= ".Bd -literal\n"; |
|---|
| 285 | $inliteral = 1; |
|---|
| 286 | $man .= "$_\n"; |
|---|
| 287 | next; |
|---|
| 288 | } |
|---|
| 289 | s/\s*=($func)\b\s*/\n.Fn $1\n/gs; |
|---|
| 290 | s/\s*=($argnames)\b\s*/\n.Fa $1\n/gs; |
|---|
| 291 | s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs; |
|---|
| 292 | s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs; |
|---|
| 293 | s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs; |
|---|
| 294 | while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) { |
|---|
| 295 | ++$xref{3}->{$1}; |
|---|
| 296 | } |
|---|
| 297 | s/\s*\"(?=\w)/\n.Do\n/gs; |
|---|
| 298 | s/\"(?!\w)\s*/\n.Dc\n/gs; |
|---|
| 299 | s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs; |
|---|
| 300 | s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs; |
|---|
| 301 | s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs; |
|---|
| 302 | $man .= "$_\n"; |
|---|
| 303 | } |
|---|
| 304 | if (defined($man)) { |
|---|
| 305 | if ($inlist || $intaglist) { |
|---|
| 306 | $man .= ".El\n"; |
|---|
| 307 | $inlist = $intaglist = 0; |
|---|
| 308 | } |
|---|
| 309 | if ($inliteral) { |
|---|
| 310 | $man .= ".Ed\n"; |
|---|
| 311 | $inliteral = 0; |
|---|
| 312 | } |
|---|
| 313 | $man =~ s/\%/\\&\%/gs; |
|---|
| 314 | $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([.,:;-])\s+/$1 $2\n/gs; |
|---|
| 315 | $man =~ s/\s*$/\n/gm; |
|---|
| 316 | $man =~ s/\n+/\n/gs; |
|---|
| 317 | $man =~ s/\0//gs; |
|---|
| 318 | $man =~ s/\n\n\./\n\./gs; |
|---|
| 319 | chomp($man); |
|---|
| 320 | } else { |
|---|
| 321 | $man = "No description available."; |
|---|
| 322 | } |
|---|
| 323 | |
|---|
| 324 | $FUNCTIONS{$func} = { |
|---|
| 325 | 'source' => $fn, |
|---|
| 326 | 'name' => $func, |
|---|
| 327 | 'descr' => $descr, |
|---|
| 328 | 'type' => $type, |
|---|
| 329 | 'args' => $args, |
|---|
| 330 | 'man' => $man, |
|---|
| 331 | 'xref' => \%xref, |
|---|
| 332 | 'errors' => \@errors, |
|---|
| 333 | 'author' => $author, |
|---|
| 334 | }; |
|---|
| 335 | if ($source =~ m/^ \* NODOC\s*$/m) { |
|---|
| 336 | $FUNCTIONS{$func}->{'nodoc'} = 1; |
|---|
| 337 | } |
|---|
| 338 | if ($source !~ m/^ \* XSSO \d/m) { |
|---|
| 339 | $FUNCTIONS{$func}->{'openpam'} = 1; |
|---|
| 340 | } |
|---|
| 341 | expand_errors($FUNCTIONS{$func}); |
|---|
| 342 | return $FUNCTIONS{$func}; |
|---|
| 343 | } |
|---|
| 344 | |
|---|
| 345 | sub expand_errors($); |
|---|
| 346 | sub expand_errors($) { |
|---|
| 347 | my $func = shift; # Ref to function hash |
|---|
| 348 | |
|---|
| 349 | my %errors; |
|---|
| 350 | my $ref; |
|---|
| 351 | my $fn; |
|---|
| 352 | |
|---|
| 353 | if (defined($func->{'recursed'})) { |
|---|
| 354 | warn("$func->{'name'}(): loop in error spec\n"); |
|---|
| 355 | return qw(); |
|---|
| 356 | } |
|---|
| 357 | $func->{'recursed'} = 1; |
|---|
| 358 | |
|---|
| 359 | foreach (@{$func->{'errors'}}) { |
|---|
| 360 | if (m/^(PAM_[A-Z_]+)$/) { |
|---|
| 361 | if (!defined($PAMERR{$1})) { |
|---|
| 362 | warn("$func->{'name'}(): unrecognized error: $1\n"); |
|---|
| 363 | next; |
|---|
| 364 | } |
|---|
| 365 | $errors{$1} = 1; |
|---|
| 366 | } elsif (m/^!(PAM_[A-Z_]+)$/) { |
|---|
| 367 | # treat negations separately |
|---|
| 368 | } elsif (m/^=([a-z_]+)$/) { |
|---|
| 369 | $ref = $1; |
|---|
| 370 | if (!defined($FUNCTIONS{$ref})) { |
|---|
| 371 | $fn = $func->{'source'}; |
|---|
| 372 | $fn =~ s/$func->{'name'}/$ref/; |
|---|
| 373 | parse_source($fn); |
|---|
| 374 | } |
|---|
| 375 | if (!defined($FUNCTIONS{$ref})) { |
|---|
| 376 | warn("$func->{'name'}(): reference to unknown $ref()\n"); |
|---|
| 377 | next; |
|---|
| 378 | } |
|---|
| 379 | foreach (@{$FUNCTIONS{$ref}->{'errors'}}) { |
|---|
| 380 | $errors{$_} = 1; |
|---|
| 381 | } |
|---|
| 382 | } else { |
|---|
| 383 | warn("$func->{'name'}(): invalid error specification: $_\n"); |
|---|
| 384 | } |
|---|
| 385 | } |
|---|
| 386 | foreach (@{$func->{'errors'}}) { |
|---|
| 387 | if (m/^!(PAM_[A-Z_]+)$/) { |
|---|
| 388 | delete($errors{$1}); |
|---|
| 389 | } |
|---|
| 390 | } |
|---|
| 391 | delete($func->{'recursed'}); |
|---|
| 392 | $func->{'errors'} = [ sort(keys(%errors)) ]; |
|---|
| 393 | } |
|---|
| 394 | |
|---|
| 395 | sub dictionary_order($$) { |
|---|
| 396 | my ($a, $b) = @_; |
|---|
| 397 | |
|---|
| 398 | $a =~ s/[^[:alpha:]]//g; |
|---|
| 399 | $b =~ s/[^[:alpha:]]//g; |
|---|
| 400 | $a cmp $b; |
|---|
| 401 | } |
|---|
| 402 | |
|---|
| 403 | sub genxref($) { |
|---|
| 404 | my $xref = shift; # References |
|---|
| 405 | |
|---|
| 406 | my $mdoc = ''; |
|---|
| 407 | my @refs = (); |
|---|
| 408 | foreach my $sect (sort(keys(%{$xref}))) { |
|---|
| 409 | foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) { |
|---|
| 410 | push(@refs, "$page $sect"); |
|---|
| 411 | } |
|---|
| 412 | } |
|---|
| 413 | while ($_ = shift(@refs)) { |
|---|
| 414 | $mdoc .= ".Xr $_" . |
|---|
| 415 | (@refs ? " ,\n" : "\n"); |
|---|
| 416 | } |
|---|
| 417 | return $mdoc; |
|---|
| 418 | } |
|---|
| 419 | |
|---|
| 420 | sub gendoc($) { |
|---|
| 421 | my $func = shift; # Ref to function hash |
|---|
| 422 | |
|---|
| 423 | local *FILE; |
|---|
| 424 | my $mdoc; |
|---|
| 425 | my $fn; |
|---|
| 426 | |
|---|
| 427 | return if defined($func->{'nodoc'}); |
|---|
| 428 | |
|---|
| 429 | $mdoc = "$COPYRIGHT |
|---|
| 430 | .Dd $TODAY |
|---|
| 431 | .Dt " . uc($func->{'name'}) . " 3 |
|---|
| 432 | .Os |
|---|
| 433 | .Sh NAME |
|---|
| 434 | .Nm $func->{'name'} |
|---|
| 435 | .Nd $func->{'descr'} |
|---|
| 436 | .Sh LIBRARY |
|---|
| 437 | .Lb libpam |
|---|
| 438 | .Sh SYNOPSIS |
|---|
| 439 | .In sys/types.h |
|---|
| 440 | .In security/pam_appl.h |
|---|
| 441 | "; |
|---|
| 442 | if ($func->{'name'} =~ m/_sm_/) { |
|---|
| 443 | $mdoc .= ".In security/pam_modules.h\n" |
|---|
| 444 | } |
|---|
| 445 | if ($func->{'name'} =~ m/openpam/) { |
|---|
| 446 | $mdoc .= ".In security/openpam.h\n" |
|---|
| 447 | } |
|---|
| 448 | $mdoc .= ".Ft \"$func->{'type'}\" |
|---|
| 449 | .Fn $func->{'name'} $func->{'args'} |
|---|
| 450 | .Sh DESCRIPTION |
|---|
| 451 | $func->{'man'} |
|---|
| 452 | "; |
|---|
| 453 | my @errors = @{$func->{'errors'}}; |
|---|
| 454 | if ($func->{'type'} eq "int" && @errors) { |
|---|
| 455 | $mdoc .= ".Sh RETURN VALUES |
|---|
| 456 | The |
|---|
| 457 | .Nm |
|---|
| 458 | function returns one of the following values: |
|---|
| 459 | .Bl -tag -width 18n |
|---|
| 460 | "; |
|---|
| 461 | foreach (@errors) { |
|---|
| 462 | $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n"; |
|---|
| 463 | } |
|---|
| 464 | $mdoc .= ".El\n"; |
|---|
| 465 | } elsif ($func->{'type'} eq "int") { |
|---|
| 466 | $mdoc .= ".Sh RETURN VALUES |
|---|
| 467 | The |
|---|
| 468 | .Nm |
|---|
| 469 | function returns 0 on success and -1 on failure. |
|---|
| 470 | "; |
|---|
| 471 | } elsif ($func->{'type'} =~ m/\*$/) { |
|---|
| 472 | $mdoc .= ".Sh RETURN VALUES |
|---|
| 473 | The |
|---|
| 474 | .Nm |
|---|
| 475 | function returns |
|---|
| 476 | .Dv NULL |
|---|
| 477 | on failure. |
|---|
| 478 | "; |
|---|
| 479 | } elsif ($func->{'type'} ne "void") { |
|---|
| 480 | warn("$func->{'name'}(): no error specification\n"); |
|---|
| 481 | } |
|---|
| 482 | $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'}); |
|---|
| 483 | $mdoc .= ".Sh STANDARDS\n"; |
|---|
| 484 | if ($func->{'openpam'}) { |
|---|
| 485 | $mdoc .= "The |
|---|
| 486 | .Nm |
|---|
| 487 | function is an OpenPAM extension. |
|---|
| 488 | "; |
|---|
| 489 | } else { |
|---|
| 490 | $mdoc .= ".Rs |
|---|
| 491 | .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" |
|---|
| 492 | .%D \"June 1997\" |
|---|
| 493 | .Re |
|---|
| 494 | "; |
|---|
| 495 | } |
|---|
| 496 | $mdoc .= ".Sh AUTHORS |
|---|
| 497 | The |
|---|
| 498 | .Nm |
|---|
| 499 | function and this manual page were developed for the |
|---|
| 500 | .Fx |
|---|
| 501 | Project by\n" . $AUTHORS{$func->{'author'} // 'THINKSEC_DARPA'} . "\n"; |
|---|
| 502 | $fn = "$func->{'name'}.3"; |
|---|
| 503 | if (open(FILE, ">", $fn)) { |
|---|
| 504 | print(FILE $mdoc); |
|---|
| 505 | close(FILE); |
|---|
| 506 | } else { |
|---|
| 507 | warn("$fn: open(): $!\n"); |
|---|
| 508 | } |
|---|
| 509 | } |
|---|
| 510 | |
|---|
| 511 | sub readproto($) { |
|---|
| 512 | my $fn = shift; # File name |
|---|
| 513 | |
|---|
| 514 | local *FILE; |
|---|
| 515 | my %func; |
|---|
| 516 | |
|---|
| 517 | open(FILE, "<", "$fn") |
|---|
| 518 | or die("$fn: open(): $!\n"); |
|---|
| 519 | while (<FILE>) { |
|---|
| 520 | if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) { |
|---|
| 521 | $func{'Nm'} = $func{'Nm'} || $1; |
|---|
| 522 | } elsif (m/^\.Ft (\S.*?)\s*$/) { |
|---|
| 523 | $func{'Ft'} = $func{'Ft'} || $1; |
|---|
| 524 | } elsif (m/^\.Fn (\S.*?)\s*$/) { |
|---|
| 525 | $func{'Fn'} = $func{'Fn'} || $1; |
|---|
| 526 | } |
|---|
| 527 | } |
|---|
| 528 | close(FILE); |
|---|
| 529 | if ($func{'Nm'}) { |
|---|
| 530 | $FUNCTIONS{$func{'Nm'}} = \%func; |
|---|
| 531 | } else { |
|---|
| 532 | warn("No function found\n"); |
|---|
| 533 | } |
|---|
| 534 | } |
|---|
| 535 | |
|---|
| 536 | sub gensummary($) { |
|---|
| 537 | my $page = shift; # Which page to produce |
|---|
| 538 | |
|---|
| 539 | local *FILE; |
|---|
| 540 | my $upage; |
|---|
| 541 | my $func; |
|---|
| 542 | my %xref; |
|---|
| 543 | |
|---|
| 544 | open(FILE, ">", "$page.3") |
|---|
| 545 | or die("$page.3: $!\n"); |
|---|
| 546 | |
|---|
| 547 | $page =~ m/(\w+)$/; |
|---|
| 548 | $upage = uc($1); |
|---|
| 549 | print FILE "$COPYRIGHT |
|---|
| 550 | .Dd $TODAY |
|---|
| 551 | .Dt $upage 3 |
|---|
| 552 | .Os |
|---|
| 553 | .Sh NAME |
|---|
| 554 | "; |
|---|
| 555 | my @funcs = sort(keys(%FUNCTIONS)); |
|---|
| 556 | while ($func = shift(@funcs)) { |
|---|
| 557 | print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}"; |
|---|
| 558 | print FILE " ," |
|---|
| 559 | if (@funcs); |
|---|
| 560 | print FILE "\n"; |
|---|
| 561 | } |
|---|
| 562 | print FILE ".Nd Pluggable Authentication Modules Library |
|---|
| 563 | .Sh LIBRARY |
|---|
| 564 | .Lb libpam |
|---|
| 565 | .Sh SYNOPSIS\n"; |
|---|
| 566 | if ($page eq 'pam') { |
|---|
| 567 | print FILE ".In security/pam_appl.h\n"; |
|---|
| 568 | } else { |
|---|
| 569 | print FILE ".In security/openpam.h\n"; |
|---|
| 570 | } |
|---|
| 571 | foreach $func (sort(keys(%FUNCTIONS))) { |
|---|
| 572 | print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n"; |
|---|
| 573 | print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n"; |
|---|
| 574 | } |
|---|
| 575 | while (<STDIN>) { |
|---|
| 576 | if (m/^\.Xr (\S+)\s*(\d)\s*$/) { |
|---|
| 577 | ++$xref{int($2)}->{$1}; |
|---|
| 578 | } |
|---|
| 579 | print FILE $_; |
|---|
| 580 | } |
|---|
| 581 | |
|---|
| 582 | if ($page eq 'pam') { |
|---|
| 583 | print FILE ".Sh RETURN VALUES |
|---|
| 584 | The following return codes are defined by |
|---|
| 585 | .In security/pam_constants.h : |
|---|
| 586 | .Bl -tag -width 18n |
|---|
| 587 | "; |
|---|
| 588 | foreach (sort(keys(%PAMERR))) { |
|---|
| 589 | print FILE ".It Bq Er $_\n$PAMERR{$_}.\n"; |
|---|
| 590 | } |
|---|
| 591 | print FILE ".El\n"; |
|---|
| 592 | } |
|---|
| 593 | print FILE ".Sh SEE ALSO |
|---|
| 594 | "; |
|---|
| 595 | if ($page eq 'pam') { |
|---|
| 596 | ++$xref{3}->{'openpam'}; |
|---|
| 597 | } |
|---|
| 598 | foreach $func (keys(%FUNCTIONS)) { |
|---|
| 599 | ++$xref{3}->{$func}; |
|---|
| 600 | } |
|---|
| 601 | print FILE genxref(\%xref); |
|---|
| 602 | print FILE ".Sh STANDARDS |
|---|
| 603 | .Rs |
|---|
| 604 | .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" |
|---|
| 605 | .%D \"June 1997\" |
|---|
| 606 | .Re |
|---|
| 607 | .Sh AUTHORS |
|---|
| 608 | The OpenPAM library and this manual page were developed for the |
|---|
| 609 | .Fx |
|---|
| 610 | Project by ThinkSec AS and Network Associates Laboratories, the |
|---|
| 611 | Security Research Division of Network Associates, Inc.\\& under |
|---|
| 612 | DARPA/SPAWAR contract N66001-01-C-8035 |
|---|
| 613 | .Pq Dq CBOSS , |
|---|
| 614 | as part of the DARPA CHATS research program. |
|---|
| 615 | "; |
|---|
| 616 | close(FILE); |
|---|
| 617 | } |
|---|
| 618 | |
|---|
| 619 | sub usage() { |
|---|
| 620 | |
|---|
| 621 | print(STDERR "usage: gendoc [-op] source [...]\n"); |
|---|
| 622 | exit(1); |
|---|
| 623 | } |
|---|
| 624 | |
|---|
| 625 | MAIN:{ |
|---|
| 626 | my %opts; |
|---|
| 627 | |
|---|
| 628 | usage() |
|---|
| 629 | unless (@ARGV && getopts("op", \%opts)); |
|---|
| 630 | setlocale(LC_ALL, "en_US.UTF-8"); |
|---|
| 631 | $TODAY = strftime("%B %e, %Y", localtime(time())); |
|---|
| 632 | $TODAY =~ s,\s+, ,g; |
|---|
| 633 | if ($opts{'o'} || $opts{'p'}) { |
|---|
| 634 | foreach my $fn (@ARGV) { |
|---|
| 635 | readproto($fn); |
|---|
| 636 | } |
|---|
| 637 | gensummary('openpam') |
|---|
| 638 | if ($opts{'o'}); |
|---|
| 639 | gensummary('pam') |
|---|
| 640 | if ($opts{'p'}); |
|---|
| 641 | } else { |
|---|
| 642 | foreach my $fn (@ARGV) { |
|---|
| 643 | my $func = parse_source($fn); |
|---|
| 644 | gendoc($func) |
|---|
| 645 | if (defined($func)); |
|---|
| 646 | } |
|---|
| 647 | } |
|---|
| 648 | exit(0); |
|---|
| 649 | } |
|---|