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