gendoc.pl revision 99158
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#18 $
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 security/pam_appl.h
359";
360    if ($func->{'name'} =~ m/_sm_/) {
361	$mdoc .= ".In security/pam_modules.h\n"
362    }
363    if ($func->{'name'} =~ m/openpam/) {
364	$mdoc .= ".In security/openpam.h\n"
365    }
366    $mdoc .= ".Ft $func->{'type'}
367.Fn $func->{'name'} $func->{'args'}
368.Sh DESCRIPTION
369$func->{'man'}
370";
371    if ($func->{'type'} eq "int") {
372	$mdoc .= ".Sh RETURN VALUES
373The
374.Nm
375function returns one of the following values:
376.Bl -tag -width 18n
377";
378	my @errors = @{$func->{'errors'}};
379	warn("$func->{'name'}(): no error specification\n")
380	    unless(@errors);
381	foreach (@errors) {
382	    $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
383	}
384	$mdoc .= ".El\n";
385    } else {
386	if ($func->{'type'} =~ m/\*$/) {
387	    $mdoc .= ".Sh RETURN VALUES
388The
389.Nm
390function returns
391.Dv NULL
392on failure.
393";
394	}
395    }
396    $mdoc .= ".Sh SEE ALSO\n";
397    my @xref = sort(keys(%{$func->{'xref'}}));
398    while (@xref) {
399	$mdoc .= ".Xr " . shift(@xref) . (@xref ? " ,\n" : "\n");
400    }
401    $mdoc .= ".Sh STANDARDS\n";
402    if ($func->{'openpam'}) {
403	$mdoc .= "The
404.Nm
405function is an OpenPAM extension.
406";
407    } else {
408	$mdoc .= ".Rs
409.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
410.%D \"June 1997\"
411.Re
412";
413    }
414    $mdoc .= ".Sh AUTHORS
415The
416.Nm
417function and this manual page were developed for the FreeBSD Project
418by ThinkSec AS and Network Associates Laboratories, the Security
419Research Division of Network Associates, Inc.  under DARPA/SPAWAR
420contract N66001-01-C-8035
421.Pq Dq CBOSS ,
422as part of the DARPA CHATS research program.
423";
424
425    $fn = "$func->{'name'}.3";
426    if (sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC)) {
427	print(FILE $mdoc);
428	close(FILE);
429    } else {
430	warn("$fn: open(): $!\n");
431    }
432}
433
434sub readproto($) {
435    my $fn = shift;		# File name
436
437    local *FILE;
438    my %func;
439
440    sysopen(FILE, $fn, O_RDONLY)
441	or die("$fn: open(): $!\n");
442    while (<FILE>) {
443	if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
444	    $func{'Nm'} = $func{'Nm'} || $1;
445	} elsif (m/^\.Ft (\S.*?)\s*$/) {
446	    $func{'Ft'} = $func{'Ft'} || $1;
447	} elsif (m/^\.Fn (\S.*?)\s*$/) {
448	    $func{'Fn'} = $func{'Fn'} || $1;
449	}
450    }
451    close(FILE);
452    if ($func{'Nm'}) {
453	$FUNCTIONS{$func{'Nm'}} = \%func;
454    } else {
455	warn("No function found\n");
456    }
457}
458
459sub gensummary($) {
460    my $page = shift;		# Which page to produce
461
462    local *FILE;
463    my $upage;
464    my $func;
465    my %xref;
466
467    sysopen(FILE, "$page.3", O_RDWR|O_CREAT|O_TRUNC)
468	or die("$page.3: $!\n");
469
470    $upage = uc($page);
471    print FILE "$COPYRIGHT
472.Dd $TODAY
473.Dt $upage 3
474.Os
475.Sh NAME
476";
477    my @funcs = sort(keys(%FUNCTIONS));
478    while ($func = shift(@funcs)) {
479	print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
480	print FILE " ,"
481		if (@funcs);
482	print FILE "\n";
483    }
484    print FILE ".Nd Pluggable Authentication Modules Library
485.Sh LIBRARY
486.Lb libpam
487.Sh SYNOPSIS\n";
488    if ($page eq 'pam') {
489	print FILE ".In security/pam_appl.h\n";
490    } else {
491	print FILE ".In security/openpam.h\n";
492    }
493    foreach $func (sort(keys(%FUNCTIONS))) {
494	print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
495	print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
496    }
497    while (<STDIN>) {
498	if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
499	    $xref{$1} = $2;
500        }
501	print FILE $_;
502    }
503
504    if ($page eq 'pam') {
505	print FILE ".Sh RETURN VALUES
506The following return codes are defined by
507.Aq Pa security/pam_constants.h :
508.Bl -tag -width 18n
509";
510	foreach (sort(keys(%PAMERR))) {
511	    print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
512	}
513	print FILE ".El\n";
514    }
515    print FILE ".Sh SEE ALSO
516";
517    print FILE ".Xr openpam 3\n"
518	if ($page eq 'pam');
519    foreach $func (keys(%FUNCTIONS)) {
520        $xref{$func} = 3;
521    }
522    my @refs = sort(keys(%xref));
523    while ($_ = shift(@refs)) {
524	print FILE ".Xr $_ $xref{$_}";
525        print FILE " ,"
526	    if (@refs);
527        print FILE "\n";
528    }
529    print FILE ".Sh STANDARDS
530.Rs
531.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
532.%D \"June 1997\"
533.Re
534.Sh AUTHORS
535The OpenPAM library and this manual page were developed for the
536FreeBSD Project by ThinkSec AS and Network Associates Laboratories,
537the Security 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    $TODAY = strftime("%B %e, %Y", localtime(time()));
557    $TODAY =~ s,\s+, ,g;
558    if ($opts{'o'} || $opts{'p'}) {
559	foreach my $fn (@ARGV) {
560	    readproto($fn);
561	}
562	gensummary('openpam')
563	    if ($opts{'o'});
564	gensummary('pam')
565	    if ($opts{'p'});
566    } else {
567	foreach my $fn (@ARGV) {
568	    my $func = parse_source($fn);
569	    gendoc($func)
570		if (defined($func));
571	}
572    }
573    exit(0);
574}
575