source: openpam/trunk/misc/gendoc.pl @ 385

Last change on this file since 385 was 320, checked in by Dag-Erling Smørgrav, 14 years ago

Convert $P4$ tags to $Id$.

  • Property svn:keywords set to Id
File size: 15.8 KB
Line 
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# $Id: gendoc.pl 320 2006-02-16 20:33:19Z des $
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 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
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        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
294sub expand_errors($);
295sub 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
344sub dictionary_order($$) {
345    my ($a, $b) = @_;
346
347    $a =~ s/[^[:alpha:]]//g;
348    $b =~ s/[^[:alpha:]]//g;
349    $a cmp $b;
350}
351
352sub 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
369sub 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
404The
405.Nm
406function 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
419The
420.Nm
421function returns
422.Dv NULL
423on 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
432function 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
442The
443.Nm
444function and this manual page were developed for the
445.Fx
446Project by ThinkSec AS and Network Associates Laboratories, the
447Security Research Division of Network Associates, Inc.\\& under
448DARPA/SPAWAR contract N66001-01-C-8035
449.Pq Dq CBOSS ,
450as 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
462sub 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
487sub 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
534The 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
558The OpenPAM library and this manual page were developed for the
559.Fx
560Project by ThinkSec AS and Network Associates Laboratories, the
561Security Research Division of Network Associates, Inc.\\& under
562DARPA/SPAWAR contract N66001-01-C-8035
563.Pq Dq CBOSS ,
564as part of the DARPA CHATS research program.
565";
566    close(FILE);
567}
568
569sub usage() {
570
571    print(STDERR "usage: gendoc [-s] source [...]\n");
572    exit(1);
573}
574
575MAIN:{
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}
Note: See TracBrowser for help on using the repository browser.