summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2009-10-12 20:41:21 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2009-10-12 20:42:04 (GMT)
commit8a4ba77068e5d7f6eab2cc1c1c10f31dcbccf7a6 (patch) (side-by-side diff)
treec70f38d943c778684bb9dbfc7db018e7efb21bf1
parentaf04ed630f18e9003756317cc627a11084d0d59a (diff)
downloadpies-8a4ba77068e5d7f6eab2cc1c1c10f31dcbccf7a6.tar.gz
pies-8a4ba77068e5d7f6eab2cc1c1c10f31dcbccf7a6.tar.bz2
Fix make distcheck and check-docs.
* doc/Makefile.am: Fix `check-*' goals. * doc/pies.texi: Update and rearrange material. Document new configuration. * lib/Makefile.am (libpies_a_SOURCES): Remove nls.c * src/Makefile.am (EXTRA_DIST): Remove pies.rc, add pp-setup. (INCLUDES): Add $(top_builddir)/gnu * src/pies.c: Minor changes. * src/progman.c: Minor changes. * README-hacking: New file.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--README-hacking75
-rw-r--r--doc/Makefile.am53
-rw-r--r--doc/pies.texi715
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/nls.c33
-rw-r--r--lib/strtotok.c77
-rw-r--r--src/Makefile.am8
-rw-r--r--src/acl.c627
-rw-r--r--src/acl.h43
-rw-r--r--src/addrfmt.c133
-rw-r--r--src/diag.c131
-rw-r--r--src/meta.c127
-rw-r--r--src/pies.c68
-rw-r--r--src/pp-setup116
-rw-r--r--src/progman.c4
-rw-r--r--src/url.c212
-rw-r--r--src/userprivs.c279
17 files changed, 2481 insertions, 223 deletions
diff --git a/README-hacking b/README-hacking
new file mode 100644
index 0000000..9441c46
--- a/dev/null
+++ b/README-hacking
@@ -0,0 +1,75 @@
+These notes intend to help people working on the GIT version of Pies.
+See end of file for copying conditions.
+
+* Requirements
+
+If you have taken the sources from GIT you will need the following
+packages to build Pies. I don't make any extra effort to accommodate
+older versions of these packages, so please make sure that you have the
+latest stable version.
+
+- Automake <http://www.gnu.org/software/automake/>
+- Autoconf <http://www.gnu.org/software/autoconf/>
+- Bison <http://www.gnu.org/software/bison/>
+- Flex <http://flex.sourceforge.net/>
+- Gettext <http://www.gnu.org/software/gettext/>
+- Gnulib <http://www.gnu.org/software/gnulib/>
+- M4 <http://www.gnu.org/software/m4/>
+- Rsync <http://samba.anu.edu.au/rsync/>
+- Texinfo <http://www.gnu.org/software/texinfo>
+
+* Bootstrapping
+
+Obviously, if you are reading these notes, you did manage to check out
+the project from GIT. The next step is to get other files needed to build,
+which are extracted from other source packages:
+
+1. Change to the source tree directory
+
+ cd pies
+
+2. Run
+
+ ./bootstrap
+
+Once done, proceed as described in the file README (section
+INSTALLATION).
+
+Normally you will have to run bootstrap only once. However, if you
+intend to hack on Pies, you might need to run it again later. In
+this case, you will probably want to save some time and bandwidth by
+avoiding downloading the same files again. If so, create in the
+project's root directory the file named `.bootstrap' with the following
+contents:
+
+ --gnulib-srcdir=$HOME/gnulib
+
+Replace `$HOME/gnulib' with the actual directory where the Gnulib
+sources reside.
+
+If you wish to avoid synchronising translations, add this option:
+--skip-po.
+
+For more information about `bootstrap', run `bootstrap --help'.
+
+
+* Copyright information
+
+Copyright (C) 2008, 2009 Sergey Poznyakoff
+
+ Permission is granted to anyone to make or distribute verbatim copies
+ of this document as received, in any medium, provided that the
+ copyright notice and this permission notice are preserved,
+ thus giving the recipient permission to redistribute in turn.
+
+ Permission is granted to distribute modified versions
+ of this document, or of portions of it,
+ under the above conditions, provided also that they
+ carry prominent notices stating who last changed them.
+
+
+Local Variables:
+mode: outline
+paragraph-separate: "[ ]*$"
+version-control: never
+End:
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 502c46e..d7d69d1 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -37,65 +37,33 @@ check-format:
false; \
fi
-check-pragmas:
- @check-docs.sh pragmas \
- '/} option_cache\[\] = {/,/^}/s/[ \t]*{ *"\(.*\)".*/\1/pg' \
- 's/@deffnx* {pragma option} *\([^@, ]*\) .*/\1/p' \
- $(top_srcdir)/mfd/main.c -- \
- $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
- $(info_TEXINFOS)
-
check-options:
@check-docs.sh options \
'/argp_option options\[\] = /,/^}/s/[ \t]*{ *"\([^,"]*\)".*/\1/pg' \
's/@opindex *\([^@,]*\).*/\1/p' \
- $(top_srcdir)/mfd/main.c -- \
+ $(top_srcdir)/src/pies.c -- \
$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
$(info_TEXINFOS)
check-config:
@check-docs.sh 'configuration statements' \
- '/mf_cfg_param\[\] *= *{/,/^}/s/[ \t]*{ *"\([^,"]*\)".*/\1/pg' \
- 's/@deffn {Pies Conf} *\([^@,]*\).*/\1/p' \
- $(top_srcdir)/mfd/main.c -- \
+ '/pies_keywords\[\] *= *{/,/^}/s/[ \t]*{ *"\([^,"]*\)".*/\1/pg' \
+ 's/@deffn {Config} *\([^@,]*\).*/\1/p' \
+ $(top_srcdir)/src/pies.c -- \
$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
$(info_TEXINFOS)
check-sub-config:
- @list=`sed -n '/mf_cfg_param\[\] *= *{/,/^}/{s/[ \t]*{ *"\([^,"]*\)", *mu_cfg_section *,.*/\1/pg}' $(top_srcdir)/mfd/main.c`; \
- for ident in $$list; do \
+ sed -n '/pies_keywords\[\] *= *{/,/^}/{p}' ../src/pies.c|tr '\n{' ' \n'|sed -n '/grecs_type_section/s/"\([^"]*\)".*grecs_type_section,[^,]*,[^,]*,[^,]*,[^,]*, *\(.*\) *}.*/\1 \2/p' | \
+ while read ident kw; do \
check-docs.sh "$$ident configuration statements" \
- "/$${ident}_section_param"'\[\] *= *{/,/^}/s/[ \t]*{ *"\([^,"]*\)".*/\1/pg' \
- "s/@deffn {$${ident}}"' *\([^@,]*\).*/\1/p' \
- $(top_srcdir)/mfd/main.c -- \
+ "/$$kw"'\[\] *= *{/,/^}/s/[ \t]*{ *"\([^,"]*\)".*/\1/pg' \
+ "s/@deffn {Config: *$${ident}}"' *\([^@,]*\).*/\1/p' \
+ $(top_srcdir)/src/pies.c $(top_srcdir)/src/acl.c -- \
$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
$(info_TEXINFOS); \
done
-check-builtins:
- @check-docs.sh builtins \
- '/MF_DEFUN/{s/[ \t]*MF_DEFUN *(\([a-zA-Z_][a-zA-Z0-9_]*\),.*/\1/p;s/[ \t]*MF_DEFUN_VARARGS\(_NO_PROM\)\? *(\([a-zA-Z_][a-zA-Z0-9_]*\),.*/\2/p;s/[ \t]*MF_DEFUN_CTYPE *(\([a-zA-Z_][a-zA-Z0-9_]*\))/\1/p}'\
- 's/@deftypefnx\{0,1\} {Built-in Function} *[^ ][^ ]* *\([^ ]*\).*/\1/p' \
- $(top_srcdir)/mfd/bi_*.m4 -- \
- $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
- $(info_TEXINFOS)
-
-check-mflib:
- @check-docs.sh "library functions" \
- '/^[ \t]*func[ \t][ \t]*__/b;/^[ \t]*func/s/[ \t]*func[ \t][ \t]*\(.[^ \t(]*\).*/\1/p' \
- 's/@deftypefn {Library Function} *[^ ][^ ]* *\([^ ]*\).*/\1/p' \
- $(top_srcdir)/mflib/*.mf -- \
- $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
- $(info_TEXINFOS)
-
-check-exceptions:
- @check-docs.sh exceptions \
- '/typedef enum mf_exception_code {/,/^};/s/[ \t]*mfe_\(.*\),.*/e_\1/p;/typedef enum mf_status_code {/,/^};/s/[ \t]*mf_\(.*\),.*/\1/p' \
- 's/@cindex \([^,][^,]*\), exception type/\1/p' \
- $(top_srcdir)/mfd/pies.h -- \
- $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
- $(info_TEXINFOS)
-
check-refs:
@for file in $(info_TEXINFOS) $(pies_TEXINFOS); \
do \
@@ -147,9 +115,8 @@ check-unrevised:
rm $@-t; \
fi
-all-check-docs: check-format check-options check-pragmas \
+all-check-docs: check-format check-options \
check-config check-sub-config \
- check-builtins check-mflib check-exceptions \
check-refs check-fixmes check-writeme check-unrevised
check-docs:
diff --git a/doc/pies.texi b/doc/pies.texi
index cc35b5d..22c0f83 100644
--- a/doc/pies.texi
+++ b/doc/pies.texi
@@ -78,27 +78,25 @@ documents @command{pies} Version @value{VERSION}.
@menu
* Intro::
* Pies Configuration File::
-* Component Statement::
-* include-meta1::
-* Global Configuration::
* Pies Debugging::
* Configuration Example::
* Command Line Usage::
* Pies Invocation::
+* Reporting Bugs::
Appendices
* Copying This Manual:: The GNU Free Documentation License.
* Concept Index:: Index of Concepts.
-@c @detailmenu
-@c @end detailmenu
+@detailmenu
+@end detailmenu
@end menu
@node Intro
@chapter Introduction
-@cindex component, pies
+@cindex component
The name @command{pies} (pronounced @samp{p-yes}) stands for
@samp{Program Invocation and Execution Supervisor}. This utility
starts and controls execution of external programs, called
@@ -110,9 +108,9 @@ terminates, @command{pies} restarts it. Its configuration allows to
specify actions other than simple restart, depending on the exit code
of the component.
-@cindex prerequisite, pies
-@cindex dependency, pies
-@cindex dependents, pies
+@cindex prerequisite
+@cindex dependency
+@cindex dependents
@anchor{component prerequisite}
A component @samp{A} may depend on another components, say
@samp{B} and @samp{C}, i.e. require them to be running at the moment of its
@@ -131,7 +129,7 @@ redirected to a file or to an arbitrary @command{syslog} channel.
@anchor{init-style}
@cindex init-style components
- These way of operation applies to so-called @dfn{init-style}
+ This way of operation applies to the @dfn{init-style}
components, called so because of the similarity with the
@command{init} process manager. @command{Pies} is also able to handle
components that receive input on their @samp{stdin} and send reply to
@@ -180,40 +178,304 @@ stopping component dependencies, the same ordering is preserved.
This order is reversed for files included by @code{include-meta1}
statement (@pxref{include-meta1}).
+@node Pies Configuration File
+@chapter Pies Configuration File
+@cindex configuration file
+@flindex pies.conf
+@xopindex{config-file, introduced}
+ @command{Pies} reads its settings and component definitions from the
+@dfn{configuration file} @file{pies.conf}, located in the @dfn{system
+configuration directory} (in most cases @file{/etc} or
+@file{/usr/local/etc}, depending on how the package was compiled).
+An alternative location may be specified using @option{--config-file}
+(@option{-c} command line option.
+
+ If any errors are encountered in the configuration file, the program
+reports them on the standard error and exits with a non-zero status.
+
+@xopindex{lint, introduced}
+ To test the configuration file without actually starting the server, the
+@option{--lint} (@option{-t}) command line option is provided. It causes
+@command{pies} to check its configuration file and exit with status 0
+if no errors were detected, and with status 1 otherwise.
+
+@opindex -E, introduced
+ Before parsing, configuration file is preprocessed using
+@command{m4} (@pxref{Preprocessor}). To see the preprocessed
+configuration without actually parsing it, use @option{-E} command
+line option.
+
+@xopindex{config-help, introduced}
+ The rest of this section describes the configuration file syntax in
+detail. You can receive a concise summary of all configuration
+directives any time by running @command{pies --config-help}.
+
@menu
+* Syntax:: Configuration file syntax.
+* Component Statement::
+* ACL:: Access Control Lists
+* include-meta1::
+* Global Configuration::
@end menu
-@node Pies Configuration File
-@chapter Pies Configuration File
- @command{Pies} reads its configuration from the main Mailutils
-configuration file. @xref{configuration, Mailutils Configuration
-File,, mailutils, GNU Mailutils Manual}, for a description of GNU
-Mailutils configuration system. It is recommended to use
-@code{include @var{directory}} statement (@pxref{Include, Include
-Statement,, mailutils, GNU Mailutils Manual}), and to place
-@command{pies} configuration in file @file{@var{directory}/pies}.
-@xref{MeTA1,,, mailfromd, Mailfromd Manual}, for an example.
-
-The following standard Mailutils configuration statements are understood:
-
-@multitable @columnfractions 0.3 0.6
-@headitem Statement @tab Reference
-@item debug @tab @xref{Debug Statement, Mailutils Debug Statement,,
-mailutils, GNU Mailutils Manual}.
-@item logging @tab @xref{Logging Statement, Mailutils Logging,,
-mailutils, GNU Mailutils Manual}.
-@item include @tab @xref{Include, Include Statements,,
-mailutils, GNU Mailutils Manual}.
-@item mailer @tab @xref{Mailer, Mailer Statement,,
-mailutils, GNU Mailutils Manual}.
-@item acl @tab @xref{ACL Statement, ACL Statement,,
-mailutils, GNU Mailutils Manual}.
+@node Syntax
+@section Configuration File Syntax
+ The configuration file consists of statements and comments.
+
+ There are three classes of lexical tokens: keywords, values, and
+separators. Blanks, tabs, newlines and comments, collectively called
+@dfn{white space} are ignored except as they serve to separate
+tokens. Some white space is required to separate otherwise adjacent
+keywords and values.
+
+@menu
+* Comments::
+* Statements::
+* Preprocessor:: Using preprocessor to improve the configuration.
+@end menu
+
+@node Comments
+@subsection Comments
+@cindex Comments in a configuration file
+@cindex single-line comments
+ @dfn{Comments} may appear anywhere where white space may appear in the
+configuration file. There are two kinds of comments:
+single-line and multi-line comments. @dfn{Single-line} comments start
+with @samp{#} or @samp{//} and continue to the end of the line:
+
+@smallexample
+# This is a comment
+// This too is a comment
+@end smallexample
+
+@cindex multi-line comments
+ @dfn{Multi-line} or @dfn{C-style} comments start with the two
+characters @samp{/*} (slash, star) and continue until the first
+occurrence of @samp{*/} (star, slash).
+
+ Multi-line comments cannot be nested.
+
+@node Statements
+@subsection Statements
+@cindex statements, configuration file
+@cindex configuration file statements
+@cindex statement, simple
+@cindex simple statements
+ A @dfn{simple statement} consists of a keyword and value
+separated by any amount of whitespace. Simple statement is terminated
+with a semicolon (@samp{;}), unless it contains a @dfn{here-document}
+(see below), in which case semicolon is optional.
+
+ Examples of simple statements:
+
+@smallexample
+pidfile /var/run/pies.pid;
+source-info yes;
+debug 10;
+@end smallexample
+
+ A @dfn{keyword} begins with a letter and may contain letters,
+decimal digits, underscores (@samp{_}) and dashes (@samp{-}).
+Examples of keywords are: @samp{group}, @samp{control-file}.
+
+ A @dfn{value} can be one of the following:
+
+@table @asis
+@item number
+ A number is a sequence of decimal digits.
+
+@item boolean
+@cindex boolean value
+ A boolean value is one of the following: @samp{yes}, @samp{true},
+@samp{t} or @samp{1}, meaning @dfn{true}, and @samp{no},
+@samp{false}, @samp{nil}, @samp{0} meaning @dfn{false}.
+
+@item unquoted string
+@cindex string, unquoted
+ An unquoted string may contain letters, digits, and any of the
+following characters: @samp{_}, @samp{-}, @samp{.}, @samp{/},
+@samp{:}.
+
+@item quoted string
+@cindex quoted string
+@cindex string, quoted
+@cindex escape sequence
+ A quoted string is any sequence of characters enclosed in
+double-quotes (@samp{"}). A backslash appearing within a quoted
+string introduces an @dfn{escape sequence}, which is replaced
+with a single character according to the following rules:
+
+@float Table, backslash-interpretation
+@caption{Backslash escapes}
+@multitable @columnfractions 0.30 .5
+@item Sequence @tab Replaced with
+@item \a @tab Audible bell character (@acronym{ASCII} 7)
+@item \b @tab Backspace character (@acronym{ASCII} 8)
+@item \f @tab Form-feed character (@acronym{ASCII} 12)
+@item \n @tab Newline character (@acronym{ASCII} 10)
+@item \r @tab Carriage return character (@acronym{ASCII} 13)
+@item \t @tab Horizontal tabulation character (@acronym{ASCII} 9)
+@item \\ @tab A single backslash (@samp{\})
+@item \" @tab A double-quote.
@end multitable
+@end float
+
+ In addition, the sequence @samp{\@var{newline}} is removed from
+the string. This allows to split long strings over several
+physical lines, e.g.:
+
+@smallexample
+@group
+"a long string may be\
+ split over several lines"
+@end group
+@end smallexample
+
+ If the character following a backslash is not one of those specified
+above, the backslash is ignored and a warning is issued.
+
+ Two or more adjacent quoted strings are concatenated, which gives
+another way to split long strings over several lines to improve
+readability. The following fragment produces the same result as the
+example above:
+
+@smallexample
+@group
+"a long string may be"
+" split over several lines"
+@end group
+@end smallexample
+
+@anchor{here-document}
+@item Here-document
+@cindex here-document
+ @dfn{Here-document} is a special construct that allows to introduce
+strings of text containing embedded newlines.
+
+ The @code{<<@var{word}} construct instructs the parser to read all
+the following lines up to the line containing only @var{word}, with
+possible trailing blanks. Any lines thus read are concatenated
+together into a single string. For example:
+
+@smallexample
+@group
+<<EOT
+A multiline
+string
+EOT
+@end group
+@end smallexample
+
+ Body of a here-document is interpreted the same way as
+double-quoted string, unless @var{word} is preceded by a backslash
+(e.g. @samp{<<\EOT}) or enclosed in double-quotes, in which case
+the text is read as is, without interpretation of escape sequences.
+
+ If @var{word} is prefixed with @code{-} (a dash), then all leading
+tab characters are stripped from input lines and the line containing
+@var{word}. Furthermore, if @code{-} is followed by a single space,
+all leading whitespace is stripped from them. This allows to indent
+here-documents in a natural fashion. For example:
+
+@smallexample
+@group
+<<- TEXT
+ All leading whitespace will be
+ ignored when reading these lines.
+TEXT
+@end group
+@end smallexample
+
+ It is important that the terminating delimiter be the only token on
+its line. The only exception to this rule is allowed if a
+here-document appears as the last element of a statement. In this
+case a semicolon can be placed on the same line with its terminating
+delimiter, as in:
+
+@smallexample
+help-text <<-EOT
+ A sample help text.
+EOT;
+@end smallexample
+
+@item list
+@cindex list
+ A @dfn{list} is a comma-separated list of values. Lists are
+delimited by parentheses. The following example shows a statement
+whose value is a list of strings:
+
+@smallexample
+dependents (pmult, auth);
+@end smallexample
+
+ In any case where a list is appropriate, a single value is allowed
+without being a member of a list: it is equivalent to a list with a
+single member. This means that, e.g. @samp{dependents auth;} is
+equivalent to @samp{dependents (mime);}.
+
+@end table
+
+@cindex statement, block
+@cindex block statement
+ A @dfn{block statement} introduces a logical group of another
+statements. It consists of a keyword, followed by an optional value,
+and a sequence of statements enclosed in curly braces, as shown in
+the example below:
+
+@smallexample
+@group
+component multiplexor @{
+ command "pmult";
+@}
+@end group
+@end smallexample
+
+ The closing curly brace may be followed by a semicolon, although
+this is not required.
+
+@node Preprocessor
+@subsection Using Preprocessor to Improve the Configuration.
+@cindex preprocessor
+@cindex m4
+ Before parsing configuration file, @command{pies} preprocesses
+it. The built-in preprocessor handles only file inclusion
+and @code{#line} statements (@FIXME-pxref{Pragmatic Comments}), while the
+rest of traditional preprocessing facilities, such as macro expansion,
+is supported via @command{m4}, which is used as an external preprocessor.
+
+ The detailed description of @command{m4} facilities lies far beyond
+the scope of this document. You will find a complete user manual in
+@ifnothtml
+@ref{Top, GNU M4 manual, GNU M4, m4, GNU M4 macro processor}.
+@end ifnothtml
+@ifhtml
+@uref{http://www.gnu.org/software/m4/manual}.
+@end ifhtml
+For the rest of this subsection we assume the reader is sufficiently
+acquainted with @command{m4} macro processor.
+
+@flindex pp-setup
+ The external preprocessor is invoked with @option{-s} flag, instructing
+it to include line synchronization information in its output. This
+information is then used by the parser to display meaningful
+diagnostic. An initial set of macro definitions is supplied by the
+@file{pp-setup} file, located in
+@file{@var{$prefix}/share/pies/@var{version}/include} directory (where
+@var{version} means the version of the package).
+
+The default @file{pp-setup} file renames all @command{m4} built-in
+macro names so they all start with the prefix @samp{m4_}. This
+is similar to GNU m4 @option{--prefix-builtin} options, but has an
+advantage that it works with non-GNU @command{m4} implementations as
+well.
@node Component Statement
-@chapter Component Statement
+@section Component Statement
@kwindex component
+
+@deffn {Config} component
The @code{component} statement defines a new component:
+@end deffn
@smallexample
component @var{tag} @{
@@ -226,7 +488,7 @@ argument to the @code{component} keyword.
The following statements are allowed within the @code{component} block:
-@deffn {Pies Conf} mode @var{mode}
+@deffn {Config: component} mode @var{mode}
Declare the type (style) of the component. Accepted values for
@var{mode} are:
@@ -249,7 +511,7 @@ the default.
@end table
@end deffn
-@deffn {Pies Conf} program @var{name}
+@deffn {Config: component} program @var{name}
Full file name of the component binary. This binary will be executed
(via @command{/bin/sh -c}) each time @command{pies} decides it needs
to start the component.
@@ -257,7 +519,7 @@ to start the component.
To supply command line arguments, use @code{command} statement.
@end deffn
-@deffn {Pies Conf} command @var{string}
+@deffn {Config: component} command @var{string}
Command line for the program. The argument should be just as
arguments normally are, starting with the name of the program. The
latter may be different from the one specified to @code{program}
@@ -265,13 +527,13 @@ statement. Its value will be available to the program as
@code{argv[0]}.
@end deffn
-@deffn {Pies Conf} disable @var{bool}
+@deffn {Config: component} disable @var{bool}
If @var{bool} is @samp{true}, this component is disabled,
i.e. @command{pies} will ignore it.
@end deffn
-@deffn {Pies Conf} precious @var{bool}
-@cindex precious components, pies
+@deffn {Config: component} precious @var{bool}
+@cindex precious components
If @var{bool} is @samp{true}, this component is marked as precious.
Precious components are never disabled by @command{pies}, even if they
respawn too fast.
@@ -291,13 +553,13 @@ respawn too fast.
@end menu
@node Prerequisites
-@section Component Prerequisites
+@subsection Component Prerequisites
@cindex declaring prerequisites
@cindex prerequisites, declaring
Prerequisites (@pxref{component prerequisite}) for a component are
declared using the following statement:
-@deffn {Pies Conf} prerequisites @var{tag-list}
+@deffn {Config: component} prerequisites @var{tag-list}
The argument is either a list of component tags, @emph{defined before
this component}, or one of the following words:
@@ -312,35 +574,35 @@ No prerequisites. This is the default.
If you wish, you can define dependents, instead of prerequisites:
-@deffn {Pies Conf} dependents @var{tag-list}
+@deffn {Config: component} dependents @var{tag-list}
Declare dependents for this component. @var{var-list} is a list of
component tags.
@end deffn
@node Component Privileges
-@section Component Privileges
-@cindex privileges, pies
+@subsection Component Privileges
+@cindex privileges
Following statements control the privileges the component
is executed with.
-@deffn {Pies Conf} user @var{user-name}
+@deffn {Config: component} user @var{user-name}
Start component with the UID and GID of this user.
@end deffn
-@deffn {Pies Conf} group @var{group-list}
+@deffn {Config: component} group @var{group-list}
Retain supplementary groups, specified in @var{group-list}.
@end deffn
-@deffn {Pies Conf} allgroups @var{bool}
+@deffn {Config: component} allgroups @var{bool}
Retain all supplementary groups of which the user (as given with
@command{user} statement) is a member. This is the default for
components specified in @file{meta1.conf} file (@pxref{include-meta1}).
@end deffn
@node Resources
-@section Resources
+@subsection Resources
-@deffn {Pies Conf} limits @var{string}
+@deffn {Config: component} limits @var{string}
Impose limits on system resources, as defined by @var{string}. The
argument consists of @dfn{commands}, optionally separated by any
amount of whitespace. A command is a single command letter followed
@@ -374,12 +636,12 @@ reserved for future use (@samp{number of logins} limit) and is ignored
in version @value{VERSION}.
@end deffn
-@deffn {Pies Conf} umask @var{number}
+@deffn {Config: component} umask @var{number}
Set the umask. The @var{number} must be an octal value not greater
than @samp{777}. The default umask is inherited at startup.
@end deffn
-@deffn {Pies Conf} env @var{args}
+@deffn {Config: component} env @var{args}
Set program environment.
Arguments are a whitespace-delimited list of specifiers. The
@@ -428,16 +690,16 @@ before assignment.
@end deffn
@node Actions Before Startup
-@section Actions Before Startup
+@subsection Actions Before Startup
Several statements are available that specify actions to perform
immediately before starting the component:
-@deffn {Pies Conf} chdir @var{dir}
+@deffn {Config: component} chdir @var{dir}
Change to the directory @var{dir}.
@end deffn
-@deffn {Pies Conf} remove-file @var{file-name}
+@deffn {Config: component} remove-file @var{file-name}
Remove @var{file-name}. This is useful, for example, to remove stale
@acronym{UNIX} sockets or pid-files, which may otherwise prevent the
component from starting normally.
@@ -445,7 +707,7 @@ component from starting normally.
As of version @value{VERSION} only one @command{remove-file} may be given.
@end deffn
-@deffn {Pies Conf} settle-timeout @var{number}
+@deffn {Config: component} settle-timeout @var{number}
Wait @var{number} seconds. This is kind of kludge. Currently it is
used for components imported from @file{meta1.conf} file
(@pxref{include-meta1}), where @code{settle-timeout 1} is implied.
@@ -453,7 +715,7 @@ This may change in future versions.
@end deffn
@node Exit Actions
-@section Exit Actions
+@subsection Exit Actions
@kwindex return-code
The default behavior of @command{pies} if an @samp{init-style}
component terminates is to restart it. Unless the component
@@ -461,11 +723,13 @@ terminates with 0 exit code, a corresponding error message is issued
to the log file. This behavior can be modified using
@code{return-code} statement:
+@deffn {Config} return-code
@smallexample
return-code @var{codes} @{
@dots{}
@}
@end smallexample
+@end deffn
The @var{codes} argument is a list of exit codes or signal names.
Exit codes can be specified either as decimal numbers or as symbolic code
@@ -508,7 +772,7 @@ or is terminated on a signal listed in @var{codes},
@command{pies} executes actions specified by its substatements.
They are executed in the order of their appearance below:
-@deffn {Pies Conf} exec @var{command}
+@deffn {Config: return-code} exec @var{command}
Execute external command. Prior to execution of @var{command} all
file descriptors are closed. It inherits the environment from the
main @command{pies} process with the following additional variables:
@@ -531,7 +795,7 @@ Program exit code.
@end table
@end deffn
-@deffn {Pies Conf} action @samp{disable | restart}
+@deffn {Config: return-code} action @samp{disable | restart}
If @samp{restart} is given, restart the component. This is the
default. Otherwise, mark the component as disabled. Component
dependents are stopped and marked as disabled as well. Once disabled,
@@ -539,7 +803,7 @@ the components are never restarted, unless their restart is requested
by the administrator.
@end deffn
-@deffn {Pies Conf} notify @var{email-string}
+@deffn {Config: return-code} notify @var{email-string}
Send an email notification to addresses in @var{email-string}. The
latter is a comma-separated list of email addresses, e.g.:
@@ -547,13 +811,11 @@ latter is a comma-separated list of email addresses, e.g.:
notify "root@@localhost,postmaster@@localhost";
@end smallexample
-The @code{mailer} statement (@pxref{Mailer, Mailer Statement,,
-mailutils, GNU Mailutils Manual}) configures the mailer used to send
-the message. The message itself is configured by the @code{message}
+The message itself is configured by the @code{message}
statement.
@end deffn
-@deffn {Pies Conf} message @var{string}
+@deffn {Config: return-code} message @var{string}
Supply notification message text to use by @code{notify} statement.
@var{String} must be a valid RFC 822 message text, i.e. it must begin
with message headers, followed by an empty line and actual message
@@ -567,7 +829,6 @@ The following macro-variables are expanded within @var{string}:
@item program-name @tab Program name of the @command{pies} binary.
@item package @tab Package name (@samp{Pies}).
@item version @tab Package version (@value{VERSION}).
-@item mu-version @tab Version of GNU Mailutils.
@item component @tab Name of the terminated component.
@item retcode @tab Component exit code, in decimal.
@end multitable
@@ -592,13 +853,13 @@ all components. Any @code{return-code} statements appearing within a
@code{component} block override the global ones.
@node Output Redirectors
-@section Output Redirectors
+@subsection Output Redirectors
@cindex repeater
Output redirectors allow to redirect the standard error and/or standard
output of a component to a file or @command{syslog} facility.
-@deffn {Pies Conf} stderr @var{type} @var{channel}
-@deffnx {Pies Conf} stdout @var{type} @var{channel}
+@deffn {Config: component} stderr @var{type} @var{channel}
+@deffnx {Config} stdout @var{type} @var{channel}
Redirect standard error (if @code{stderr}) or standard output (if
@code{stdout}) to the given channel.
@@ -618,9 +879,8 @@ Redirect to the syslog channel. The syslog priority is given by the
@var{channel} argument. Its allowed values are: @samp{emerg},
@samp{alert}, @samp{crit}, @samp{err}, @samp{warning}, @samp{notice},
@samp{info}, @samp{debug}. The facility is inherited from the
-@code{logging} statement (@pxref{Logging Statement, Mailutils Logging,,
-mailutils, GNU Mailutils Manual}), or from @code{facility} statement
-(see below), if given.
+@code{syslog} statement (@pxref{syslog}), or from @code{facility}
+statement (see below), if given.
Example:
@@ -630,7 +890,7 @@ stderr syslog err;
@end table
@end deffn
-@deffn {Pies Conf} facility @var{syslog-facility}
+@deffn {Config: component} facility @var{syslog-facility}
Specify the syslog facility to use in syslog redirectors. Allowed
values for @var{syslog-facility} are: @samp{user}, @samp{daemon},
@samp{auth}, @samp{authpriv}, @samp{mail}, @samp{cron}, @samp{local0}
@@ -638,14 +898,14 @@ through @samp{local7} (all names case-insensitive), or a facility number.
@end deffn
@node Inetd-Style Components
-@section Inetd-Style Components
+@subsection Inetd-Style Components
@cindex inetd-style components
Inetd-style components are declared using @code{mode inetd}
statement. You must also declare a socket to listen for requests for
such components:
@anchor{inetd-socket}
-@deffn {Pies Conf} socket @var{url}
+@deffn {Config: component} socket @var{url}
Define a socket to listen on. Allowed values for @var{url} are:
@table @asis
@@ -696,7 +956,7 @@ be added if there is a demand.
@end deffn
@node Meta1-Style Components
-@section Meta1-Style Components
+@subsection Meta1-Style Components
@cindex meta1-style components
Meta1-style components are declared using @code{mode pass}
statement. For such components, you must declare both a socket to
@@ -704,7 +964,7 @@ listen on (@pxref{inetd-socket} and a @acronym{UNIX} socket name to
pass the file descriptor to the component. The latter is defined
using @code{pass-fd-socket} statement:
-@deffn {Pies Conf} pass-fd-socket @var{file-name}
+@deffn {Config: component} pass-fd-socket @var{file-name}
The argument is an absolute or relative file name of the socket file.
In the latter case, the @code{chdir @var{dir}} statement must be used
for this component (@pxref{Actions Before Startup, chdir}), and the
@@ -715,7 +975,7 @@ upon its startup.
@end deffn
@node Component Syntax Summary
-@section Component Syntax Summary
+@subsection Component Syntax Summary
This subsection summarizes the @code{component} summary. For each
statement, a reference to its detailed description is supplied.
@@ -814,13 +1074,147 @@ component @var{tag} @{
@}
@end smallexample
+@node ACL
+@section Access Control Lists
+@cindex @acronym{ACL}
+@cindex access control lists
+@dfn{Access control lists}, or @acronym{ACL}s for short, are lists of
+permissions that control access to @samp{inetd}, @samp{access} and
+@samp{meta1}-style components.
+
+An @acronym{ACL} is defined using @code{acl} block statement:
+
+@deffn {Config} acl
+@smallexample
+acl @var{name} @{
+ @var{definitions}
+@}
+@end smallexample
+@end deffn
+
+The @var{name} parameter specifies a unique name for that
+@acronym{ACL}. This name can be used by another configuration
+statements to refer to that @acronym{ACL}.
+
+A part between the curly braces (denoted by @var{definitions} above),
+is a list of @dfn{access statements}. There are two types of
+such statements:
+
+@deffn {Config: acl} allow @var{user-group} @var{sub-acl} @var{host-list}
+Allow access to resource.
+@end deffn
+
+@deffn {Config: acl} deny @var{user-group} @var{sub-acl} @var{host-list}
+Deny access to resource.
+@end deffn
+
+All parts of an access statement are optional, but at least one
+of them must be present.
+
+The @var{user-group} part specifies which users match this entry.
+Allowed values are the following:
+
+@table @code
+@kwindex all
+@item all
+All users.
+
+@kwindex authenticated
+@item authenticated
+Only authenticated users.
+
+@kwindex group
+@item group @var{group-list}
+Authenticated users which are members of at least one of groups listed in
+@var{group-list}.
+@end table
+
+The @var{sub-acl} part, if present, allows to branch to another
+@acronym{ACL}. The syntax of this group is:
+
+@smallexample
+acl @var{name}
+@end smallexample
+
+@noindent
+where @var{name} is the name of a previously defined @acronym{ACL}.
+
+Finally, the @var{host-list} group allows to match client addresses.
+It consists of a @code{from} keyword followed by a list of
+@dfn{address specifiers}. Allowed address specifiers are:
+
+@table @asis
+@item @var{addr}
+Matches if the client @acronym{IP} equals @var{addr}.
+The latter may be given either as an @acronym{IP}
+address or as a host name, in which case it will be resolved and the
+first of its @acronym{IP} addresses will be used.
+
+
+@item @var{addr}/@var{netlen}
+Matches if first @var{netlen} bits from the client @acronym{IP}
+address equal to @var{addr}. The network mask length, @var{netlen}
+must be an integer number in the range from 0 to 32. The address part,
+@var{addr}, is as described above.
+
+@item @var{addr}/@var{netmask}
+The specifier matches if the result of logical @acronym{AND} between
+the client @acronym{IP} address and @var{netmask} equals to
+@var{addr}. The network mask must be specified in ``dotted quad''
+form, e.g. @samp{255.255.255.224}.
+
+@item @var{filename}
+Matches if connection was received from a @acronym{UNIX} socket
+@var{filename}, which must be given as an absolute file name.
+@end table
+
+To summarize, the syntax of an access statement is:
+
+@smallexample
+allow|deny [all|authenticated|group @var{group-list}]
+ [acl @var{name}] [from @var{addr-list}]
+@end smallexample
+
+@noindent
+where square brackets denote optional parts and vertical bar means
+@samp{one of}.
+
+When an @acronym{ACL} is applied to a particular object, its entries
+are tried in turn until one of them matches, or the end of the list is
+reached. If a matched entry is found, its command verb, @code{allow}
+or @code{deny}, defines the result of @acronym{ACL} match. If the end
+of list is reached, the result is @samp{allow}, unless explicitly
+specified otherwise.
+
+@FIXME{Trim that example:}
+For example, the following statement defines an @acronym{ACL} named
+@samp{common}, that allows access for any user connected via local
+@acronym{UNIX} socket @file{/tmp/dicod.sock} or coming from a local
+network @samp{192.168.10.0/24}. Any authenticated users are allowed,
+provided that they are allowed by another @acronym{ACL} @samp{my-nets}
+(which should have been defined before this definition). Users
+coming from the network @samp{10.10.0.0/24} are allowed if they
+authenticate themselves and are members of groups @samp{pies} or
+@samp{users}. Access is denied for anybody else:
+
+@smallexample
+@group
+acl common @{
+ allow all from ("/tmp/pies.sock", "192.168.10.0/24");
+ allow authenticated acl "my-nets";
+ allow group ("pies", "users") from "10.10.0.0/24";
+ deny all;
+@}
+@end group
+@end smallexample
+
@node include-meta1
-@chapter Using MeTA1 Configuration File
+@section Using MeTA1 Configuration File
@cindex /etc/meta1/meta1.conf
@command{Pies} is able to take a list of components from MeTA1
configuration file:
-@deffn {Pies Conf} include-meta1 @var{file}
+@deffn {Config} include-meta1 @var{file}
Parse @var{file} as MeTA1 configuration file and incorporate
components defined there into the current component list.
@@ -851,7 +1245,7 @@ Here, @var{compname} stands for the name of the component, and
latter is @file{/var/spool/meta1} by default. It can be changed using
the following statement
-@deffn {Pies Conf} meta1-queue-dir @var{dir}
+@deffn {Config} meta1-queue-dir @var{dir}
Set name of MeTA1 queue directory.
@end deffn
@@ -870,26 +1264,45 @@ component smtps @{
@end smallexample
@node Global Configuration
-@chapter Global Configuration
+@section Global Configuration
+@cindex Global Configuration
The statements described in this section affect @command{pies}
behavior as a whole.
-@deffn {Pies Conf} umask @var{number}
+@anchor{syslog}
+@deffn {Config} syslog @{ ... @}
+This block statement configures logging via syslog. It has two
+substatements:
+@end deffn
+
+@deffn {Config: syslog} tag @var{string}
+Prefix syslog messages with this string. By default, the program name
+is used.
+
+@deffn {Config: syslog} facility @var{string}
+Set syslog facility to use. Allowed values are: @samp{user},
+@samp{daemon}, @samp{auth}, @samp{authpriv}, @samp{mail}, @samp{cron},
+@samp{local0} through @samp{local7} (case-insensitive), or a facility
+number.
+@end deffn
+@end deffn
+
+@deffn {Config} umask @var{number}
Set the default umask. The @var{number} must be an octal value not greater
than @samp{777}. The default umask is inherited at startup.
@end deffn
-@deffn {Pies Conf} limits @var{arg}
+@deffn {Config} limits @var{arg}
Set global system limits for all pies components. @xref{Resources,
limits}, for a detailed description of @var{arg}.
@end deffn
-@deffn {Pies Conf} return-code @{ ... @}
+@deffn {Config} return-code @{ ... @}
Configure global exit actions. @xref{Exit Actions}, for a detailed
description of this statement.
@end deffn
-@deffn {Pies Conf} shutdown-timeout @var{number};
+@deffn {Config} shutdown-timeout @var{number};
Wait @var{number} of seconds for all components to shut down.
Default is 5 seconds.
@end deffn
@@ -899,7 +1312,7 @@ Default is 5 seconds.
@end menu
@node Less Useful Statements
-@section Less Useful Statements
+@subsection Less Useful Statements
Some configuration file statements are provided for completeness and
are rarely, if at all used. If used improperly, they may severely
@@ -911,18 +1324,18 @@ useless. Do not use them, unless you have a good knowledge of
needed by @command{pies}. Use them only if the defaults does not
suit your needs:
-@deffn {Pies Conf} pidfile @var{file}
+@deffn {Config} pidfile @var{file}
Write PID of the master @command{pies} process to @var{file}. By
default, master PID is stored in @file{@var{statedir}/pies.pid}
(@FIXME-pxref{statedir}).
@end deffn
-@deffn {Pies Conf} control-file @var{file}
+@deffn {Config} control-file @var{file}
Set file name of the @command{pies} control file. Default is
@file{@var{statedir}/pies.ctl}
@end deffn
-@deffn {Pies Conf} stat-file @var{file}
+@deffn {Config} stat-file @var{file}
Set file name of the statistics output file. Default is
@file{@var{statedir}/pies.stat}.
@end deffn
@@ -932,55 +1345,62 @@ however, you found such an implementation for it, that requires another
privileges, you may change them using the following three statements:
@command{pies} process.
-@deffn {Pies Conf} user @var{user-name}
+@deffn {Config} user @var{user-name}
Start @command{pies} with the UID and GID of this user.
@end deffn
-@deffn {Pies Conf} group @var{group-list}
+@deffn {Config} group @var{group-list}
Retain supplementary groups, specified in @var{group-list}.
@end deffn
-@deffn {Pies Conf} allgroups @var{bool}
+@deffn {Config} allgroups @var{bool}
Retain all supplementary groups of which user, given with
@command{user} statement, is a member.
@end deffn
@node Pies Debugging
@chapter Pies Debugging
+@xopindex{debug, described}
The amount of debugging information produced by @command{pies} is configured
-by the following two configuration statements. First of all, the standard
-@code{debug} block statement controls debugging of the underlying GNU
-Mailutils libraries (@pxref{Debug Statement, Mailutils Configuration
-File,, mailutils, GNU Mailutils Manual}). Secondly, the @code{debug}
-statement controls debugging output of the @command{pies} utility
-itself.
-
-@deffn {Pies Conf} debug @var{spec}
-Set debugging level for the @command{pies} code. @xref{Debug
-Statement, Mailutils Configuration File,, mailutils, GNU Mailutils
-Manual}, for a description of @var{spec} syntax. The following
-debugging levels are used:
+by the following statements:
+
+@deffn {Config} debug @var{level}
+Set debugging level. The @var{level} must be a non-negative decimal
+integer. In version @value{VERSION} the following debugging levels
+are used:
@table @asis
-@item trace1
+@item 1
Log all basic actions: starting and stopping of components, received incoming
@acronym{TCP} connections, sending mails. Notify about setting
limits. Log pre-startup actions (@pxref{Actions Before Startup}).
-@item trace2
+@item 2
Log setting particular limits. Log the recomputed alarms.
-@item trace4
+@item 4
Dump execution environments
-@item trace6
+@item 6
Debug the parser of MeTA1 configuration grammar.
-@item trace7
+@item 7
Debug the lexical analyzer of MeTA1 configuration file.
@end table
@end deffn
+@anchor{source-info}
+@deffn {Config} source-info @var{bool}
+This statement decides whether debugging messages should contain
+source information. To enable source information, use:
+
+@smallexample
+source-info yes;
+@end smallexample
+
+This feature is designed for @command{pies} developers.
+@end deffn
+
@node Configuration Example
@chapter Configuration Example
In this section we provide several examples of working @command{pies}
@@ -1135,6 +1555,7 @@ component pmult [disabled; scheduled for Mon 01 Dec 2008 20:27:02]
(the example above is split in two lines for readability).
@anchor{pies-restart}
+@xopindex{restart-component, described}
You can restart any component by using the
@option{--restart-component} (@option{-R}) option, e.g.:
@@ -1142,6 +1563,7 @@ component pmult [disabled; scheduled for Mon 01 Dec 2008 20:27:02]
$ pies -R pmult smtps
@end smallexample
+@xopindex{stop, described}
To stop all running components and shut down @command{pies}, use the
@option{--stop} (@option{-S}) command line option:
@@ -1149,9 +1571,9 @@ $ pies -R pmult smtps
$ pies --stop
@end smallexample
-@cindex dependencies, pies
+@cindex dependencies
@anchor{dump-depmap}
-@cindex --dump-depmap option, pies
+@xopindex{dump-depmap option, introduced}
Two options are provided for verifying inter-component
dependencies. The @option{--dump-depmap} option prints on the
standard output the @dfn{dependency map}. This map is a square matrix
@@ -1184,7 +1606,7 @@ component. There are two @samp{X} marks: in columns 1 and 2. This
means that @samp{smtps} depends on @samp{smar} and @samp{qmgr}.
@anchor{dump-prereq}
-@cindex --dump-prereq option, pies
+@xopindex{dump-prereq, described}
You can also list prerequisites explicitly:
@smallexample
@@ -1200,52 +1622,109 @@ smtps: smar qmgr
This section summarizes @command{pies} command line options.
@table @option
+@opsummary{config-help}
+@item --config-help
+Show configuration file summary. @xref{Pies Configuration File}.
+
+@opsummary{config-file}
+@item --config-file=@var{file}
+@item -c @var{file}
+Read configuration from @var{file}, instead of the default
+@file{/etc/pies.conf}.
+
+@xref{Pies Configuration File}.
+
+@item -E
+Preprocess configuration file and exit. @xref{Preprocessor}.
+
+@opsummary{force}
@item --force
Force startup even if another instance may be running.
+@opsummary{foreground}
@item --foreground
Remain in foreground.
+@opsummary{source-info}
+@item --source-info
+Show source info with debugging messages. @xref{source-info}.
+
+@opsummary{lint}
+@item --lint
+@itemx -t
+
+@opsummary{stderr}
@item --stderr
Log to standard error.
+@opsummary{syslog}
@item --syslog
Log to syslog. This is the default.
+@opsummary{debug}
@item -x @var{level}
@itemx --debug=@var{level}
Set debug verbosity level. @xref{Pies Debugging}, for a description
of @var{level}.
+@opsummary{reload}
+@opsummary{hup}
@item -r
@itemx --reload
@itemx --hup
Reload the running instance of pies.
+@opsummary{restart-component}
@item -R
@itemx --restart-component
Restart components named in the command line. @xref{pies-restart}.
+@opsummary{status}
@item -s
@itemx --status
Display info about the running instance. @xref{pies-status}.
+@opsummary{stop}
@item -S
@itemx --stop
Stop the running instance.
+@opsummary{dump-depmap}
@item --dump-depmap
Dump dependency map. @xref{dump-depmap}.
+@opsummary{dump-prereq}
@item --dump-prereq
Dump prerequisite charts. @xref{dump-prereq}.
+@item --help
+Display a short usage summary and exit.
+
+@item --usage
+Display a short summary of available options and exit.
+
+@item --version
+Display program version and license information and exit.
+
@end table
-Apart from these, the common GNU Mailutils options are understood, which
-are useful for checking @command{pies} configuration file for syntax
-errors. @xref{Common Options, Common Options, , mailutils, GNU
-Mailutils Manual}, for a detailed description of these.
+@node Reporting Bugs
+@chapter How to Report a Bug
+
+ Send bug-reports and suggestions to @email{bug-mailfromd@@gnu.org.ua}.
+
+ If you think you've found a bug, please be sure to include maximum
+information needed to reliably reproduce it, or at least to analyze
+it. The information needed is:
+
+@itemize
+@item Version of the package you are using.
+@item Compilation options used when configuring the package.
+@item Run-time configuration (@file{pies.conf} file and the command
+line options used).
+@item Detailed description of the bug.
+@item Conditions under which the bug appears.
+@end itemize
@node Copying This Manual
@appendix GNU Free Documentation License
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a3ffed2..bbcb054 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -19,7 +19,6 @@ noinst_LIBRARIES=libpies.a
noinst_HEADERS = libpies.h
libpies_a_SOURCES=\
- nls.c\
parsetime.c\
proctitle.c\
strtotok.c
@@ -28,5 +27,3 @@ libpies_a_LIBADD=$(LIBOBJS)
INCLUDES = -I$(top_srcdir)/gnu -I../gnu
-AM_CPPFLAGS=-DLOCALEDIR=\"$(localedir)\"
-
diff --git a/lib/nls.c b/lib/nls.c
deleted file mode 100644
index b42192f..0000000
--- a/lib/nls.c
+++ b/dev/null
@@ -1,33 +0,0 @@
-/* This file is part of Pies.
- Copyright (C) 2009 Sergey Poznyakoff
-
- Pies is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
-
- Pies is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Pies. If not, see <http://www.gnu.org/licenses/>. */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include <libpies.h>
-#include <locale.h>
-#include <mailutils/nls.h>
-
-void
-mf_init_nls ()
-{
-#ifdef ENABLE_NLS
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- bindtextdomain ("mailfromd", LOCALEDIR);
- textdomain (PACKAGE);
-#endif
-}
diff --git a/lib/strtotok.c b/lib/strtotok.c
new file mode 100644
index 0000000..116c96e
--- a/dev/null
+++ b/lib/strtotok.c
@@ -0,0 +1,77 @@
+/* This file is part of Pies.
+ Copyright (C) 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include "libpies.h"
+#include "c-strcase.h"
+
+int
+strtotok_len (struct tokendef *tab, const char *str, size_t len, int *pres)
+{
+ for (; tab->name; tab++)
+ {
+ size_t kwlen = strlen (tab->name);
+ if (kwlen == len && memcmp (tab->name, str, len) == 0)
+ {
+ *pres = tab->tok;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int
+strtotok_len_ci (struct tokendef *tab, const char *str, size_t len, int *pres)
+{
+ for (; tab->name; tab++)
+ {
+ size_t kwlen = strlen (tab->name);
+ if (kwlen == len && c_strncasecmp (tab->name, str, len) == 0)
+ {
+ *pres = tab->tok;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int
+strtotok (struct tokendef *tab, const char *str, int *pres)
+{
+ return strtotok_len (tab, str, strlen (str), pres);
+}
+
+int
+strtotok_ci (struct tokendef *tab, const char *str, int *pres)
+{
+ return strtotok_len_ci (tab, str, strlen (str), pres);
+}
+
+int
+toktostr (struct tokendef *tab, int tok, const char **pres)
+{
+ for (; tab->name; tab++)
+ if (tab->tok == tok)
+ {
+ *pres = tab->name;
+ return 0;
+ }
+ return 1;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index c0c3b8a..970a8b3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,20 +37,16 @@ noinst_HEADERS = \
meta1lex.h\
pies.h
-EXTRA_DIST = pies.rcin
-noinst_DATA = pies.rc
-DISTCLEANFILES = pies.rc
-.rcin.rc:
- $(AM_V_GEN)sed 's|SBINDIR|$(sbindir)|g' $< > $@
-
meta1lex.c: meta1gram.h
incdir=$(pkgdatadir)/$(VERSION)/include
inc_DATA = pp-setup
+EXTRA_DIST = pp-setup
INCLUDES = \
-I$(top_srcdir)/lib\
-I$(top_srcdir)/gnu\
+ -I$(top_builddir)/gnu\
-I$(top_srcdir)/grecs/src
LDADD = \
diff --git a/src/acl.c b/src/acl.c
new file mode 100644
index 0000000..8561967
--- a/dev/null
+++ b/src/acl.c
@@ -0,0 +1,627 @@
+/* This file is part of Pies
+ Copyright (C) 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "pies.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <hash.h>
+
+struct pies_sockaddr
+{
+ unsigned netmask;
+ int salen;
+ struct sockaddr sa;
+};
+
+struct acl_entry
+{
+ grecs_locus_t locus;
+ int allow;
+ int authenticated;
+ pies_acl_t acl;
+ gl_list_t groups;
+ gl_list_t sockaddrs;
+};
+
+struct pies_acl
+{
+ char *name;
+ grecs_locus_t locus;
+ gl_list_t list;
+};
+
+
+
+/* ACL creation */
+
+pies_acl_t
+pies_acl_create (const char *name, grecs_locus_t *locus)
+{
+ pies_acl_t acl = xmalloc (sizeof (acl[0]));
+ acl->name = xstrdup (name);
+ acl->locus = *locus;
+ acl->list = gl_list_create_empty(&gl_linked_list_implementation,
+ NULL,
+ NULL,
+ NULL,
+ false);
+ return acl;
+}
+
+static struct pies_sockaddr *
+create_acl_sockaddr (int family, int len)
+{
+ struct pies_sockaddr *p = xzalloc (sizeof (*p));
+ p->salen = len;
+ p->sa.sa_family = family;
+ return p;
+}
+
+/* allow|deny [all|authenticated|group <grp: list>]
+ [acl <name: string>] [from <addr: list>] */
+
+static int
+_parse_token (struct acl_entry *entry, const grecs_value_t *value)
+{
+ if (strcmp (value->v.string, "all") == 0
+ || strcmp (value->v.string, "any") == 0)
+ /* FIXME: Nothing? */ ;
+ else if (strcmp (value->v.string, "auth") == 0
+ || strcmp (value->v.string, "authenticated") == 0)
+ entry->authenticated = 1;
+ else
+ return 1;
+ return 0;
+}
+
+static int
+_parse_sockaddr (struct acl_entry *entry, const grecs_value_t *value)
+{
+ struct pies_sockaddr *sptr;
+ const char *string;
+
+ if (assert_grecs_value_type (&entry->locus, value, GRECS_TYPE_STRING))
+ return 1;
+
+ string = value->v.string;
+
+ if (string[0] == '/')
+ {
+ size_t len;
+ struct sockaddr_un *s_un;
+
+ len = strlen (string);
+ if (len >= sizeof (s_un->sun_path))
+ {
+ grecs_error (&entry->locus, 0,
+ _("socket name too long: `%s'"), string);
+ return 1;
+ }
+ sptr = create_acl_sockaddr (AF_UNIX, sizeof (s_un));
+ s_un = (struct sockaddr_un *) &sptr->sa;
+ memcpy (s_un->sun_path, string, len);
+ s_un->sun_path[len] = 0;
+ }
+ else
+ {
+ struct in_addr addr;
+ struct sockaddr_in *s_in;
+ char *p = strchr (string, '/');
+
+ if (p)
+ *p = 0;
+
+ if (inet_aton (string, &addr) == 0)
+ {
+ struct hostent *hp = gethostbyname (string);
+ if (!hp)
+ {
+ grecs_error (&entry->locus, 0,
+ _("cannot resolve host name: `%s'"), string);
+ if (p)
+ *p = '/';
+ return 1;
+ }
+ memcpy (&addr.s_addr, hp->h_addr, sizeof (addr.s_addr));
+ }
+ addr.s_addr = ntohl (addr.s_addr);
+
+ sptr = create_acl_sockaddr (AF_INET, sizeof (s_in));
+ s_in = (struct sockaddr_in *) &sptr->sa;
+ s_in->sin_addr = addr;
+
+ if (p)
+ {
+ *p++ = '/';
+ char *q;
+ unsigned netlen;
+
+ netlen = strtoul (p, &q, 10);
+ if (*q == 0)
+ {
+ if (netlen == 0)
+ sptr->netmask = 0;
+ else
+ {
+ sptr->netmask = 0xfffffffful >> (32 - netlen);
+ sptr->netmask <<= (32 - netlen);
+ sptr->netmask = htonl (sptr->netmask);
+ }
+ }
+ else if (*q == '.')
+ {
+ struct in_addr addr;
+
+ if (inet_aton (p, &addr) == 0)
+ {
+ grecs_error (&entry->locus, 0,
+ _("invalid netmask: `%s'"), p);
+ return 1;
+ }
+ sptr->netmask = addr.s_addr;
+ }
+ else
+ {
+ grecs_error (&entry->locus, 0, _("invalid netmask: `%s'"), p);
+ return 1;
+ }
+ }
+ else
+ sptr->netmask = 0xfffffffful;
+ }
+ gl_list_add_last (entry->sockaddrs, sptr);
+ return 0;
+}
+
+static int
+_parse_from (struct acl_entry *entry, size_t argc, const grecs_value_t *argv)
+{
+ if (argc == 0)
+ return 0;
+ else if (argv->type == GRECS_TYPE_LIST)
+ {
+ grecs_error (&entry->locus, 0, _("expected `from', but found list"));
+ return 1;
+ }
+ else if (strcmp (argv->v.string, "from"))
+ {
+ grecs_error (&entry->locus, 0, _("expected `from', but found `%s'"),
+ argv->v.string);
+ return 1;
+ }
+ argc--;
+ argv++;
+
+ if (argc == 0)
+ {
+ grecs_error (&entry->locus, 0,
+ _("unexpected end of statement after `from'"));
+ return 1;
+ }
+
+ entry->sockaddrs = gl_list_create_empty(&gl_linked_list_implementation,
+ NULL,
+ NULL,
+ NULL,
+ false);
+ if (argv->type == GRECS_TYPE_STRING)
+ {
+ if (_parse_sockaddr (entry, argv))
+ return 1;
+ }
+ else
+ {
+ gl_list_iterator_t itr = gl_list_iterator (argv->v.list);
+ const void *p;
+ int rc = 0;
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ rc += _parse_sockaddr (entry, (const grecs_value_t*) p);
+ gl_list_iterator_free (&itr);
+ if (rc)
+ return rc;
+ }
+
+ if (argc - 1)
+ {
+ grecs_warning (&entry->locus, 0, _("junk after `from' list"));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+_parse_sub_acl (struct acl_entry *entry, size_t argc, grecs_value_t *argv)
+{
+ if (argc == 0)
+ return 0;
+ if (strcmp (argv->v.string, "acl") == 0)
+ {
+ argc--;
+ argv++;
+ if (argc == 0)
+ {
+ grecs_error (&entry->locus, 0,
+ _("expected ACL name, but found end of statement"));
+ return 1;
+ }
+
+ if (argv->type != GRECS_TYPE_STRING)
+ {
+ grecs_error (&entry->locus, 0,
+ _("expected string, but found list"));
+ return 1;
+ }
+
+ entry->acl = pies_acl_lookup (argv->v.string);
+
+ if (!entry->acl)
+ {
+ grecs_error (&entry->locus, 0, _("ACL not defined: `%s'"),
+ argv->v.string);
+ return 1;
+ }
+ argc--;
+ argv++;
+ }
+ return _parse_from (entry, argc, argv);
+}
+
+static int
+_parse_group (struct acl_entry *entry, size_t argc, grecs_value_t * argv)
+{
+ if (strcmp (argv->v.string, "group") == 0)
+ {
+ argc--;
+ argv++;
+ if (argc == 0)
+ {
+ grecs_error (&entry->locus, 0,
+ _("expected group list, but found end of statement"));
+ return 1;
+ }
+ if (argv->type == GRECS_TYPE_STRING)
+ {
+ entry->groups = gl_list_create_empty(&gl_linked_list_implementation,
+ NULL,
+ NULL,
+ NULL,
+ false);
+ gl_list_add_last (entry->groups, (void *) argv->v.string);
+ }
+ else
+ entry->groups = argv->v.list;
+ argc--;
+ argv++;
+ }
+ return _parse_sub_acl (entry, argc, argv);
+}
+
+static int
+_parse_acl (struct acl_entry *entry, size_t argc, grecs_value_t *argv)
+{
+ if (assert_grecs_value_type (&entry->locus, argv, GRECS_TYPE_STRING))
+ return 1;
+ else if (_parse_token (entry, argv) == 0)
+ return _parse_sub_acl (entry, argc - 1, argv + 1);
+ else
+ return _parse_group (entry, argc, argv);
+}
+
+int
+parse_acl_line (grecs_locus_t *locus, int allow, pies_acl_t acl,
+ grecs_value_t *value)
+{
+ struct acl_entry *entry = xzalloc (sizeof (*entry));
+
+ entry->locus = *locus;
+ entry->allow = allow;
+
+ switch (value->type)
+ {
+ case GRECS_TYPE_STRING:
+ if (_parse_token (entry, value))
+ {
+ grecs_error (&entry->locus, 0, _("unknown word `%s'"),
+ value->v.string);
+ return 1;
+ }
+ break;
+
+ case GRECS_TYPE_ARRAY:
+ if (_parse_acl (entry, value->v.arg.c, value->v.arg.v))
+ return 1;
+ break;
+
+ case GRECS_TYPE_LIST:
+ grecs_error (locus, 0, _("unexpected list"));
+ return 1;
+ }
+ gl_list_add_last (acl->list, entry);
+ return 0;
+}
+
+int
+acl_section_parser (enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ void **pdata = cb_data;
+ pies_acl_t acl;
+
+ switch (cmd)
+ {
+ case grecs_callback_section_begin:
+ if (value->type != GRECS_TYPE_STRING)
+ grecs_error (locus, 0, _("ACL name must be a string"));
+ else if (!value->v.string)
+ grecs_error (locus, 0, _("missing ACL name"));
+ else
+ {
+ grecs_locus_t defn_loc;
+ acl = pies_acl_create (value->v.string, locus);
+ if (pies_acl_install (acl, &defn_loc))
+ {
+ grecs_error (locus, 0,
+ _("redefinition of ACL %s"),
+ value->v.string);
+ grecs_error (&defn_loc, 0,
+ _("location of the previous definition"));
+ return 1;
+ }
+ *pdata = acl;
+ }
+ break;
+
+ case grecs_callback_section_end:
+ case grecs_callback_set_value:
+ break;
+ }
+ return 0;
+}
+
+static int
+allow_cb (enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ pies_acl_t acl = varptr;
+
+ if (cmd != grecs_callback_set_value)
+ {
+ grecs_error (locus, 0, _("unexpected block statement"));
+ return 1;
+ }
+ parse_acl_line (locus, 1, acl, value);
+ return 0;
+}
+
+static int
+deny_cb (enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ pies_acl_t acl = varptr;
+ if (cmd != grecs_callback_set_value)
+ {
+ grecs_error (locus, 0, _("unexpected block statement"));
+ return 1;
+ }
+ parse_acl_line (locus, 0, acl, value);
+ return 0;
+}
+
+struct grecs_keyword acl_keywords[] = {
+ { "allow", N_("[all|authenticated|group <grp: list>] [from <addr: list>]"),
+ N_("Allow access"),
+ grecs_type_string, NULL, 0,
+ allow_cb },
+ { "deny", N_("[all|authenticated|group <grp: list>] [from <addr: list>]"),
+ N_("Deny access"),
+ grecs_type_string, NULL, 0,
+ deny_cb },
+ { NULL }
+};
+
+
+
+
+/* ACL verification */
+
+#define S_UN_NAME(sa, salen) \
+ ((salen < offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path)
+
+static int
+_check_sockaddr (struct pies_sockaddr *sptr, struct acl_input *input)
+{
+ if (sptr->sa.sa_family != input->addr->sa_family)
+ return 0;
+
+ switch (sptr->sa.sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin_clt = (struct sockaddr_in *) input->addr;
+ struct sockaddr_in *sin_item = (struct sockaddr_in *) &sptr->sa;
+
+ if (ntohl (sin_clt->sin_addr.s_addr) ==
+ (sin_item->sin_addr.s_addr & sptr->netmask))
+ return 1;
+ break;
+ }
+
+ case AF_UNIX:
+ {
+ struct sockaddr_un *sun_clt = (struct sockaddr_un *) input->addr;
+ struct sockaddr_un *sun_item = (struct sockaddr_un *) &sptr->sa;
+
+ if (S_UN_NAME (sun_clt, input->addrlen)[0]
+ && S_UN_NAME (sun_item, sptr->salen)[0]
+ && strcmp (sun_clt->sun_path, sun_item->sun_path) == 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+match_group (const char **groups, const char *arg)
+{
+ for (; *groups; groups++)
+ if (strcmp (*groups, arg) == 0)
+ return 1;
+ return 0;
+}
+
+static int
+_acl_check (struct acl_entry *ent, struct acl_input *input)
+{
+ int result = 1;
+
+ if (ent->authenticated)
+ {
+ result = input->user != NULL;
+ if (!result)
+ return result;
+ }
+
+ if (ent->groups)
+ {
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (ent->groups);
+ while (result && gl_list_iterator_next (&itr, &p, NULL))
+ result = match_group (input->groups, p);
+ gl_list_iterator_free (&itr);
+ if (!result)
+ return result;
+ }
+
+ result = pies_acl_check (ent->acl, input, 1);
+ if (!result)
+ return result;
+
+ if (ent->sockaddrs)
+ {
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (ent->sockaddrs);
+ result = 0;
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ {
+ if (_check_sockaddr ((struct pies_sockaddr *)p, input))
+ break;
+ }
+ gl_list_iterator_free (&itr);
+ }
+
+ return result;
+}
+
+static int
+_acl_check_cb (struct acl_entry *ent, struct acl_input *input, int *pres)
+{
+ int result = _acl_check (ent, input);
+ debug (10, ("%s:%d: %s", ent->locus.file, ent->locus.line,
+ /* TRANSLATIONS: `MATCHES' is the verb `match' in 2nd person.
+ E.g., in French: CONCORD AVEC */
+ result ? _("MATCHES") : _("does not match")));
+
+ if (result)
+ {
+ *pres = ent->allow;
+ return 1;
+ }
+ return 0;
+}
+
+int
+pies_acl_check (pies_acl_t acl, struct acl_input *input, int result)
+{
+ if (acl)
+ {
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (acl->list);
+ while (gl_list_iterator_next (&itr, &p, NULL)
+ && _acl_check_cb ((struct acl_entry *)p, input, &result))
+ ;
+ gl_list_iterator_free (&itr);
+ }
+ return result;
+}
+
+
+/* Hash table */
+
+static Hash_table *acl_table;
+
+/* Calculate the hash of a string. */
+static size_t
+acl_hasher (void const *data, unsigned n_buckets)
+{
+ const struct pies_acl *p = data;
+ return hash_string (p->name, n_buckets);
+}
+
+/* Compare two strings for equality. */
+static bool
+acl_compare (void const *data1, void const *data2)
+{
+ const struct pies_acl *p1 = data1;
+ const struct pies_acl *p2 = data2;
+ return strcasecmp (p1->name, p2->name) == 0;
+}
+
+int
+pies_acl_install (pies_acl_t acl, grecs_locus_t * locus)
+{
+ pies_acl_t ret;
+ if (!((acl_table
+ || (acl_table = hash_initialize (0, 0,
+ acl_hasher,
+ acl_compare, 0)))
+ && (ret = hash_insert (acl_table, acl))))
+ xalloc_die ();
+
+ if (ret != acl)
+ {
+ if (locus)
+ *locus = ret->locus;
+ return 1;
+ }
+ return 0;
+}
+
+pies_acl_t
+pies_acl_lookup (const char *name)
+{
+ struct pies_acl samp;
+ if (!acl_table)
+ return NULL;
+ samp.name = (char *) name;
+ return hash_lookup (acl_table, &samp);
+}
diff --git a/src/acl.h b/src/acl.h
new file mode 100644
index 0000000..8d7725e
--- a/dev/null
+++ b/src/acl.h
@@ -0,0 +1,43 @@
+/* This file is part of Pies
+ Copyright (C) 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+/* acl.c */
+typedef struct pies_acl *pies_acl_t;
+
+struct acl_input
+{
+ struct sockaddr *addr;
+ socklen_t addrlen;
+ const char *user;
+ const char **groups;
+};
+
+pies_acl_t pies_acl_create (const char *name, grecs_locus_t *locus);
+int pies_acl_check (pies_acl_t acl, struct acl_input *input, int result);
+int parse_acl_line (grecs_locus_t *locus, int allow, pies_acl_t acl,
+ grecs_value_t *value);
+pies_acl_t pies_acl_lookup (const char *name);
+int pies_acl_install (pies_acl_t acl, grecs_locus_t * locus);
+
+int assert_grecs_value_type (grecs_locus_t *locus,
+ const grecs_value_t *value, int type);
+
+extern struct grecs_keyword acl_keywords[];
+extern int acl_section_parser (enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data);
diff --git a/src/addrfmt.c b/src/addrfmt.c
new file mode 100644
index 0000000..0687445
--- a/dev/null
+++ b/src/addrfmt.c
@@ -0,0 +1,133 @@
+/* This file is part of Pies
+ Copyright (C) 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "pies.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+int
+str2port (char *str)
+{
+ struct servent *serv;
+ char *p;
+ int port;
+
+ /* First try to read it from /etc/services */
+ serv = getservbyname (str, "tcp");
+
+ if (serv != NULL)
+ port = ntohs(serv->s_port);
+ else
+ {
+ unsigned long l;
+ /* Not in services, maybe a number? */
+ l = strtoul (str, &p, 0);
+
+ if (*p || l < 0 || l > USHRT_MAX)
+ return -1;
+
+ port = l;
+ }
+
+ return port;
+}
+
+static size_t
+_my_stpcpy (char **pbuf, size_t *psize, const char *src)
+{
+ size_t slen = strlen (src);
+ if (pbuf == NULL || *pbuf == NULL)
+ return slen;
+ else
+ {
+ char *buf = *pbuf;
+ size_t size = *psize;
+ if (size > slen)
+ size = slen;
+ memcpy (buf, src, size);
+ *psize -= size;
+ *pbuf += size;
+ if (*psize)
+ **pbuf = 0;
+ else
+ (*pbuf)[-1] = 0;
+ return size;
+ }
+}
+
+#define S_UN_NAME(sa, salen) \
+ ((salen < offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path)
+
+void
+sockaddr_to_str (const struct sockaddr *sa, int salen,
+ char *bufptr, size_t buflen,
+ size_t *plen)
+{
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)]; /* FIXME: too much */
+ size_t len = 0;
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in s_in = *(struct sockaddr_in *)sa;
+ len += _my_stpcpy (&bufptr, &buflen, inet_ntoa(s_in.sin_addr));
+ len += _my_stpcpy (&bufptr, &buflen, ":");
+ len += _my_stpcpy (&bufptr, &buflen,
+ umaxtostr(ntohs (s_in.sin_port), buf));
+ break;
+ }
+
+ case AF_UNIX:
+ {
+ struct sockaddr_un *s_un = (struct sockaddr_un *)sa;
+ if (S_UN_NAME(s_un, salen)[0] == 0)
+ len += _my_stpcpy (&bufptr, &buflen, "anonymous socket");
+ else
+ {
+ len += _my_stpcpy (&bufptr, &buflen, "socket ");
+ len += _my_stpcpy (&bufptr, &buflen, s_un->sun_path);
+ }
+ break;
+ }
+
+ default:
+ len += _my_stpcpy (&bufptr, &buflen, "{Unsupported family: ");
+ len += _my_stpcpy (&bufptr, &buflen, umaxtostr (sa->sa_family, buf));
+ len += _my_stpcpy (&bufptr, &buflen, "}");
+ }
+ if (plen)
+ *plen = len + 1;
+}
+
+char *
+sockaddr_to_astr (const struct sockaddr *sa, int salen)
+{
+ size_t size;
+ char *p;
+
+ sockaddr_to_str(sa, salen, NULL, 0, &size);
+ p = xmalloc (size);
+ sockaddr_to_str(sa, salen, p, size, NULL);
+ return p;
+}
diff --git a/src/diag.c b/src/diag.c
new file mode 100644
index 0000000..8ae3911
--- a/dev/null
+++ b/src/diag.c
@@ -0,0 +1,131 @@
+/* This file is part of Pies.
+ Copyright (C) 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "pies.h"
+#include "xvasprintf.h"
+
+unsigned debug_level;
+int source_info_option;
+
+void
+syslog_printer (int prio, const char *fmt, va_list ap)
+{
+#if HAVE_VSYSLOG
+ vsyslog (prio, fmt, ap);
+#else
+ char buf[128];
+ vsnprintf (buf, sizeof buf, fmt, ap);
+#endif
+}
+
+void
+vlogmsg (int prio, const char *fmt, va_list ap)
+{
+ if (log_to_stderr)
+ {
+ vfprintf (stderr, fmt, ap);
+ fprintf (stderr, "\n");
+ }
+ else
+ syslog_printer (prio, fmt, ap);
+}
+
+void
+logmsg (int prio, const char *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vlogmsg (prio, fmt, ap);
+ va_end (ap);
+}
+
+void
+debug_msg (const char *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vlogmsg (LOG_DEBUG, fmt, ap);
+ va_end (ap);
+}
+
+
+static struct obstack log_stk;
+static int log_stk_init;
+
+void
+logmsg_vprintf (int prio, const char *fmt, va_list ap)
+{
+ char *str, *p;
+
+ str = xvasprintf (fmt, ap);
+
+ if (!log_stk_init)
+ {
+ obstack_init (&log_stk);
+ log_stk_init = 1;
+ }
+ for (p = str; *p; )
+ {
+ size_t len = strcspn (p, "\n");
+ if (len)
+ obstack_grow (&log_stk, p, len);
+ p += len;
+ if (*p)
+ {
+ char *msg;
+
+ obstack_1grow (&log_stk, 0);
+ msg = obstack_finish (&log_stk);
+ logmsg (prio, "%s", msg);
+ obstack_free (&log_stk, msg);
+ p++;
+ }
+ }
+ free (str);
+}
+
+void
+logmsg_printf (int prio, const char *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ logmsg_vprintf (prio, fmt, ap);
+ va_end (ap);
+}
+
+void
+grecs_print_diag (grecs_locus_t *locus, int err, int errcode, const char *msg)
+{
+ if (locus)
+ {
+ if (errcode)
+ logmsg (err ? LOG_ERR : LOG_WARNING, "%s:%lu: %s: %s",
+ locus->file, (unsigned long)locus->line, msg,
+ strerror (errcode));
+ else
+ logmsg (err ? LOG_ERR : LOG_WARNING, "%s:%lu: %s",
+ locus->file, (unsigned long)locus->line, msg);
+ }
+ else
+ {
+ if (errcode)
+ logmsg (err ? LOG_ERR : LOG_WARNING, "%s: %s", msg,
+ strerror (errcode));
+ else
+ logmsg (err ? LOG_ERR : LOG_WARNING, "%s", msg);
+ }
+}
+
diff --git a/src/meta.c b/src/meta.c
new file mode 100644
index 0000000..9acc3bb
--- a/dev/null
+++ b/src/meta.c
@@ -0,0 +1,127 @@
+/* This file is part of Pies.
+ Copyright (C) 2008, 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "pies.h"
+#include <c-ctype.h>
+
+static const char *
+meta_expand (struct metadef *def, void *data)
+{
+ if (!def->value)
+ {
+ if (def->expand)
+ return def->expand (def, data);
+ def->value = "INTERNAL ERROR: NONEXPANDABLE DATA";
+ }
+ return def->value;
+}
+
+static const char *
+find_expansion_char (int c, struct metadef *def, void *data)
+{
+ for (; def->kw; def++)
+ if (def->kw[1] == 0 && def->kw[0] == c)
+ return meta_expand (def, data);
+ return NULL;
+}
+
+static const char *
+find_expansion_word (const char *kw, size_t len,
+ struct metadef *def, void *data)
+{
+ for (; def->kw; def++)
+ if (strlen (def->kw) == len && memcmp (def->kw, kw, len) == 0)
+ return meta_expand (def, data);
+ return NULL;
+}
+
+char *
+meta_expand_string (const char *string, struct metadef *def, void *data)
+{
+ const char *p, *s;
+ char *res;
+ struct obstack stk;
+
+ if (!string)
+ return NULL;
+
+ obstack_init (&stk);
+
+ for (p = string; *p;)
+ {
+ char *e;
+ size_t len = strcspn (p, "$");
+
+ obstack_grow (&stk, p, len);
+ p += len;
+ if (*p == '$')
+ {
+ switch (*++p)
+ {
+ case '$':
+ obstack_grow (&stk, p, 1);
+ p++;
+ break;
+
+ case '{':
+ e = strchr (p + 1, '}');
+ if (e && (s = find_expansion_word (p + 1, e - p - 1, def, data)))
+ {
+ obstack_grow (&stk, s, strlen (s));
+ p = e + 1;
+ }
+ else
+ {
+ obstack_grow (&stk, p - 1, 2);
+ p++;
+ }
+ break;
+
+ default:
+ if ((s = find_expansion_char (*p, def, data)) != NULL)
+ len = strlen (s);
+ else
+ {
+ s = p - 1;
+ len = 1;
+ }
+
+ obstack_grow (&stk, s, len);
+ p++;
+ }
+ }
+ else
+ obstack_grow (&stk, p, 1);
+ }
+ obstack_1grow (&stk, 0);
+ res = xstrdup (obstack_finish (&stk));
+ obstack_free (&stk, NULL);
+ return res;
+}
+
+void
+meta_free (struct metadef *def)
+{
+ for (; def->kw; def++)
+ {
+ if (def->storage)
+ {
+ free (def->storage);
+ def->value = def->storage = NULL;
+ }
+ }
+}
+
diff --git a/src/pies.c b/src/pies.c
index f36c21f..87ddad3 100644
--- a/src/pies.c
+++ b/src/pies.c
@@ -15,6 +15,8 @@
along with Pies. If not, see <http://www.gnu.org/licenses/>. */
#include "pies.h"
+#include <locale.h>
+#include <configmake.h>
#include "meta1lex.h"
char *conffile = SYSCONFDIR "/pies.conf";
@@ -106,7 +108,7 @@ _cb_action (enum grecs_callback_command cmd,
return 0;
}
-struct grecs_keyword return_code_cfg_param[] = {
+struct grecs_keyword return_code_keywords[] = {
{"action",
/* TRANSLATORS: disable and restart are keywords, do not translate them. */
N_("arg: {disable | restart}"),
@@ -718,7 +720,7 @@ _cb_limits (enum grecs_callback_command cmd,
return 0;
}
-struct grecs_keyword component_cfg_param[] = {
+struct grecs_keyword component_keywords[] = {
{"mode",
/* TRANSLATORS: The words between '{' and '}' are keywords, do not
translate them. */
@@ -868,7 +870,7 @@ struct grecs_keyword component_cfg_param[] = {
N_("<tag: exit-code-list>"),
NULL, /* FIXME: Docstring? */
grecs_type_section, NULL, 0,
- return_code_section_parser, NULL, return_code_cfg_param},
+ return_code_section_parser, NULL, return_code_keywords},
{NULL}
};
@@ -877,7 +879,7 @@ find_component_keyword (const char *ident)
{
struct grecs_keyword *kwp;
- for (kwp = component_cfg_param; kwp->ident; kwp++)
+ for (kwp = component_keywords; kwp->ident; kwp++)
if (strcmp (kwp->ident, ident) == 0)
return kwp;
return NULL;
@@ -1028,6 +1030,10 @@ component_section_parser (enum grecs_callback_command cmd,
case grecs_callback_section_end:
comp = *(struct component **) section_data;
component_finish (comp, locus);
+ break;
+
+ case grecs_callback_set_value:
+ grecs_error (locus, 0, _("expected block statement"));
}
return 0;
}
@@ -1064,13 +1070,13 @@ _cm_include_meta1 (enum grecs_callback_command cmd,
return 0;
}
-struct grecs_keyword pies_cfg_param[] = {
+struct grecs_keyword pies_keywords[] = {
/* FIXME */
{"component",
N_("<tag: string>"),
NULL, /* FIXME: Docstring */
grecs_type_section, NULL, 0,
- component_section_parser, NULL, component_cfg_param},
+ component_section_parser, NULL, component_keywords},
{"syslog",
NULL,
N_("Configure syslog logging"),
@@ -1140,7 +1146,7 @@ struct grecs_keyword pies_cfg_param[] = {
N_("<tag: exit-code-list>"),
NULL, /* FIXME: Docstring? */
grecs_type_section, &default_component, 0,
- return_code_section_parser, NULL, return_code_cfg_param},
+ return_code_section_parser, NULL, return_code_keywords},
{"acl",
N_("name: string"),
N_("Define ACL."),
@@ -1164,7 +1170,7 @@ struct grecs_keyword pies_cfg_param[] = {
void
config_init ()
{
- grecs_set_keywords (pies_cfg_param);
+ grecs_set_keywords (pies_keywords);
grecs_include_path_setup (DEFAULT_VERSION_INCLUDE_DIR,
DEFAULT_INCLUDE_DIR, NULL);
grecs_preprocessor = DEFAULT_PREPROCESSOR;
@@ -1178,19 +1184,18 @@ config_help ()
N_("Configuration file structure for pies.\n"
"For more information, use `info pies configuration'.");
grecs_format_docstring (stdout, docstring, 0);
- grecs_format_statement_array (stdout, pies_cfg_param, 1, 0);
+ grecs_format_statement_array (stdout, pies_keywords, 1, 0);
}
const char *program_version = "pies (" PACKAGE_STRING ")";
-const char *package_bugreport = "<" PACKAGE_BUGREPORT ">";
+const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
static char doc[] = N_("pies -- process invocation and execution supervisor");
static char args_doc[] = "";
enum
{
OPT_FOREGROUND = 256,
- OPT_LOG_TAG,
OPT_SYSLOG,
OPT_STDERR,
OPT_DUMP_PREREQ,
@@ -1210,8 +1215,6 @@ static struct argp_option options[] = {
{"foreground", OPT_FOREGROUND, 0, 0, N_("remain in foreground"), GRP + 1},
{"stderr", OPT_STDERR, NULL, 0, N_("log to stderr"),},
{"syslog", OPT_SYSLOG, NULL, 0, N_("log to syslog"),},
- {"log-tag", OPT_LOG_TAG, N_("STRING"), 0,
- N_("set the identifier used in syslog messages to STRING"), GRP + 1},
{"debug", 'x', N_("LEVEL"), 0,
N_("set debug verbosity level"), GRP + 1},
{"source-info", OPT_SOURCE_INFO, NULL, 0,
@@ -1304,10 +1307,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
force_option = 1;
break;
- case OPT_LOG_TAG:
- log_tag = arg;
- break;
-
case ARGP_KEY_INIT:
break;
@@ -1672,6 +1671,33 @@ remove_pidfile (char *name)
name, strerror (errno));
}
+const char version_etc_copyright[] =
+ /* Do *not* mark this string for translation. %s is a copyright
+ symbol suitable for this locale */
+ "Copyright %s 2009 Sergey Poznyakoff";
+
+
+static void
+version (FILE *stream, struct argp_state *state)
+{
+ fprintf (stream, "%s (%s) %s\n", "pies", PACKAGE, PACKAGE_VERSION);
+ /* TRANSLATORS: Translate "(C)" to the copyright symbol
+ (C-in-a-circle), if this symbol is available in the user's
+ locale. Otherwise, do not translate "(C)"; leave it as-is. */
+ fprintf (stream, version_etc_copyright, _("(C)"));
+
+ fputs (_("\
+\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+\n\
+"),
+ stream);
+
+ /* TRANSLATORS: %s denotes an author name. */
+ fprintf (stream, _("Written by %s.\n"), "Sergey Poznyakoff");
+}
int
main (int argc, char **argv)
@@ -1681,12 +1707,18 @@ main (int argc, char **argv)
extern char **environ;
set_program_name (argv[0]);
- mf_init_nls ();
+#ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ bindtextdomain ("mailfromd", LOCALEDIR);
+ textdomain (PACKAGE);
+#endif
mf_proctitle_init (argc, argv, environ);
/* Set default logging */
log_setup (!stderr_closed_p ());
config_init ();
+ argp_program_version_hook = version;
if (argp_parse (&argp, argc, argv, 0, &index, NULL))
exit (EX_USAGE);
diff --git a/src/pp-setup b/src/pp-setup
new file mode 100644
index 0000000..cd0264d
--- a/dev/null
+++ b/src/pp-setup
@@ -0,0 +1,116 @@
+divert(-1) dnl -*- m4 -*-
+# This file is part of Pies.
+# Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff
+#
+# Pies is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# Pies is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Pies. If not, see <http://www.gnu.org/licenses/>.
+
+changecom(/*,*/)
+
+/* ------------------------------
+ * Simulate --prefix-builtins.
+ * Borrowed from Autoconf
+ * ------------------------------- */
+
+define(`m4_define', defn(`define'))
+define(`m4_defn', defn(`defn'))
+define(`m4_undefine', defn(`undefine'))
+
+m4_undefine(`define')
+m4_undefine(`defn')
+m4_undefine(`undefine')
+
+/* m4_copy(SRC, DST)
+ * -----------------
+ * Define DST as the definition of SRC.
+ * What's the difference between:
+ * 1. m4_copy(`from', `to')
+ * 2. m4_define(`to', `from($@)')
+ * Well, obviously 1 is more expensive in space. Maybe 2 is more expensive
+ * in time, but because of the space cost of 1, it's not that obvious.
+ * Nevertheless, one huge difference is the handling of `$0'. If `from'
+ * uses `$0', then with 1, `to''s `$0' is `to', while it is `from' in 2.
+ * The user will certainly prefer to see `to'.
+ */
+m4_define(`m4_copy',
+`m4_define(`$2', m4_defn(`$1'))')
+
+/* m4_rename(SRC, DST)
+ * -------------------
+ * Rename the macro SRC as DST.
+ */
+m4_define(`m4_rename',
+`m4_copy(`$1', `$2')m4_undefine(`$1')')
+
+/* m4_rename_m4(MACRO-NAME)
+ * ------------------------
+ * Rename MACRO-NAME as m4_MACRO-NAME.
+ */
+m4_define(`m4_rename_m4',
+`m4_rename(`$1', `m4_$1')')
+
+/* Some m4 internals have names colliding with tokens we might use.
+ * Rename them a` la `m4 --prefix-builtins'.
+ */
+m4_rename_m4(`builtin')
+m4_rename_m4(`changecom')
+m4_rename_m4(`changequote')
+m4_rename_m4(`debugfile')
+m4_rename_m4(`debugmode')
+m4_rename_m4(`decr')
+m4_rename_m4(`divert')
+m4_rename_m4(`divnum')
+m4_rename_m4(`dumpdef')
+m4_rename_m4(`errprint')
+m4_rename_m4(`esyscmd')
+m4_rename_m4(`eval')
+m4_rename_m4(`format')
+m4_rename_m4(`ifdef')
+m4_rename_m4(`ifelse')
+m4_rename_m4(`include')
+m4_rename_m4(`incr')
+m4_rename_m4(`index')
+m4_rename_m4(`indir')
+m4_rename_m4(`len')
+m4_rename(`m4exit', `m4_exit')
+m4_rename(`m4wrap', `m4_wrap')
+m4_rename_m4(`maketemp')
+m4_rename_m4(`patsubst')
+m4_rename_m4(`popdef')
+m4_rename_m4(`pushdef')
+m4_rename_m4(`regexp')
+m4_rename_m4(`shift')
+m4_rename_m4(`sinclude')
+m4_rename_m4(`substr')
+m4_rename_m4(`symbols')
+m4_rename_m4(`syscmd')
+m4_rename_m4(`sysval')
+m4_rename_m4(`traceoff')
+m4_rename_m4(`traceon')
+m4_rename_m4(`translit')
+m4_rename_m4(`undivert')
+m4_rename_m4(`dnl')
+m4_rename_m4(`__line__')
+m4_rename_m4(`__file__')
+
+/* defined(X) -- Return true if the optional argument X is passed to
+ the function */
+m4_define(`defined',``$'#>@$1')
+
+/* printf(FMT, ...) */
+m4_define(`printf',`echo sprintf($*)')
+
+m4_define(`_', `m4_ifelse($#,1,`gettext($1)',``_'')')
+m4_define(`N_', `m4_ifelse($#,1,`$1',``N_'')')
+
+m4_divert(0)m4_dnl
diff --git a/src/progman.c b/src/progman.c
index cee6775..3211d9a 100644
--- a/src/progman.c
+++ b/src/progman.c
@@ -150,11 +150,11 @@ void
unlink_prog (struct prog *pp)
{
struct prog *x;
- if (x = pp->prev)
+ if ((x = pp->prev))
x->next = pp->next;
else
proghead = pp->next;
- if (x = pp->next)
+ if ((x = pp->next))
x->prev = pp->prev;
else
progtail = pp->prev;
diff --git a/src/url.c b/src/url.c
new file mode 100644
index 0000000..69abcfb
--- a/dev/null
+++ b/src/url.c
@@ -0,0 +1,212 @@
+/* This file is part of Pies
+ Copyright (C) 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "pies.h"
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+/* proto://[user[:password]@][host[:port]/]path[;arg=str[;arg=str...]
+*/
+
+static int
+alloc_string_len (char **sptr, const char *start, size_t len)
+{
+ *sptr = malloc (len + 1);
+ if (!*sptr)
+ return 1;
+ memcpy (*sptr, start, len);
+ (*sptr)[len] = 0;
+ return 0;
+}
+
+static int
+alloc_string (char **sptr, const char *start, const char *end)
+{
+ size_t len = end ? end - start : strlen (start);
+ return alloc_string_len (sptr, start, len);
+}
+
+static int
+url_parse_args (struct pies_url *url, char **str)
+{
+ struct wordsplit ws;
+
+ ws.ws_delim = ";";
+ if (wordsplit (*str, &ws, WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM))
+ return 1;
+ url->argc = ws.ws_wordc;
+ url->argv = ws.ws_wordv;
+ return 0;
+}
+
+static int
+url_parse_path (struct pies_url *url, char **str)
+{
+ char *p;
+
+ p = strchr (*str, ';');
+ if (!p)
+ p = *str + strlen (*str);
+ if (alloc_string(&url->path, *str, p))
+ return 1;
+ *str = p;
+ if (*p)
+ ++ * str;
+ return url_parse_args (url, str);
+}
+
+/* On input str points at the beginning of host part */
+static int
+url_parse_host (struct pies_url *url, char **str)
+{
+ char *s = *str;
+ size_t len = strcspn (s, "/:");
+
+ if (s[len] == ':')
+ {
+ char *q;
+ unsigned long n = strtoul (s + len + 1, &q, 10);
+ if ((*q && !strchr("/;:", *q)) || n > USHRT_MAX)
+ return 1;
+ url->port = n;
+ *str = q + strcspn(q, "/");
+ }
+ else
+ *str = s + len;
+ if (alloc_string_len (&url->host, s, len))
+ return 1;
+ if (**str)
+ {
+ ++*str;
+ return url_parse_path (url, str);
+ }
+ return 0;
+}
+
+/* On input str points past the mech:// part */
+static int
+url_parse_user (struct pies_url *url, char **str)
+{
+ size_t len = strcspn (*str, ":;@/");
+ char *p = *str + len;
+
+ switch (*p)
+ {
+ case ';':
+ case ':':
+ len = strcspn (p + 1, "@/:");
+ if (p[len+1] == '@')
+ {
+ if (alloc_string_len(&url->passwd, p + 1, len))
+ return 1;
+ if (alloc_string (&url->user, *str, p))
+ return 1;
+ *str = p + len + 2;
+ }
+ break;
+
+ case '@':
+ if (alloc_string (&url->user, *str, p))
+ return 1;
+ url->passwd = NULL;
+ *str = p + 1;
+ }
+ return url_parse_host (url, str);
+}
+
+static int
+url_parse_proto (struct pies_url *url, const char *str)
+{
+ char *p;
+
+ if (!str)
+ {
+ errno = EINVAL;
+ return 1;
+ }
+
+ p = strchr (str, ':');
+ if (!p)
+ {
+ errno = EINVAL;
+ return 1;
+ }
+
+ alloc_string (&url->proto, str, p);
+
+ /* Skip slashes */
+ for (p++; *p == '/'; p++)
+ ;
+ return url_parse_user (url, &p);
+}
+
+void
+pies_url_destroy (struct pies_url **purl)
+{
+ int i;
+ struct pies_url *url = *purl;
+
+ free (url->string);
+ free (url->proto);
+ free (url->host);
+ free (url->path);
+ free (url->user);
+ free (url->passwd);
+ for (i = 0; i < url->argc; i++)
+ free (url->argv[i]);
+ free (url->argv);
+ free(url);
+ *purl = NULL;
+}
+
+int
+pies_url_create (struct pies_url **purl, const char *str)
+{
+ int rc;
+ struct pies_url *url;
+
+ url = malloc (sizeof (*url));
+ if (!url)
+ return 1;
+ memset (url, 0, sizeof(*url));
+ rc = url_parse_proto (url, str);
+ if (rc)
+ pies_url_destroy (&url);
+ else
+ {
+ url->string = strdup (str);
+ *purl = url;
+ }
+ return rc;
+}
+
+const char *
+pies_url_get_arg (struct pies_url *url, const char *argname)
+{
+ int i;
+ size_t arglen = strlen (argname);
+ for (i = 0; i < url->argc; i++)
+ {
+ size_t len = strcspn (url->argv[i], "=");
+ if (len == arglen && memcmp (url->argv[i], argname, arglen) == 0)
+ return url->argv[i] + len + 1;
+ }
+ return NULL;
+}
diff --git a/src/userprivs.c b/src/userprivs.c
new file mode 100644
index 0000000..3abb001
--- a/dev/null
+++ b/src/userprivs.c
@@ -0,0 +1,279 @@
+/* This file is part of Pies.
+ Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff
+
+ Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <stdbool.h>
+#include "pies.h"
+
+static bool
+str_eq (const void *elt1, const void *elt2)
+{
+ return strcmp (elt1, elt2) == 0;
+}
+
+void
+str_dispose (const void *elt)
+{
+ free ((void*)elt);
+}
+
+gl_list_t
+get_user_groups (gl_list_t init_list, const char *user)
+{
+ int rc;
+ struct group *gr;
+ gl_list_t list;
+
+ list = gl_list_create_empty(&gl_linked_list_implementation,
+ str_eq,
+ NULL,
+ str_dispose,
+ false);
+
+ if (init_list)
+ {
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (init_list);
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ {
+ char *s = xstrdup (p);
+ if (!gl_list_add_last (list, s))
+ free (s);
+ }
+ gl_list_iterator_free (&itr);
+ }
+
+ setgrent ();
+ for (rc = 0; rc == 0 && (gr = getgrent ());)
+ {
+ char **p;
+ for (p = gr->gr_mem; *p; p++)
+ if (strcmp (*p, user) == 0)
+ {
+ char *s = xstrdup (gr->gr_name);
+ if (!gl_list_add_last (list, s))
+ free (s);
+ break;
+ }
+ }
+ endgrent ();
+ return list;
+}
+
+/* Switch to the given UID/GID */
+int
+switch_to_privs (uid_t uid, gid_t gid, gl_list_t retain_groups)
+{
+ int rc = 0;
+ gid_t *emptygidset;
+ size_t size = 1, j = 1;
+
+ if (uid == 0)
+ {
+ logmsg (LOG_ERR, _("Refusing to run as root"));
+ return 1;
+ }
+
+ /* Create a list of supplementary groups */
+ size = 1 + (retain_groups ? gl_list_size (retain_groups) : 0);
+ emptygidset = xcalloc (size, sizeof emptygidset[0]);
+ emptygidset[0] = gid ? gid : getegid ();
+
+ if (retain_groups)
+ {
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (retain_groups);
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ {
+ struct group *group = getgrnam ((const char*)p);
+ if (!group)
+ {
+ logmsg (LOG_ERR, _("unknown group: %s"), (const char*)p);
+ free (emptygidset);
+ return 1;
+ }
+ emptygidset[j++] = group->gr_gid;
+ }
+ gl_list_iterator_free (&itr);
+ }
+
+ /* Reset group permissions */
+ if (geteuid () == 0 && setgroups (j, emptygidset))
+ {
+ logmsg (LOG_ERR, _("setgroups(1, %lu) failed: %s"),
+ (unsigned long) emptygidset[0], strerror (errno));
+ rc = 1;
+ }
+ free (emptygidset);
+
+ /* Switch to the user's gid. On some OSes the effective gid must
+ be reset first */
+
+#if defined(HAVE_SETEGID)
+ if ((rc = setegid (gid)) < 0)
+ logmsg (LOG_ERR, _("setegid(%lu) failed: %s"),
+ (unsigned long) gid, strerror (errno));
+#elif defined(HAVE_SETREGID)
+ if ((rc = setregid (gid, gid)) < 0)
+ logmsg (LOG_ERR, _("setregid(%lu,%lu) failed: %s"),
+ (unsigned long) gid, (unsigned long) gid, strerror (errno));
+#elif defined(HAVE_SETRESGID)
+ if ((rc = setresgid (gid, gid, gid)) < 0)
+ logmsg (LOG_ERR, _("setresgid(%lu,%lu,%lu) failed: %s"),
+ (unsigned long) gid,
+ (unsigned long) gid, (unsigned long) gid, strerror (errno));
+#endif
+
+ if (rc == 0 && gid != 0)
+ {
+ if ((rc = setgid (gid)) < 0 && getegid () != gid)
+ logmsg (LOG_ERR, _("setgid(%lu) failed: %s"),
+ (unsigned long) gid, strerror (errno));
+ if (rc == 0 && getegid () != gid)
+ {
+ logmsg (LOG_ERR, _("Cannot set effective gid to %lu"),
+ (unsigned long) gid);
+ rc = 1;
+ }
+ }
+
+ /* Now reset uid */
+ if (rc == 0 && uid != 0)
+ {
+ uid_t euid;
+
+ if (setuid (uid)
+ || geteuid () != uid
+ || (getuid () != uid && (geteuid () == 0 || getuid () == 0)))
+ {
+
+#if defined(HAVE_SETREUID)
+ if (geteuid () != uid)
+ {
+ if (setreuid (uid, -1) < 0)
+ {
+ logmsg (LOG_ERR, _("setreuid(%lu,-1) failed: %s"),
+ (unsigned long) uid, strerror (errno));
+ rc = 1;
+ }
+ if (setuid (uid) < 0)
+ {
+ logmsg (LOG_ERR, _("second setuid(%lu) failed: %s"),
+ (unsigned long) uid, strerror (errno));
+ rc = 1;
+ }
+ }
+ else
+#endif
+ {
+ logmsg (LOG_ERR, _("setuid(%lu) failed: %s"),
+ (unsigned long) uid, strerror (errno));
+ rc = 1;
+ }
+ }
+
+ euid = geteuid ();
+ if (uid != 0 && setuid (0) == 0)
+ {
+ logmsg (LOG_ERR, _("seteuid(0) succeeded when it should not"));
+ rc = 1;
+ }
+ else if (uid != euid && setuid (euid) == 0)
+ {
+ logmsg (LOG_ERR, _("Cannot drop non-root setuid privileges"));
+ rc = 1;
+ }
+
+ }
+
+ return rc;
+}
+
+
+void
+pies_priv_setup (struct pies_privs *privs)
+{
+ struct passwd *pw;
+ gl_list_t grplist = 0;
+
+ if (!privs || !privs->user)
+ return;
+
+ pw = getpwnam (privs->user);
+ if (!pw)
+ {
+ logmsg (LOG_ERR, _("no such user: %s"), privs->user);
+ exit (EX_CONFIG);
+ }
+
+ if (privs->allgroups)
+ grplist = get_user_groups (privs->groups, privs->user);
+ if (switch_to_privs (pw->pw_uid, pw->pw_gid,
+ grplist ? grplist : privs->groups))
+ exit (EX_SOFTWARE);
+ if (grplist)
+ gl_list_free (grplist);
+}
+
+
+void
+pies_epriv_setup (struct pies_privs *privs)
+{
+ uid_t uid;
+ gid_t gid;
+
+ if (privs)
+ {
+ struct passwd *pw;
+ if (!privs->user)
+ return;
+
+ pw = getpwnam (privs->user);
+ if (!pw)
+ {
+ logmsg (LOG_ERR, _("No such user: %s"), privs->user);
+ exit (EX_CONFIG);
+ }
+ uid = pw->pw_uid;
+ gid = pw->pw_gid;
+ }
+ else
+ {
+ uid = 0;
+ gid = 0;
+ }
+
+ if (setegid (gid))
+ {
+ logmsg (LOG_ERR, _("Cannot switch to EGID %lu: %s"),
+ (unsigned long) gid, strerror (errno));
+ exit (EX_USAGE);
+ }
+ if (seteuid (uid))
+ {
+ logmsg (LOG_ERR, _("Cannot switch to EUID %lu: %s"),
+ (unsigned long) uid, strerror (errno));
+ exit (EX_USAGE);
+ }
+}

Return to:

Send suggestions and report system problems to the System administrator.