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

Last change on this file since 228 was 228, checked in by des, 12 years ago

Tweak the function-locating regexp to handle the slightly special
case of openpam_log(3) better. Also work around a mysterious Perl
bug by using a loop to emulate the 'g' regexp flag.

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