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