INN config file parsing infrastructure

Russ Allbery rra at
Mon Jun 26 02:42:23 UTC 2000

Back to this, at long last.

Forrest J Cavalier <mibsoft at> writes:

> To make things clearer, can you provide some Perl .def examples showing
> an item with two parameters and also including a subgroup with two
> parameters?  (With comments, default values, etc.)


Here's a really quick take on the basic structure of incoming.conf; note
that I haven't really checked this much and this is just off the top of my
head as a demonstration of the syntax.


##  Definition file for incoming.conf.
##  $Id$

    # The top group is special and is the "top-level" group that the
    # parser is in by default at the beginning of the file.  It can
    # contain global parameter which can't be inside any group as well
    # as general settings that could apply to multiple peers.
    top => {
        struct          => 'incoming',
        contains        => [ qw(global general) ],
        subgroups       => [ qw(group peer) ],
        init            => 'incoming_init',
        finish          => 'incoming_finish'

    # Peers can be collected into groups, which can contain general
    # settings that apply to all peers contained therein.  Note that
    # "group" is the *name* of the group, not some general part of the
    # syntax of the .def file.  Note also that peers don't have to be part
    # of a group, since top supports peer as a direct subgroup.
    group => {
        struct          => 'incoming_group',
        contains        => [ qw(general) ],
        subgroups       => [ qw(peer) ],
        init            => 'incoming_group_init',
        finish          => 'incoming_group_finish'

    # A peer is the most interesting group, since this represents an
    # actual incoming peer.  In addition to taking general parameters, it
    # can also take peer-specific parameters.
    peer => {
        struct          => 'incoming_peer',
        contains        => [ qw(general peer) ],
        init            => 'incoming_peer_init',
        finish          => 'incoming_peer_finish'

    # The peer parameter group (stuff that can only be inside peer groups
    # because it's specific to a single peer).  This is just the hostname
    # parameter.
    peer => [
        name            => 'hostname',
        type            => 'string',
        default         => undef,
        description     => 'Hostname or IP address of peer',
        documentation   => <<'EOD'
This key requires a string value.  It is a list of hostnames separated by
a comma.  A hostname is the host's FQDN, or the dotted quad ip-address of
the peer.  If this key is not present in a peer block, the hostname
defaults to the label of the peer.

    # Part of the general parameter group.  Only the first couple of
    # parameters are listed here.
    general => [
        name            => 'streaming',
        type            => 'boolean',
        default         => 'true',
        description     => 'Accept streaming commands?',
        documentation   => <<'EOD'
This key requires a boolean value.  It defines whether streaming commands
are allowed from this peer.  (default=true)
    }, {
        name            => 'max-connections',
        type            => 'integer',
        default         => 0,
        description     => 'Maximum connections from peer',
        documentation   => <<'EOD'
This key requires positive integer value.  It defines the maximum number
of connections allowed.  A value of zero specifies an unlimited number of
maximum connections.  (default=0)

# Other portions omitted (such as the global parameter group).


If you don't know Perl, there's a bunch of fairly annoying magical syntax
that you have to use (random parens and braces and so forth), but if you
just copy another entry, it shouldn't be too bad.  The advantage of
writing in this style is that a Perl script can then just load the data
file directly and let Perl syntax-check it (although we'll still have to
do some syntax checking like making sure that the type is valid, that the
default is a valid value for the type, that all the referenced parameter
sets and subgroups are defined, and so forth).

The above sets up initialization and cleanup callbacks for every group;
that may not be necessary in practice.  Cleanup callbacks are useful for
doing things like setting dependent defaults; for example, the hostname
parameter of a peer group defaults to the label of the group if not set,
so incoming_peer_finish would check to see if the struct member hostname
were NULL and if so would set it to the same as the peer group tag.  (Note
that in this case, the default is set to undef -- equivalent to NULL.  The
default key can't handle complex things like defaults that involve parts
of other parameters; anything like that has to be handled in the finish

If subgroups share a parameter set with their parent group, my plan is to
have the parser just initialize the subgroup's parameters with the parent
group's values for those parameters before calling the init function.  So,
for example, a peer group would get all of the values in the general
parameter set filled in from its parent group.

The current incoming.conf syntax doesn't require the global parameter set
referred to above, but e.g. innfeed.conf would (for things like paths to
spool directories).

Finally, here's one more example; this is the beginning of the inn.conf
.def file (currently containing only the General Settings parameter set).


##  Definition file for inn.conf.
##  $Id$

    top => {
        struct          => 'innconf',
        contains        => [ qw(default) ],
        init            => 'innconf_init',
        finish          => 'innconf_finish',

    default => [
        name            => 'domain',
        type            => 'string',
        default         => undef,
        description     => 'Default domain of local host',
        documentation   => <<'EOD'
This should be the domain name of the local host.  It should not have a
leading period, and it should not be a full host address.  It is used only
if the GetFQDN() routine in libinn(3) cannot get the fully-qualified
domain name by using either the gethostname(3) or gethostbyname(3) calls.
The check is very simple; if either routine returns a name with a period
in it, then it is assumed to have the full domain name.  The default value
is unset.
    }, {
        name            => 'innflags',
        type            => 'string',
        default         => undef,
        description     => 'Flags to pass to innd on startup',
        documentation   => <<'EOD'
The flags to pass to innd on startup.  See innd(8) for details on the
possible flags.  The default value is unset.
    }, {
        name            => 'mailcmd',
        type            => 'string',
        default         => 'innmail',
        description     => 'Command to send report/control type mail',
        documentation   => <<'EOD'
The path to the program to be used for mailing reports and control
messages.  The default is I<pathbin>/innmail.  This should not normally
need to be changed.
    }, {
        name            => 'mta',
        type            => 'string',
        default         => undef,
        description     => 'MTA for mailing to moderators, innmail',
        documentation   => <<'EOD'
The command to use when mailing postings to moderators and for the use of
innmail(1).  The message, with headers and an added To: header, will be
piped into this program.  The string C<%s>, if present, will be replaced
by the e-mail address of the moderator.  It's strongly recommended for
this command to include C<%s> on the command line rather than use the
addresses in the To: and Cc: headers of the message, since the latter
approach allows the news server to be abused as a mechanism to send mail
to arbitrary addresses and will result in unexpected behavior.  There is
no default value for this parameter; it must be set in F<inn.conf> or a
fatal error message will be logged via syslog.

For most systems, C</usr/lib/sendmail -oi -oem %s> (adjusted for the
correct path to sendmail) is a good choice.
    }, {
        name            => 'pathhost'
        type            => 'string',
        default         => undef,
        description     => 'Entry for the Path line',
        documentation   => <<'EOD'
What to put into the Path: header to represent the local site.  This is
added to the Path: header of all articles that pass through the system,
including locally posted articles, and is also used when processing some
control messages and when naming the server in status reports.  There is
no default value; this parameter must be set in F<inn.conf> or INN will
not start.  A good value to use is the fully-qualified hostname of the
    }, {
        name            => 'server',
        type            => 'string',
        default         => undef,
        description     => 'Default server to connect to',
        documentation   => <<'EOD'
The name of the default NNTP server.  If I<nnrpdposthost> is not set and
UNIX domain sockets are not supported, nnrpd(8) tries to hand off
locally-posted articles through an INET domain socket to this server.
actsync(8), nntpget(8), and getlist(8) also use this value as the default
server to connect to.  In the latter cases, the value of the NNTPSERVER
environment variable, if it exists, overrides this.  The default value is

Russ Allbery (rra at             <>

More information about the inn-workers mailing list