]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/openpam/misc/gendoc.pl
This commit was generated by cvs2svn to compensate for changes in r159985,
[FreeBSD/FreeBSD.git] / contrib / openpam / misc / gendoc.pl
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#30 $
36 #
37
38 use strict;
39 use locale;
40 use Fcntl;
41 use Getopt::Std;
42 use POSIX qw(locale_h strftime);
43 use 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 account 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
114 sub 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         3 => { 'pam' => 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{3}->{'pam_strerror'};
168     }
169
170     $argnames = $args;
171     # extract names of regular arguments
172     $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
173     # extract names of function pointer arguments
174     $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
175     # escape metacharacters (there shouldn't be any, but...)
176     $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
177     # separate argument names with |
178     $argnames =~ s/\" \"/|/g;
179     # and surround with ()
180     $argnames =~ s/^\"(.*)\"$/($1)/;
181     # $argnames is now a regexp that matches argument names
182     $inliteral = $inlist = 0;
183     foreach (split("\n", $source)) {
184         s/\s*$//;
185         if (!defined($man)) {
186             if (m/^\/\*\*$/) {
187                 $man = "";
188             }
189             next;
190         }
191         last if (m/^ \*\/$/);
192         s/^ \* ?//;
193         s/\\(.)/$1/gs;
194         if (m/^$/) {
195             if ($man ne "" && $man !~ m/\.Pp\n$/s) {
196                 if ($inliteral) {
197                     $man .= "\0\n";
198                 } elsif ($inlist) {
199                     $man .= ".El\n.Pp\n";
200                     $inlist = 0;
201                 } else {
202                     $man .= ".Pp\n";
203                 }
204             }
205             next;
206         }
207         if (m/^>(\w+)(\s+\d)?$/) {
208             my ($page, $sect) = ($1, $2 ? int($2) : 3);
209             ++$xref{$sect}->{$page};
210             next;
211         }
212         if (s/^\s+(=?\w+):\s*/.It $1/) {
213             if ($inliteral) {
214                 $man .= ".Ed\n";
215                 $inliteral = 0;
216             }
217             if (!$inlist) {
218                 $man =~ s/\.Pp\n$//s;
219                 $man .= ".Bl -tag -width 18n\n";
220                 $inlist = 1;
221             }
222             s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs;
223             $man .= "$_\n";
224             next;
225         } elsif ($inlist && m/^\S/) {
226             $man .= ".El\n.Pp\n";
227             $inlist = 0;
228         } elsif ($inliteral && m/^\S/) {
229             $man .= ".Ed\n";
230             $inliteral = 0;
231         } elsif ($inliteral) {
232             $man .= "$_\n";
233             next;
234         } elsif ($inlist) {
235             s/^\s+//;
236         } elsif (m/^\s+/) {
237             $man .= ".Bd -literal\n";
238             $inliteral = 1;
239             $man .= "$_\n";
240             next;
241         }
242         s/\s*=$func\b\s*/\n.Nm\n/gs;
243         s/\s*=$argnames\b\s*/\n.Fa $1\n/gs;
244         s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
245         s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs;
246         s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs;
247         while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
248             ++$xref{3}->{$1};
249         }
250         s/\s*\"(?=\w)/\n.Do\n/gs;
251         s/\"(?!\w)\s*/\n.Dc\n/gs;
252         s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
253         s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
254         s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
255         $man .= "$_\n";
256     }
257     if (defined($man)) {
258         if ($inlist) {
259             $man .= ".El\n";
260         }
261         if ($inliteral) {
262             $man .= ".Ed\n";
263         }
264         $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs;
265         $man =~ s/\s*$/\n/gm;
266         $man =~ s/\n+/\n/gs;
267         $man =~ s/\0//gs;
268         $man =~ s/\n\n\./\n\./gs;
269         chomp($man);
270     } else {
271         $man = "No description available.";
272     }
273
274     $FUNCTIONS{$func} = {
275         'source'        => $fn,
276         'name'          => $func,
277         'descr'         => $descr,
278         'type'          => $type,
279         'args'          => $args,
280         'man'           => $man,
281         'xref'          => \%xref,
282         'errors'        => \@errors,
283     };
284     if ($source =~ m/^ \* NODOC\s*$/m) {
285         $FUNCTIONS{$func}->{'nodoc'} = 1;
286     }
287     if ($source !~ m/^ \* XSSO \d/m) {
288         $FUNCTIONS{$func}->{'openpam'} = 1;
289     }
290     expand_errors($FUNCTIONS{$func});
291     return $FUNCTIONS{$func};
292 }
293
294 sub expand_errors($);
295 sub expand_errors($) {
296     my $func = shift;           # Ref to function hash
297
298     my %errors;
299     my $ref;
300     my $fn;
301
302     if (defined($func->{'recursed'})) {
303         warn("$func->{'name'}(): loop in error spec\n");
304         return qw();
305     }
306     $func->{'recursed'} = 1;
307
308     foreach (@{$func->{'errors'}}) {
309         if (m/^(PAM_[A-Z_]+)$/) {
310             if (!defined($PAMERR{$1})) {
311                 warn("$func->{'name'}(): unrecognized error: $1\n");
312                 next;
313             }
314             $errors{$1} = 1;
315         } elsif (m/^!(PAM_[A-Z_]+)$/) {
316             # treat negations separately
317         } elsif (m/^=([a-z_]+)$/) {
318             $ref = $1;
319             if (!defined($FUNCTIONS{$ref})) {
320                 $fn = $func->{'source'};
321                 $fn =~ s/$func->{'name'}/$ref/;
322                 parse_source($fn);
323             }
324             if (!defined($FUNCTIONS{$ref})) {
325                 warn("$func->{'name'}(): reference to unknown $ref()\n");
326                 next;
327             }
328             foreach (@{$FUNCTIONS{$ref}->{'errors'}}) {
329                 $errors{$_} = 1;
330             }
331         } else {
332             warn("$func->{'name'}(): invalid error specification: $_\n");
333         }
334     }
335     foreach (@{$func->{'errors'}}) {
336         if (m/^!(PAM_[A-Z_]+)$/) {
337             delete($errors{$1});
338         }
339     }
340     delete($func->{'recursed'});
341     $func->{'errors'} = [ sort(keys(%errors)) ];
342 }
343
344 sub dictionary_order($$) {
345     my ($a, $b) = @_;
346
347     $a =~ s/[^[:alpha:]]//g;
348     $b =~ s/[^[:alpha:]]//g;
349     $a cmp $b;
350 }
351
352 sub genxref($) {
353     my $xref = shift;           # References
354
355     my $mdoc = '';
356     my @refs = ();
357     foreach my $sect (sort(keys(%{$xref}))) {
358         foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
359             push(@refs, "$page $sect");
360         }
361     }
362     while ($_ = shift(@refs)) {
363         $mdoc .= ".Xr $_" .
364             (@refs ? " ,\n" : "\n");
365     }
366     return $mdoc;
367 }
368
369 sub gendoc($) {
370     my $func = shift;           # Ref to function hash
371
372     local *FILE;
373     my $mdoc;
374     my $fn;
375
376     return if defined($func->{'nodoc'});
377
378     $mdoc = "$COPYRIGHT
379 .Dd $TODAY
380 .Dt " . uc($func->{'name'}) . " 3
381 .Os
382 .Sh NAME
383 .Nm $func->{'name'}
384 .Nd $func->{'descr'}
385 .Sh LIBRARY
386 .Lb libpam
387 .Sh SYNOPSIS
388 .In sys/types.h
389 .In security/pam_appl.h
390 ";
391     if ($func->{'name'} =~ m/_sm_/) {
392         $mdoc .= ".In security/pam_modules.h\n"
393     }
394     if ($func->{'name'} =~ m/openpam/) {
395         $mdoc .= ".In security/openpam.h\n"
396     }
397     $mdoc .= ".Ft \"$func->{'type'}\"
398 .Fn $func->{'name'} $func->{'args'}
399 .Sh DESCRIPTION
400 $func->{'man'}
401 ";
402     if ($func->{'type'} eq "int") {
403         $mdoc .= ".Sh RETURN VALUES
404 The
405 .Nm
406 function returns one of the following values:
407 .Bl -tag -width 18n
408 ";
409         my @errors = @{$func->{'errors'}};
410         warn("$func->{'name'}(): no error specification\n")
411             unless(@errors);
412         foreach (@errors) {
413             $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
414         }
415         $mdoc .= ".El\n";
416     } else {
417         if ($func->{'type'} =~ m/\*$/) {
418             $mdoc .= ".Sh RETURN VALUES
419 The
420 .Nm
421 function returns
422 .Dv NULL
423 on failure.
424 ";
425         }
426     }
427     $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'});
428     $mdoc .= ".Sh STANDARDS\n";
429     if ($func->{'openpam'}) {
430         $mdoc .= "The
431 .Nm
432 function is an OpenPAM extension.
433 ";
434     } else {
435         $mdoc .= ".Rs
436 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
437 .%D \"June 1997\"
438 .Re
439 ";
440     }
441     $mdoc .= ".Sh AUTHORS
442 The
443 .Nm
444 function and this manual page were developed for the
445 .Fx
446 Project by ThinkSec AS and Network Associates Laboratories, the
447 Security Research Division of Network Associates, Inc.\\& under
448 DARPA/SPAWAR contract N66001-01-C-8035
449 .Pq Dq CBOSS ,
450 as part of the DARPA CHATS research program.
451 ";
452
453     $fn = "$func->{'name'}.3";
454     if (sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC)) {
455         print(FILE $mdoc);
456         close(FILE);
457     } else {
458         warn("$fn: open(): $!\n");
459     }
460 }
461
462 sub readproto($) {
463     my $fn = shift;             # File name
464
465     local *FILE;
466     my %func;
467
468     sysopen(FILE, $fn, O_RDONLY)
469         or die("$fn: open(): $!\n");
470     while (<FILE>) {
471         if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
472             $func{'Nm'} = $func{'Nm'} || $1;
473         } elsif (m/^\.Ft (\S.*?)\s*$/) {
474             $func{'Ft'} = $func{'Ft'} || $1;
475         } elsif (m/^\.Fn (\S.*?)\s*$/) {
476             $func{'Fn'} = $func{'Fn'} || $1;
477         }
478     }
479     close(FILE);
480     if ($func{'Nm'}) {
481         $FUNCTIONS{$func{'Nm'}} = \%func;
482     } else {
483         warn("No function found\n");
484     }
485 }
486
487 sub gensummary($) {
488     my $page = shift;           # Which page to produce
489
490     local *FILE;
491     my $upage;
492     my $func;
493     my %xref;
494
495     sysopen(FILE, "$page.3", O_RDWR|O_CREAT|O_TRUNC)
496         or die("$page.3: $!\n");
497
498     $upage = uc($page);
499     print FILE "$COPYRIGHT
500 .Dd $TODAY
501 .Dt $upage 3
502 .Os
503 .Sh NAME
504 ";
505     my @funcs = sort(keys(%FUNCTIONS));
506     while ($func = shift(@funcs)) {
507         print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
508         print FILE " ,"
509                 if (@funcs);
510         print FILE "\n";
511     }
512     print FILE ".Nd Pluggable Authentication Modules Library
513 .Sh LIBRARY
514 .Lb libpam
515 .Sh SYNOPSIS\n";
516     if ($page eq 'pam') {
517         print FILE ".In security/pam_appl.h\n";
518     } else {
519         print FILE ".In security/openpam.h\n";
520     }
521     foreach $func (sort(keys(%FUNCTIONS))) {
522         print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
523         print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
524     }
525     while (<STDIN>) {
526         if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
527             ++$xref{int($2)}->{$1};
528         }
529         print FILE $_;
530     }
531
532     if ($page eq 'pam') {
533         print FILE ".Sh RETURN VALUES
534 The following return codes are defined by
535 .In security/pam_constants.h :
536 .Bl -tag -width 18n
537 ";
538         foreach (sort(keys(%PAMERR))) {
539             print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
540         }
541         print FILE ".El\n";
542     }
543     print FILE ".Sh SEE ALSO
544 ";
545     if ($page eq 'pam') {
546         ++$xref{3}->{'openpam'};
547     }
548     foreach $func (keys(%FUNCTIONS)) {
549         ++$xref{3}->{$func};
550     }
551     print FILE genxref(\%xref);
552     print FILE ".Sh STANDARDS
553 .Rs
554 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
555 .%D \"June 1997\"
556 .Re
557 .Sh AUTHORS
558 The OpenPAM library and this manual page were developed for the
559 .Fx
560 Project by ThinkSec AS and Network Associates Laboratories, the
561 Security Research Division of Network Associates, Inc.\\& under
562 DARPA/SPAWAR contract N66001-01-C-8035
563 .Pq Dq CBOSS ,
564 as part of the DARPA CHATS research program.
565 ";
566     close(FILE);
567 }
568
569 sub usage() {
570
571     print(STDERR "usage: gendoc [-s] source [...]\n");
572     exit(1);
573 }
574
575 MAIN:{
576     my %opts;
577
578     usage()
579         unless (@ARGV && getopts("op", \%opts));
580     setlocale(LC_ALL, "en_US.ISO8859-1");
581     $TODAY = strftime("%B %e, %Y", localtime(time()));
582     $TODAY =~ s,\s+, ,g;
583     if ($opts{'o'} || $opts{'p'}) {
584         foreach my $fn (@ARGV) {
585             readproto($fn);
586         }
587         gensummary('openpam')
588             if ($opts{'o'});
589         gensummary('pam')
590             if ($opts{'p'});
591     } else {
592         foreach my $fn (@ARGV) {
593             my $func = parse_source($fn);
594             gendoc($func)
595                 if (defined($func));
596         }
597     }
598     exit(0);
599 }