gendoc.pl revision 108794
1#!/usr/bin/perl -w
2#-
3# Copyright (c) 2002 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#19 $
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) 2002 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	if (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/gs) {
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	$FUNCTIONS{$func}->{'nolist'} = 1;
278    }
279    if ($source =~ m/^ \* NOLIST\s*$/m) {
280	$FUNCTIONS{$func}->{'nolist'} = 1;
281    }
282    if ($source !~ m/^ \* XSSO \d/m) {
283	$FUNCTIONS{$func}->{'openpam'} = 1;
284    }
285    expand_errors($FUNCTIONS{$func});
286    return $FUNCTIONS{$func};
287}
288
289sub expand_errors($);
290sub expand_errors($) {
291    my $func = shift;		# Ref to function hash
292
293    my %errors;
294    my $ref;
295    my $fn;
296
297    if (defined($func->{'recursed'})) {
298	warn("$func->{'name'}(): loop in error spec\n");
299	return qw();
300    }
301    $func->{'recursed'} = 1;
302
303    foreach (@{$func->{'errors'}}) {
304	if (m/^(PAM_[A-Z_]+)$/) {
305	    if (!defined($PAMERR{$1})) {
306		warn("$func->{'name'}(): unrecognized error: $1\n");
307		next;
308	    }
309	    $errors{$1} = 1;
310	} elsif (m/^!(PAM_[A-Z_]+)$/) {
311	    # treat negations separately
312	} elsif (m/^=([a-z_]+)$/) {
313	    $ref = $1;
314	    if (!defined($FUNCTIONS{$ref})) {
315		$fn = $func->{'source'};
316		$fn =~ s/$func->{'name'}/$ref/;
317		parse_source($fn);
318	    }
319	    if (!defined($FUNCTIONS{$ref})) {
320		warn("$func->{'name'}(): reference to unknown $ref()\n");
321		next;
322	    }
323	    foreach (@{$FUNCTIONS{$ref}->{'errors'}}) {
324		$errors{$_} = 1;
325	    }
326	} else {
327	    warn("$func->{'name'}(): invalid error specification: $_\n");
328	}
329    }
330    foreach (@{$func->{'errors'}}) {
331	if (m/^!(PAM_[A-Z_]+)$/) {
332	    delete($errors{$1});
333	}
334    }
335    delete($func->{'recursed'});
336    $func->{'errors'} = [ sort(keys(%errors)) ];
337}
338
339sub gendoc($) {
340    my $func = shift;		# Ref to function hash
341
342    local *FILE;
343    my $mdoc;
344    my $fn;
345
346    return if defined($func->{'nodoc'});
347
348    $mdoc = "$COPYRIGHT
349.Dd $TODAY
350.Dt " . uc($func->{'name'}) . " 3
351.Os
352.Sh NAME
353.Nm $func->{'name'}
354.Nd $func->{'descr'}
355.Sh LIBRARY
356.Lb libpam
357.Sh SYNOPSIS
358.In sys/types.h
359.In security/pam_appl.h
360";
361    if ($func->{'name'} =~ m/_sm_/) {
362	$mdoc .= ".In security/pam_modules.h\n"
363    }
364    if ($func->{'name'} =~ m/openpam/) {
365	$mdoc .= ".In security/openpam.h\n"
366    }
367    $mdoc .= ".Ft $func->{'type'}
368.Fn $func->{'name'} $func->{'args'}
369.Sh DESCRIPTION
370$func->{'man'}
371";
372    if ($func->{'type'} eq "int") {
373	$mdoc .= ".Sh RETURN VALUES
374The
375.Nm
376function returns one of the following values:
377.Bl -tag -width 18n
378";
379	my @errors = @{$func->{'errors'}};
380	warn("$func->{'name'}(): no error specification\n")
381	    unless(@errors);
382	foreach (@errors) {
383	    $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
384	}
385	$mdoc .= ".El\n";
386    } else {
387	if ($func->{'type'} =~ m/\*$/) {
388	    $mdoc .= ".Sh RETURN VALUES
389The
390.Nm
391function returns
392.Dv NULL
393on failure.
394";
395	}
396    }
397    $mdoc .= ".Sh SEE ALSO\n";
398    my @xref = sort(keys(%{$func->{'xref'}}));
399    while (@xref) {
400	$mdoc .= ".Xr " . shift(@xref) . (@xref ? " ,\n" : "\n");
401    }
402    $mdoc .= ".Sh STANDARDS\n";
403    if ($func->{'openpam'}) {
404	$mdoc .= "The
405.Nm
406function is an OpenPAM extension.
407";
408    } else {
409	$mdoc .= ".Rs
410.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
411.%D \"June 1997\"
412.Re
413";
414    }
415    $mdoc .= ".Sh AUTHORS
416The
417.Nm
418function and this manual page were developed for the FreeBSD Project
419by ThinkSec AS and Network Associates Laboratories, the Security
420Research Division of Network Associates, Inc.  under DARPA/SPAWAR
421contract N66001-01-C-8035
422.Pq Dq CBOSS ,
423as part of the DARPA CHATS research program.
424";
425
426    $fn = "$func->{'name'}.3";
427    if (sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC)) {
428	print(FILE $mdoc);
429	close(FILE);
430    } else {
431	warn("$fn: open(): $!\n");
432    }
433}
434
435sub readproto($) {
436    my $fn = shift;		# File name
437
438    local *FILE;
439    my %func;
440
441    sysopen(FILE, $fn, O_RDONLY)
442	or die("$fn: open(): $!\n");
443    while (<FILE>) {
444	if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
445	    $func{'Nm'} = $func{'Nm'} || $1;
446	} elsif (m/^\.Ft (\S.*?)\s*$/) {
447	    $func{'Ft'} = $func{'Ft'} || $1;
448	} elsif (m/^\.Fn (\S.*?)\s*$/) {
449	    $func{'Fn'} = $func{'Fn'} || $1;
450	}
451    }
452    close(FILE);
453    if ($func{'Nm'}) {
454	$FUNCTIONS{$func{'Nm'}} = \%func;
455    } else {
456	warn("No function found\n");
457    }
458}
459
460sub gensummary($) {
461    my $page = shift;		# Which page to produce
462
463    local *FILE;
464    my $upage;
465    my $func;
466    my %xref;
467
468    sysopen(FILE, "$page.3", O_RDWR|O_CREAT|O_TRUNC)
469	or die("$page.3: $!\n");
470
471    $upage = uc($page);
472    print FILE "$COPYRIGHT
473.Dd $TODAY
474.Dt $upage 3
475.Os
476.Sh NAME
477";
478    my @funcs = sort(keys(%FUNCTIONS));
479    while ($func = shift(@funcs)) {
480	print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
481	print FILE " ,"
482		if (@funcs);
483	print FILE "\n";
484    }
485    print FILE ".Nd Pluggable Authentication Modules Library
486.Sh LIBRARY
487.Lb libpam
488.Sh SYNOPSIS\n";
489    if ($page eq 'pam') {
490	print FILE ".In security/pam_appl.h\n";
491    } else {
492	print FILE ".In security/openpam.h\n";
493    }
494    foreach $func (sort(keys(%FUNCTIONS))) {
495	print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
496	print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
497    }
498    while (<STDIN>) {
499	if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
500	    $xref{$1} = $2;
501        }
502	print FILE $_;
503    }
504
505    if ($page eq 'pam') {
506	print FILE ".Sh RETURN VALUES
507The following return codes are defined by
508.Aq Pa security/pam_constants.h :
509.Bl -tag -width 18n
510";
511	foreach (sort(keys(%PAMERR))) {
512	    print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
513	}
514	print FILE ".El\n";
515    }
516    print FILE ".Sh SEE ALSO
517";
518    print FILE ".Xr openpam 3\n"
519	if ($page eq 'pam');
520    foreach $func (keys(%FUNCTIONS)) {
521        $xref{$func} = 3;
522    }
523    my @refs = sort(keys(%xref));
524    while ($_ = shift(@refs)) {
525	print FILE ".Xr $_ $xref{$_}";
526        print FILE " ,"
527	    if (@refs);
528        print FILE "\n";
529    }
530    print FILE ".Sh STANDARDS
531.Rs
532.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
533.%D \"June 1997\"
534.Re
535.Sh AUTHORS
536The OpenPAM library and this manual page were developed for the
537FreeBSD Project by ThinkSec AS and Network Associates Laboratories,
538the Security Research Division of Network Associates, Inc.  under
539DARPA/SPAWAR contract N66001-01-C-8035
540.Pq Dq CBOSS ,
541as part of the DARPA CHATS research program.
542";
543    close(FILE);
544}
545
546sub usage() {
547
548    print(STDERR "usage: gendoc [-s] source [...]\n");
549    exit(1);
550}
551
552MAIN:{
553    my %opts;
554
555    usage()
556	unless (@ARGV && getopts("op", \%opts));
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