summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am2
-rw-r--r--NEWS57
-rw-r--r--README2
-rw-r--r--README-hacking2
-rw-r--r--am/gcc.m42
-rw-r--r--am/proctitle.m42
-rw-r--r--bootstrap.conf2
-rw-r--r--configure.ac38
-rw-r--r--doc/.gitignore3
-rw-r--r--doc/Makefile.am16
-rw-r--r--doc/ctl.texi442
-rw-r--r--doc/gendocs.pl480
-rwxr-xr-xdoc/gendocs.sh490
-rw-r--r--doc/gendocs_template79
-rw-r--r--doc/html.init164
-rw-r--r--doc/inetd.texi2
-rw-r--r--doc/otherdoc.texi.in9
-rw-r--r--doc/pies.texi369
-rw-r--r--doc/usr-acl.texi2
-rw-r--r--doc/webdoc.init8
-rw-r--r--ident/Makefile.am2
-rw-r--r--ident/ident.c2
-rw-r--r--ident/ident.h2
-rw-r--r--ident/identity.h2
-rw-r--r--ident/pam.c2
-rw-r--r--ident/provider.c2
-rw-r--r--ident/system.c2
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/addrfmt.c2
-rw-r--r--lib/arraymember.c2
-rw-r--r--lib/closefds.c131
-rw-r--r--lib/envop.c2
-rw-r--r--lib/envop.h2
-rw-r--r--lib/grecsasrt.c2
-rw-r--r--lib/grecsasrt.h2
-rw-r--r--lib/libpies.h4
-rw-r--r--lib/mkfilename.c2
-rw-r--r--lib/netrc.c2
-rw-r--r--lib/parsetime.c2
-rw-r--r--lib/pp.c2
-rw-r--r--lib/proctitle.c2
-rw-r--r--lib/safe_strcmp.c2
-rw-r--r--lib/split3.c2
-rw-r--r--lib/strtotok.c2
-rw-r--r--lib/url.c2
-rw-r--r--lib/urlconn.c2
-rw-r--r--lib/wildmatch.c2
-rw-r--r--po/POTFILES.in2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/acl.c2
-rw-r--r--src/acl.h2
-rw-r--r--src/cmdline.opt4
-rw-r--r--src/comp.c94
-rw-r--r--src/ctl.c83
-rw-r--r--src/depmap.c406
-rw-r--r--src/diag.c16
-rw-r--r--src/inetd-bi.c2
-rw-r--r--src/inetd.c4
-rw-r--r--src/inetd.in2
-rw-r--r--src/limits.c2
-rw-r--r--src/meta1parse.c2
-rw-r--r--src/pies.c172
-rw-r--r--src/pies.h34
-rw-r--r--src/pies_syslog.h37
-rw-r--r--src/piesctl-cl.opt4
-rw-r--r--src/piesctl.c26
-rw-r--r--src/pp-setup2
-rw-r--r--src/prog.h14
-rw-r--r--src/progman.c430
-rw-r--r--src/socket.c15
-rw-r--r--src/sysdep.c2
-rw-r--r--src/syslog.c510
-rw-r--r--src/sysvinit.c6
-rw-r--r--src/telinit.opt2
-rw-r--r--src/userprivs.c2
-rw-r--r--src/utmp.c2
-rw-r--r--tests/Makefile.am4
-rw-r--r--tests/accept.at6
-rw-r--r--tests/atlocal.in2
-rw-r--r--tests/builtin.at4
-rw-r--r--tests/chargen.c2
-rw-r--r--tests/control.at4
-rw-r--r--tests/cyclic.at37
-rw-r--r--tests/env.at4
-rw-r--r--tests/envglobal.at86
-rw-r--r--tests/envop.at4
-rw-r--r--tests/envtest.c2
-rw-r--r--tests/expandenv.at50
-rw-r--r--tests/inet.at4
-rw-r--r--tests/iobuf.h2
-rw-r--r--tests/lines.c2
-rw-r--r--tests/maxinst.at4
-rw-r--r--tests/nt.c2
-rw-r--r--tests/passfd.at6
-rw-r--r--tests/readtime.c2
-rw-r--r--tests/recvfd.c2
-rw-r--r--tests/redirect.at4
-rw-r--r--tests/respawn.at4
-rw-r--r--tests/ret-exec.at4
-rw-r--r--tests/ret-notify.at4
-rw-r--r--tests/shell.at4
-rw-r--r--tests/shutdown.at4
-rw-r--r--tests/startup.at4
-rw-r--r--tests/testsuite.at8
-rw-r--r--tests/to.c2
-rw-r--r--tests/version.at4
107 files changed, 3099 insertions, 1413 deletions
diff --git a/.gitignore b/.gitignore
index 11f9326..c307c8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,4 @@ core
gnu
m4
stamp-h1
+/tmp
diff --git a/Makefile.am b/Makefile.am
index e741602..d7afea8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of GNU Pies.
-# Copyright (C) 2008-2020 Sergey Poznyakoff
+# Copyright (C) 2008-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NEWS b/NEWS
index 4d7f456..f253eac 100644
--- a/NEWS
+++ b/NEWS
@@ -1,12 +1,12 @@
-GNU Pies NEWS -- history of user-visible changes. 2020-10-17
+GNU Pies NEWS -- history of user-visible changes. 2020-12-14
See the end of file for copying conditions.
Please send Pies bug reports to <bug-pies@gnu.org> or
<bug-pies@gnu.org.ua>
-Version 1.4.91 (git)
+Version 1.5, 2020-12-14
-* Detect if pies is started from docker.
+* Detect if pies is started from docker
This makes the --no-init option unnecessary.
@@ -15,6 +15,55 @@ This makes the --no-init option unnecessary.
This is to make sure the exited preprocessor command is cleaned up
properly.
+* Fix cyclic dependency detection
+
+* New component flag: expandenv
+
+The "expandenv" flag instructs pies to parse the command line
+and to expand any references to environment variables within it
+as the Bourne shell would have done it. This allows for expanding
+the environment variables without the overhead of actually running
+the shell.
+
+This flag is incompatible with the "shell" flag. When both are used,
+the preference is given to "shell" and a warning message to that
+effect is issued.
+
+* Component standard stream redirection rewritten from scratch
+
+In particular, redirecting stdout/stderr to syslog no longer requires
+starting an auxiliar process.
+
+* The component.facility configuration statement is withdrawn
+
+To specify a particular facility, use the fully qualified
+facility.priority argument to the stdout or stderr statement, e.g.:
+
+ stderr syslog local1.err;
+
+* New configuration statement syslog.dev
+
+The statement configures the socket to communicate with the syslog
+daemon. Its argument is either the file name of the local socket
+or the IP[:PORT] specification, e.g.
+
+ syslog {
+ dev 172.31.255.252;
+ }
+
+* Global env section
+
+The "env" section appearing in global context modifies the environment
+for the main pies process. This modified environment will be
+inherited by all processes started by pies in the course of its normal
+operation.
+
+* New control endpoint /alive
+
+HTTP response to GET /alive/PROG reflects the current state of the
+component PROG: 200 if it is running, 503 if not, and 404 if there's
+no such component.
+
Version 1.4, 2019-07-02
@@ -215,7 +264,7 @@ part of Mailfromd (http://mailfromd.software.gnu.org.ua).
=========================================================================
Copyright information:
-Copyright (C) 2009-2020 Sergey Poznyakoff
+Copyright (C) 2009-2021 Sergey Poznyakoff
Permission is granted to anyone to make or distribute verbatim copies
of this document as received, in any medium, provided that the
diff --git a/README b/README
index caf1622..bba85c6 100644
--- a/README
+++ b/README
@@ -72,7 +72,7 @@ Send bug reports to <bug-pies@gnu.org.ua>.
* Copyright information:
-Copyright (C) 2009-2020 Sergey Poznyakoff
+Copyright (C) 2009-2021 Sergey Poznyakoff
Permission is granted to anyone to make or distribute verbatim copies
of this document as received, in any medium, provided that the
diff --git a/README-hacking b/README-hacking
index 8b58436..83a7de8 100644
--- a/README-hacking
+++ b/README-hacking
@@ -55,7 +55,7 @@ For more information about `bootstrap', run `bootstrap --help'.
* Copyright information
-Copyright (C) 2008-2020 Sergey Poznyakoff
+Copyright (C) 2008-2021 Sergey Poznyakoff
Permission is granted to anyone to make or distribute verbatim copies
of this document as received, in any medium, provided that the
diff --git a/am/gcc.m4 b/am/gcc.m4
index c4abfe1..4087099 100644
--- a/am/gcc.m4
+++ b/am/gcc.m4
@@ -1,5 +1,5 @@
# This file is part of Mailfromd. -*- autoconf -*-
-# Copyright (C) 2009-2020 Sergey Poznyakoff
+# Copyright (C) 2009-2021 Sergey Poznyakoff
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/am/proctitle.m4 b/am/proctitle.m4
index 9fff12f..7506bb5 100644
--- a/am/proctitle.m4
+++ b/am/proctitle.m4
@@ -1,5 +1,5 @@
dnl This file is part of Mailfromd.
-dnl Copyright (C) 2008-2020 Sergey Poznyakoff
+dnl Copyright (C) 2008-2021 Sergey Poznyakoff
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
diff --git a/bootstrap.conf b/bootstrap.conf
index 5c97565..55dbda1 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -1,5 +1,5 @@
# Bootstrap configuration for GNU Pies. -*- shell-script -*-
-# Copyright (C) 2008-2020 Sergey Poznyakoff
+# Copyright (C) 2008-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/configure.ac b/configure.ac
index 34c87a9..fe629bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
# This file is part of GNU Pies. -*- autoconf -*-
-# Copyright (C) 2009-2020 Sergey Poznyakoff
+# Copyright (C) 2009-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,11 +15,11 @@
# along with GNU Pies. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ([2.63])
-AC_INIT([GNU Pies], [1.4.91], [bug-pies@gnu.org.ua])
+AC_INIT([GNU Pies], [1.5], [bug-pies@gnu.org.ua])
AC_CONFIG_SRCDIR([src/pies.h])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
-AM_INIT_AUTOMAKE([1.11 gnits tar-ustar dist-bzip2 std-options silent-rules])
+AM_INIT_AUTOMAKE([1.15 gnits tar-ustar dist-bzip2 std-options silent-rules])
# Enable silent rules by default:
AM_SILENT_RULES([yes])
@@ -155,6 +155,38 @@ AH_BOTTOM([
# endif
])
+# #################################
+# Select interface used to close file descriptors greater than or
+# equal to the given one.
+#
+# Variants:
+# 1. closefrom call (FreeBSD)
+# 2. F_CLOSEM fcntl (NetBSD, AIX, IRIX)
+# 3. proc_pidinfo call (Darwin)
+# 4. /proc/self/fd filesystem (Linux)
+# 5. Brute force
+#
+# The defines created here direct conditionalal compilation in
+# lib/closefds.c
+
+AC_CHECK_FUNCS([closefrom])
+AC_CHECK_DECL([F_CLOSEM],
+ AC_DEFINE([HAVE_FCNTL_CLOSEM], [1],
+ [Use F_CLOSEM fcntl for mu_close_fds]),
+ [],
+ [#include <limits.h>
+ #include <fcntl.h>
+])
+
+AC_CHECK_HEADERS([libproc.h])
+AC_CHECK_FUNCS([proc_pidinfo])
+
+if test -d "/proc/self/fd" ; then
+ AC_DEFINE([HAVE_PROC_SELF_FD], [1], [Define if you have /proc/self/fd])
+fi
+
+# ##############################
+
AC_SUBST([DEFAULT_PIES_CONTROL_URL],['unix:///tmp/$${PIES_INSTANCE}.ctl'])
AC_ARG_VAR([DEFAULT_PIES_CONTROL_URL],
[URL of the default control socket])
diff --git a/doc/.gitignore b/doc/.gitignore
index 94d867b..e58b88f 100644
--- a/doc/.gitignore
+++ b/doc/.gitignore
@@ -3,6 +3,7 @@ Makefile.in
pies.info*
stamp-vti
version.texi
+otherdoc.texi
pies.aux
pies.cp
pies.cps
@@ -20,3 +21,5 @@ pies.ps
pies.toc
pies.tp
pies.vr
+pies.fns
+pies.vrs
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 33ae9cf..dddf34b 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of GNU Pies.
-# Copyright (C) 2005-2020 Sergey Poznyakoff
+# Copyright (C) 2005-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,6 +16,7 @@
info_TEXINFOS=pies.texi
pies_TEXINFOS=\
+ ctl.texi\
fdl.texi\
inetd.texi\
macros.texi\
@@ -74,8 +75,8 @@ master-menu: imprimatur-master-menu
untabify: imprimatur-untabify
final: imprimatur-final
-GENDOCS=$(srcdir)/gendocs.sh --no-copy-images --html --init-file='$(abs_srcdir)/html.init'
-EXTRA_DIST += gendocs.sh
+GENDOCS = perl gendocs.pl
+EXTRA_DIST += gendocs.pl
TEXI2DVI=texi2dvi -t '@set $(RENDITION)' -I $(top_srcdir)/imprimatur
@@ -87,9 +88,6 @@ manual:
TEXINPUTS=$(srcdir):$(top_srcdir)/build-aux:$(TEXINPUTS) \
MAKEINFO="$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS)" \
TEXI2DVI="$(TEXI2DVI) -t @finalout" \
- $(GENDOCS) $(PACKAGE) '$(PACKAGE_NAME) manual'
-
-manual.tar.bz2: manual
- tar cfj manual.tar.bz2 manual
-
-man-tar: manual.tar.bz2
+ $(GENDOCS) -C manual -o otherdoc.texi $(PACKAGE) otherdoc.texi.in
+ $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -DWEBDOC \
+ --html --init-file=webdoc.init $(info_TEXINFOS) -o manual
diff --git a/doc/ctl.texi b/doc/ctl.texi
new file mode 100644
index 0000000..77f519c
--- a/dev/null
+++ b/doc/ctl.texi
@@ -0,0 +1,442 @@
+@c This is part of the GNU Pies manual.
+@c Copyright (C) 2009--2021 Sergey Poznyakoff
+@c This file is distributed under GFDL 1.3 or any later version
+@c published by the Free Software Foundation.
+
+ This appendix describes @dfn{control API} used to communicate with the
+running @command{pies} daemon via the control interface
+(@pxref{control}). This API is used by @command{piesctl} (@pxref{piesctl}).
+
+The API is designed as a REST service and uses HTTP. Queries are sent to
+pies @dfn{endpoints}, each of which serves a distinct purpose. Data
+are serialized using the JSON format.
+
+The sections below describe in detail each endpoint and associated
+with it request types.
+
+@node /instance
+@appendixsec /instance
+ This endpoint controls the state of the running @command{pies}
+instance and accepts the following HTTP requests: @code{GET},
+@code{DELETE}, @code{POST} (or @code{PUT}).
+
+@deffn {Request} GET /instance
+ Retrieves information about the current instance. The response body
+is a JSON object with the following attributes:
+
+@table @samp
+@item PID
+PID of the running daemon.
+
+@item argv
+Array of the command line arguments. @samp{argv[0]} is the program
+name.
+
+@item binary
+Name of the @command{pies} binary.
+
+@item instance
+The instance name. @xref{instances}.
+
+@item package
+Package name (the string @samp{GNU Pies}).
+
+@item version
+Package version
+@end table
+
+Any of these can be used in the URI to request the information about
+that particular attribute, e.g.:
+
+@example
+GET /instance/argv @result{} @{"argv":["pies", "-x2"]@}
+@end example
+@end deffn
+
+@deffn {Request} DELETE /instance/PID
+Stops the current @command{pies} instance.
+@end deffn
+
+@deffn {Request} PUT /instance/PID
+@deffnx {Request} POST /instance/PID
+Restarts the current @command{pies} instance.
+@end deffn
+
+@node /conf
+@appendixsec /conf
+
+The @samp{/conf} endpoint allows the client to inspect and change the
+configuration of the running @command{pies} instance.
+
+@node /conf/files
+@appendixsubsec /conf/files
+
+@deffn {Request} GET /conf/files
+Return list of configuration files. On success, a JSON array is
+returned. Each array element is an object with two attributes:
+
+@defvr {Attr} string file
+Pathname of the configuration file.
+@end defvr
+
+@defvr {Attr} string syntax
+Configuration file syntax (@pxref{Syntax}).
+@end defvr
+
+For example:
+
+@example
+GET /conf/files @result{}
+[@{"file":"/etc/pies.conf", "syntax":"pies"@},
+ @{"file":"/etc/inetd.conf", "syntax":"inetd"@}]
+@end example
+@end deffn
+
+@deffn {Request} POST /conf/files
+Adds a new configuration file. The body must be a JSON object with
+@samp{file} and @samp{syntax} attributes, as described above. The
+@samp{file} value must contain a pathname of a configuration file
+written in a syntax supplied by the @samp{syntax} attribute
+(@pxref{Syntax}).
+
+This request returns 201 code on success. To actually parse and load
+the added configuration file, send a @samp{PUT} request to
+@samp{/conf/runtime} (@pxref{/conf/runtime}).
+@end deffn
+
+@deffn {Request} DELETE /conf/files/true
+Clears all previously configured configuration files. Responds with:
+
+@example
+@{ "message":"file list cleared", "status":"OK" @}
+@end example
+@end deffn
+
+@deffn {Request} DELETE /conf/files/[@var{list}]
+Removes files named in the @var{list} from the list of configuration files.
+
+The @samp{DELETE} response is 200 on success. To actually update the
+configuration of the running process, send a @samp{PUT} request to
+@samp{/conf/runtime} (@pxref{/conf/runtime}).
+@end deffn
+
+@node /conf/runtime
+@appendixsubsec /conf/runime
+
+This is a write-only URI. The only request supported is
+@samp{PUT /conf/runtime}. It initiates reloading of the
+@command{pies} configuration. Usually, this request is sent after one
+or more @samp{POST} and/or @samp{DELETE} requests to
+@samp{/conf/files}, in order to finalize the changes applied to the
+configuration.
+
+@node /programs
+@appendixsec /programs
+
+ A request sent to this URI selects one or more components and
+applies operation defined by the request type to all of them.
+
+ Components are selected using a query in the form of JSON object
+(a @dfn{selector}). Valid selectors are:
+
+@table @samp
+@item null
+@itemx false
+Matches nothing.
+
+@item true
+Matches all components.
+
+@item @{ "op": "component", "arg": @var{tag} @}
+Matches component with the given @var{tag} (@pxref{tag}).
+
+@item @{ "op": "type", "arg": "component" @}
+Matches all components.
+
+@item @{ "op": "type", "arg": "command" @}
+Matches all commands.
+
+@item @{ "op": "mode", "arg": @var{mode} @}
+Matches all components with the given @var{mode}. @xref{component
+mode}.
+
+@item @{ "op": "active" @}
+Matches all active components.
+
+@item @{ "op": "status", "arg": @var{status} @}
+Matches all components with the given @var{status} (one of
+@samp{stopped}, @samp{running}, @samp{listener}, @samp{sleeping},
+@samp{stopping}, @samp{finished}). @xref{component status} for a
+discussion of these values.
+
+@item @{ "op: "not", "arg": @var{condition} @}
+Negates @var{condition}, which is any valid selector.
+
+@item @{ "op": "and", "arg": @var{array} @}
+Returns the result of logical conjunction on the @var{array} of selectors.
+
+@item @{ "op": "or", "arg": @var{array} @}
+Returns the result of logical disjunction on the @var{array} of selectors.
+@end table
+
+ For example, the following selector matches all components that are
+in @samp{running} state, excepting components of @samp{inetd} mode:
+
+@example
+@{ "op": "and",
+ "arg": [ @{ "op": "type", "arg": "component" @},
+ @{ "op": "not", "arg": @{ "op": "mode", "arg": "inetd" @}
+ ]
+@}
+@end example
+
+The following requests are supported:
+
+@deffn {Request} GET /programs?@var{selector}
+@deffnx {Request} GET /programs/@var{tag}
+ This request returns information about components matched by
+@var{selector} (see below for the @samp{/programs/@var{tag} variant}.
+The response is a JSON array of descriptions. If no component matches
+the @var{selector}, empty array is returned. Each description is a
+JSON object with the following attributes:
+
+@deftypevr {Attr} string type
+Type of the described entity: @samp{component} for an instance of a
+configured component, and @samp{command} for a command run as a part
+of exit action (@pxref{Exit Actions}), including mailer invocations
+(@pxref{Notification}).
+@end deftypevr
+
+@deftypevr {Attr} string mode
+Mode of the entity. @xref{component mode}.
+@end deftypevr
+
+@anchor{component status}
+@deftypevr {Attr} string status
+Entity status. Possible values are:
+
+@table @code
+@item finished
+A @samp{once} or @samp{startup} component has finished.
+
+@item listener
+Component is an inetd listener.
+
+@item running
+Component is running.
+
+@item sleeping
+Component has been put to sleep because of excessive number of
+failures (@pxref{respawn}).
+
+@item stopped
+Component is stopped.
+
+@item stopping
+Component is being stopped (a @code{SIGTERM} was sent).
+@end table
+@end deftypevr
+
+@deftypevr {Attr} boolean active
+Whether this component is active. By default, all components are
+active, unless marked with a @samp{disable} flag (@pxref{flags}) or
+administratively stopped.
+@end deftypevr
+
+@deftypevr {Attr} integer PID
+PID of the running process.
+@end deftypevr
+
+@deftypevr {Attr} string URL
+(for @samp{inetd} components) URL of the socket the component is
+listening on.
+@end deftypevr
+
+@deftypevr {Attr} string service
+(for @samp{tcpmux} components) TCPMUX service name. @xref{TCPMUX}.
+@end deftypevr
+
+@deftypevr {Attr} string master
+(for @samp{tcpmux} components) Tag of master TCPMUX component.
+@xref{TCPMUX}.
+@end deftypevr
+
+@deftypevr {Attr} string runlevels
+For inittab components, the string of runlevels this component is
+configured to run in. @xref{Init Process}.
+@end deftypevr
+
+@deftypevr {Attr} integer wakeup-time
+If component is in the @samp{sleeping} state, this attribute gives the
+number of seconds after which an attempt will be made to restart it.
+@end deftypevr
+
+@deftypevr {Attr} array argv
+Component command line split into words.
+@end deftypevr
+
+@deftypevr {Attr} string command
+Component command.
+@end deftypevr
+@end deffn
+
+@deffn {Request} DELETE /programs?@var{selector}
+@deffnx {Request} DELETE /programs/@var{tag}
+Stop components matched by the @var{selector}. On success returns:
+
+@example
+@{ "status":"OK" @}
+@end example
+
+@noindent
+On failure, returns
+
+@example
+@{ "status":"ER", "message": @var{text} @}
+@end example
+
+@noindent
+where @var{text} is a textual human-readable description of the failure.
+@end deffn
+
+@deffn {Request} PUT /programs?@var{selector}
+@deffnx {Request} PUT /programs/@var{tag}
+Start components matched by @var{selector}.
+@end deffn
+
+@deffn {Request} POST /programs
+Restart components. The selector is supplied in the request content.
+@end deffn
+
+Wherever a selector is passed via query parameters, a simplified form
+with component tag passed as query path is also allowed. For example:
+
+@example
+ GET /programs/@var{tag}
+@end example
+
+@noindent
+is a shortcut for:
+
+@example
+@{ "op":"and",
+ "arg":[ @{"op":"type", "arg":"component"@},
+ @{"op":"component", "arg":@var{tag} @} ] @}
+@end example
+
+@node /alive
+@appendixsec /alive
+
+ This entry point accepts only @samp{GET} requests. The URI must not
+be empty and must not include sub-directories (parts separated with
+slashes). It is treated as the name of the component to return the
+status of. E.g. querying @samp{/alive/foo} returns the status of the
+component named @samp{foo}. The status is returned as HTTP status
+code:
+
+@table @asis
+@item 200
+The component is up and running. For regular components that means
+that the corresponding program is running. For @samp{inetd}
+components that means that the listener is listening on the configured
+socket.
+
+@item 403
+No component specified.
+
+@item 404
+There is no such component.
+
+@item 503
+The component is not running. This means that it has failed, or has
+been stopped administratively or (for @samp{once} and @samp{startup}
+components) that it has run once and finished.
+
+If the component has failed, the @samp{Retry-After:} HTTP header
+contains the number of seconds after which @command{pies} will retry
+starting this component.
+@end table
+
+@node /runlevel
+@appendixsec /runlevel
+ This URI is active when @command{pies} runs as init process
+(@pxref{Init Process}). It supports two requests:
+
+@deffn {Request} GET /runlevel
+Returns the current state of the program as a JSON object with the
+following attributes:
+
+@defvr {Attr} string runlevel
+Current runlevel. @xref{Runlevels}.
+@end defvr
+
+@defvr {Attr} string prevlevel
+Previous runlevel (@samp{N} if none).
+@end defvr
+
+@defvr {Attr} string bootstate
+Boot state. @xref{startup states}.
+@end defvr
+
+@defvr {Attr} string initdefault
+Default runlevel.
+@end defvr
+@end deffn
+
+@deffn {Request} PUT /runlevel/@{"runlevel":@var{L}@}
+Initiates transition from the current runlevel to runlevel @var{L}
+(@pxref{Runlevels}).
+@end deffn
+
+@node /environ
+@appendixsec /environ
+ This URI is active when @command{pies} runs as init process
+(@pxref{Init Process}). It manipulates the program initial
+environment, i.e. the environment that all programs inherit.
+@xref{Init Environment}.
+
+@deffn {Request} GET /environ/
+Returns entire environment formatted as a JSON array of strings. On
+success, the 200 response is returned:
+
+@example
+["RUNLEVEL=3", "CONSOLE=/dev/tty", ...]
+@end example
+@end deffn
+
+@deffn {Request} GET /environ/@var{var}
+Returns the value of the environment variable @var{var}, if such is
+defined. On success, the 200 response carries the object:
+
+@example
+@{ "status":"OK", "value":@var{string} @}
+@end example
+
+@noindent
+If the variable @var{var} is not defined, a 404 response is returned.
+On error, a 403 response is returned. In both cases, the response
+body is the usual @command{pies} diagnostics object:
+
+@example
+@{ "status":"ER", "message":@var{text} @}
+@end example
+@end deffn
+
+@deffn {Request} DELETE /environ/@var{var}
+Deletes from the environment the variable @var{var}. On success,
+responds with HTTP 200:
+
+@example
+@{ "status":"OK" @}
+@end example
+
+Error responses are the same as for @samp{GET}.
+@end deffn
+
+@deffn {Request} PUT /environ/@var{name}=@var{value}
+Initializes environment variable @var{name} to @var{value}. See
+@samp{GET} for the possible responses.
+@end deffn
+
+
+
diff --git a/doc/gendocs.pl b/doc/gendocs.pl
new file mode 100644
index 0000000..3574687
--- a/dev/null
+++ b/doc/gendocs.pl
@@ -0,0 +1,480 @@
+# This file is part of GNU Pies.
+# Copyright (C) 2020-2021 Sergey Poznyakoff
+#
+# GNU 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.
+#
+# GNU 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 GNU Pies. If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+use Getopt::Long qw(:config gnu_getopt no_ignore_case);
+use File::Basename;
+use File::Spec;
+use File::Path qw(make_path);
+use Pod::Usage;
+
+my $dirname = '.';
+my $pkgname;
+my $srcname;
+my $output_name;
+my @includes;
+
+GetOptions("h" => sub {
+ pod2usage(-message => "$0: generate docs",
+ -exitstatus => 0);
+ },
+ "help" => sub {
+ pod2usage(-exitstatus => 0, -verbose => 2);
+ },
+ "usage" => sub {
+ pod2usage(-exitstatus => 0, -verbose => 0);
+ },
+ 'source|s=s' => \$srcname,
+ 'directory|C=s' => \$dirname,
+ 'output|o=s' => \$output_name,
+ 'include|I=s@' => \@includes
+ ) or exit(1);
+
+$pkgname = shift @ARGV or pod2usage(-exitstatus => 1, -verbose => 0);
+$srcname //= "${pkgname}.texi";
+my $template_name = shift @ARGV or pod2usage(-exitstatus => 1, -verbose => 0);
+
+unless (-d $dirname) {
+ make_path($dirname);
+}
+
+unless ($output_name) {
+ $output_name = File::Spec->catfile($dirname, (fileparse($template_name, qr/\.[^.]*/))[0]);
+}
+
+if (@includes) {
+ @includes = map { '-I '.$_} @includes;
+ # FIXME: Not used yet
+}
+
+sub template_scan {
+ my $file = shift;
+ open(FH, '<', $file) or die "can't open $file: $!\n";
+ my $line = 0;
+ while (<FH>) {
+ chomp;
+ ++$line;
+ s{ \$ ((?:BASE)?FILE|SIZE) \( ([a-z_]+) \) }{
+ eval { Gendocs->instance($2, $pkgname, $srcname) };
+ if ($@) {
+ if ($@ =~ m{Can't locate object method "new"}) {
+ die "$file:$line: unknown format: $2\n";
+ } else {
+ die $@;
+ }
+ }
+ }gex;
+ }
+ close FH
+}
+
+sub template_expand {
+ my ($infile, $outfile) = @_;
+ open(IFH, '<', $infile) or die "can't open $infile: $!\n";
+ open(OFH, '>', $outfile) or die "can't open $outfile: $!\n";
+ while (<IFH>) {
+ chomp;
+ s{ \$ ((?:BASE)?FILE|SIZE) \( ([a-z_]+) \) }{
+ if ($1 eq 'FILE') {
+ Gendocs->instance($2)->output;
+ } elsif ($1 eq 'BASEFILE') {
+ basename(Gendocs->instance($2)->output);
+ } else {
+ Gendocs->instance($2)->size;
+ }
+ }gex;
+ print OFH "$_\n";
+ }
+}
+
+template_scan $template_name;
+Gendocs->generate();
+template_expand($template_name, $output_name);
+Gendocs->sweep();
+
+package Gendocs;
+use strict;
+use warnings;
+
+my %registry;
+
+sub generate {
+ my ($class) = @_;
+ my @keys = keys %registry;
+ foreach my $k (@keys) {
+ $registry{$k}->build();
+ $registry{$k}->mark();
+ }
+}
+
+sub sweep {
+ my ($class) = @_;
+ my @keys = keys %registry;
+ foreach my $k (@keys) {
+ unless ($registry{$k}->has_mark) {
+ $registry{$k}->remove;
+ delete $registry{$k};
+ }
+ }
+}
+
+sub new {
+ my ($class, $pkgname, $name) = @_;
+ unless (exists($registry{$class})){
+ $registry{$class} = bless { pkgname => $pkgname, input => $name }, $class;
+ }
+ return $registry{$class}
+}
+sub instance {
+ my ($class, $fmt, @args) = @_;
+ my $subclass = "Gendocs::".ucfirst($fmt);
+ unless (exists($registry{$subclass})) {
+ $registry{$subclass} = $subclass->new(@args);
+ }
+ return $registry{$subclass};
+}
+
+sub runcom {
+ my $self = shift;
+ system @_;
+ if ($? == -1) {
+ die "failed to execute $_[0]: $!";
+ } elsif ($? & 127) {
+ die sprintf("$_[0] died with signal %d\n", $? & 127);
+ } elsif ($? >> 8) {
+ warn sprintf("$_[0] exited with value %d\n", $? >> 8);
+ }
+}
+
+sub mark { shift->{mark} = 1 }
+sub has_mark { shift->{mark} }
+sub remove {
+ my ($self) = @_;
+ if ($self->{output}) {
+ unlink $self->{output};
+ delete $self->{output};
+ }
+}
+sub size {
+ my ($self) = @_;
+ my $s = (stat($self->output))[7];
+ if ($s > 1048576) {
+ $s = int($s / 1048576) . 'M';
+ } elsif ($s > 1024) {
+ $s = int($s / 1024) . 'K';
+ }
+ return $s;
+}
+
+sub pkgname { shift->{pkgname} }
+sub input { shift->{input} }
+sub output { shift->{output} }
+
+package Gendocs::Makeinfo;
+use strict;
+use warnings;
+use base 'Gendocs';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{makeinfo} = $ENV{'MAKEINFO'} || 'makeinfo';
+ return $self;
+}
+
+package Gendocs::Info;
+use strict;
+use warnings;
+use base 'Gendocs::Makeinfo';
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $output = File::Spec->catfile($dirname, $self->pkgname . '.info');
+ print "Generating info file: " . $self->input . " -> $output\n";
+ $self->runcom("$self->{makeinfo} -o $output " . $self->input);
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+
+package Gendocs::Info_gz;
+use strict;
+use warnings;
+use base 'Gendocs';
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $input = Gendocs->instance('info', $self->pkgname, $self->input)->build();
+ my $output = "$input.gz";
+ print "Compressing info file: $input -> $output\n";
+ $self->runcom("gzip -f -9 -c $input > $output");
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+
+package Gendocs::Ascii;
+use strict;
+use warnings;
+use base 'Gendocs::Makeinfo';
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $output = File::Spec->catfile($dirname, $self->pkgname . '.txt');
+ print "Generating ascii file: " . $self->input . " -> $output\n";
+ $self->runcom("$self->{makeinfo} -o $output --no-split --no-headers " . $self->input);
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+
+package Gendocs::Ascii_gz;
+use strict;
+use warnings;
+use base 'Gendocs';
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $input = Gendocs->instance('ascii', $self->pkgname, $self->input)->build();
+ my $output = "$input.gz";
+ print "Compressing ascii file: $input -> $output\n";
+ $self->runcom("gzip -f -9 -c $input > $output");
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+
+package Gendocs::Texinfo_gz;
+use strict;
+use warnings;
+use base 'Gendocs';
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $output = File::Spec->catfile($dirname, $self->pkgname . '.tar.gz');
+ print "Creating compressed sources: $output\n";
+ $self->runcom("tar czfh $output *.texinfo *.texi *.txi *.eps 2>/dev/null || /bin/true");
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+
+package Gendocs::Dvi;
+use strict;
+use warnings;
+use base 'Gendocs';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{texi2dvi} = $ENV{'TEXI2DVI'} || 'texi2dvi --build=tidy -t @finalout';
+ return $self;
+}
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $output = File::Spec->catfile($dirname, $self->pkgname . '.dvi');
+ my $cmd = "$self->{texi2dvi} -o $output $self->{input}";
+ print "Creating dvi: $cmd\n";
+ $self->runcom($cmd);
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+
+package Gendocs::Dvi_gz;
+use strict;
+use warnings;
+use base 'Gendocs';
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $input = Gendocs->instance('dvi', $self->pkgname, $self->input)->build();
+ my $output = "$input.gz";
+ print "Compressing dvi file: $input -> $output\n";
+ $self->runcom("gzip -f -9 -c $input > $output");
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+
+package Gendocs::Pdf;
+use strict;
+use warnings;
+use base 'Gendocs::Dvi';
+
+sub build {
+ my ($self) = @_;
+ unless ($self->{output}) {
+ my $output = File::Spec->catfile($dirname, $self->pkgname . '.pdf');
+ my $cmd = "$self->{texi2dvi} -o $output --pdf $self->{input}";
+ print "Creating pdf: $cmd\n";
+ $self->runcom($cmd);
+ $self->{output} = $output;
+ }
+ return $self->{output};
+}
+__END__
+=head1 NAME
+
+gendocs.pl - generate documentation in various formats
+
+=head1 SYNOPSIS
+
+B<gendocs.pl>
+[B<-C> I<DIR>]
+[B<-s> I<SOURCE>]
+[B<-o> I<OUTPUT-FILE>]
+[B<-I> I<INCLUDE-DIR>]
+[B<--directory=>I<DIR>]
+[B<--include=>I<INCLUDE-DIR>]
+[B<--output=>I<OUTPUT-FILE>]
+[B<--source=>I<SOURCE>]
+I<PACKAGE> I<TEMPLATE>
+
+B<gendocs.pl> B<-h> | B<--help> | B<--usage>
+
+=head1 DESCRIPTION
+
+Generates documentation for the I<PACKAGE> in various formats. I<TEMPLATE>
+is a template file for the index page. When processing I<TEMPLATE> the
+following I<macros> inform B<gendocs.pl> about the desired documentation
+formats and are expanded on output:
+
+=over 4
+
+=item B<$FILE(I<FORMAT>)>
+
+Full pathname of the documentation file in format I<FORMAT>.
+
+=item B<$BASEFILE(I<FORMAT>)>
+
+Base name of the documentation file for format I<FORMAT>.
+
+=item B<$SIZE(I<FORMAT>)>
+
+Size of the documentation file in format I<FORMAT>. Proper size suffix
+(B<K> or B<M>) is appended, as needed.
+
+=back
+
+The file is processed twice. On the first pass, the program collects the
+mentioned I<FORMAT>s. Then the requested files are generated. On the
+second pass, the macros are replaced with the actual values and the output
+index file is generated. The name of the index file can be supplied using
+the B<-o> (B<--output>) option. If it is not given, the name is obtained
+by removing last suffix (a substring beginning with a dot and containing
+one or more characters, excepting dots) from the I<TEMPLATE> argument.
+
+Unless the B<-C> (B<--directory>) option is given, the output will be
+generated in the current working directory. If the B<-C> option is
+given, all output files, including index file, will be generated in
+the supplied directory.
+
+The following output formats are supported:
+
+=over 4
+
+=item B<info>
+
+Monolithic info file.
+
+=item B<info_gz>
+
+Monolithic info file, compressed with B<gzip>
+
+=item B<ascii>
+
+Monolithic ASCII file.
+
+=item B<ascii_gz>
+
+Monolithic ASCII file, compressed with B<gzip>
+
+=item B<texinfo_gz>
+
+A tar archive with the Texinfo documentation sources, compressed with B<gzip>.
+
+=item B<dvi>
+
+TeX B<dvi> file.
+
+=item B<dvi_gz>
+
+TeX B<dvi> file, compressed with B<gzip>
+
+=item B<pdf>
+
+A B<PDF> file.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-C>, B<--directory=>I<DIR>
+
+Create all output files in the directory I<DIR>.
+
+=item B<-s>, B<--source=>I<SOURCE>
+
+Name of the main Texinfo source file. By default F<I<PACKAGE>.texi> is
+used.
+
+=item B<-o>, B<--output=>I<OUTPUT-FILE>
+
+Name of the output index file. By default it is constructed by removing
+the last filename suffix from I<TEMPLATE>. E.g. F<index.texi.in> produces
+F<index.texi>.
+
+=item B<-I>, B<--include=>I<INCLUDE-DIR>
+
+Name of the directory with Texinfo include files. This option is not
+actually used. It is reserved for future use.
+
+=back
+
+=head1 ENVIRONMENT
+
+The following environment variables affect the behavior of B<gendocs.pl>:
+
+=over 4
+
+=item B<MAKEINFO>
+
+Name and initial options of the B<makeinfo> program.
+
+=item B<TEXI2DVI>
+
+Name and initial options of the B<texi2dvi> program. The default is
+F<texi2dvi --build=tidy -t @finalout>.
+
+=item B<TEXINPUTS>
+
+Used by F<texi2dvi>. Colon-separated list of Texinfo input directories.
+
+=back
+
+=cut
diff --git a/doc/gendocs.sh b/doc/gendocs.sh
deleted file mode 100755
index 8ac3a06..0000000
--- a/doc/gendocs.sh
+++ b/dev/null
@@ -1,490 +0,0 @@
-#!/bin/sh -e
-# gendocs.sh -- generate a GNU manual in many formats. This script is
-# mentioned in maintain.texi. See the help message below for usage details.
-
-scriptversion=2015-02-28.17
-
-# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
-# Free Software Foundation, Inc.
-#
-# This program 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 of the License, or
-# (at your option) any later version.
-#
-# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# Original author: Mohit Agarwal.
-# Send bug reports and any other correspondence to bug-texinfo@gnu.org.
-#
-# The latest version of this script, and the companion template, is
-# available from Texinfo CVS:
-# http://savannah.gnu.org/cgi-bin/viewcvs/texinfo/texinfo/util/gendocs.sh
-# http://savannah.gnu.org/cgi-bin/viewcvs/texinfo/texinfo/util/gendocs_template
-#
-# An up-to-date copy is also maintained in Gnulib (gnu.org/software/gnulib).
-
-# TODO:
-# - image importation was only implemented for HTML generated by
-# makeinfo. But it should be simple enough to adjust.
-# - images are not imported in the source tarball. All the needed
-# formats (PDF, PNG, etc.) should be included.
-
-prog=`basename "$0"`
-srcdir=`pwd`
-
-scripturl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs.sh"
-templateurl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs_template"
-
-: ${SETLANG="env LANG= LC_MESSAGES= LC_ALL= LANGUAGE="}
-: ${MAKEINFO="makeinfo"}
-: ${TEXI2DVI="texi2dvi -t @finalout"}
-: ${DOCBOOK2HTML="docbook2html"}
-: ${DOCBOOK2PDF="docbook2pdf"}
-: ${DOCBOOK2TXT="docbook2txt"}
-: ${GENDOCS_TEMPLATE_DIR="."}
-: ${PERL='perl'}
-: ${TEXI2HTML="texi2html"}
-unset CDPATH
-unset use_texi2html
-
-version="gendocs.sh $scriptversion
-
-Copyright 2013 Free Software Foundation, Inc.
-There is NO warranty. You may redistribute this software
-under the terms of the GNU General Public License.
-For more information about these matters, see the files named COPYING."
-
-usage="Usage: $prog [OPTION]... PACKAGE MANUAL-TITLE
-
-Generate output in various formats from PACKAGE.texinfo (or .texi or
-.txi) source. See the GNU Maintainers document for a more extensive
-discussion:
- http://www.gnu.org/prep/maintain_toc.html
-
-Options:
- --email ADR use ADR as contact in generated web pages; always give this.
-
- -s SRCFILE read Texinfo from SRCFILE, instead of PACKAGE.{texinfo|texi|txi}
- -o OUTDIR write files into OUTDIR, instead of manual/.
- -I DIR append DIR to the Texinfo search path.
- --common ARG pass ARG in all invocations.
- --html ARG pass ARG to makeinfo or texi2html for HTML targets.
- --info ARG pass ARG to makeinfo for Info, instead of --no-split.
- --no-ascii skip generating the plain text output.
- --no-copy-images
- don't try to copy images referenced by img HTML tags,
- --source ARG include ARG in tar archive of sources.
- --split HOW make split HTML by node, section, chapter; default node.
-
- --texi2html use texi2html to make HTML target, with all split versions.
- --docbook convert through DocBook too (xml, txt, html, pdf).
-
- --help display this help and exit successfully.
- --version display version information and exit successfully.
-
-Simple example: $prog --email bug-gnu-emacs@gnu.org emacs \"GNU Emacs Manual\"
-
-Typical sequence:
- cd PACKAGESOURCE/doc
- wget \"$scripturl\"
- wget \"$templateurl\"
- $prog --email BUGLIST MANUAL \"GNU MANUAL - One-line description\"
-
-Output will be in a new subdirectory \"manual\" (by default;
-use -o OUTDIR to override). Move all the new files into your web CVS
-tree, as explained in the Web Pages node of maintain.texi.
-
-Please use the --email ADDRESS option so your own bug-reporting
-address will be used in the generated HTML pages.
-
-MANUAL-TITLE is included as part of the HTML <title> of the overall
-manual/index.html file. It should include the name of the package being
-documented. manual/index.html is created by substitution from the file
-$GENDOCS_TEMPLATE_DIR/gendocs_template. (Feel free to modify the
-generic template for your own purposes.)
-
-If you have several manuals, you'll need to run this script several
-times with different MANUAL values, specifying a different output
-directory with -o each time. Then write (by hand) an overall index.html
-with links to them all.
-
-If a manual's Texinfo sources are spread across several directories,
-first copy or symlink all Texinfo sources into a single directory.
-(Part of the script's work is to make a tar.gz of the sources.)
-
-As implied above, by default monolithic Info files are generated.
-If you want split Info, or other Info options, use --info to override.
-
-You can set the environment variables MAKEINFO, TEXI2DVI, TEXI2HTML,
-and PERL to control the programs that get executed, and
-GENDOCS_TEMPLATE_DIR to control where the gendocs_template file is
-looked for. With --docbook, the environment variables DOCBOOK2HTML,
-DOCBOOK2PDF, and DOCBOOK2TXT are also consulted.
-
-By default, makeinfo and texi2dvi are run in the default (English)
-locale, since that's the language of most Texinfo manuals. If you
-happen to have a non-English manual and non-English web site, see the
-SETLANG setting in the source.
-
-Email bug reports or enhancement requests to bug-texinfo@gnu.org.
-"
-
-MANUAL_TITLE=
-PACKAGE=
-EMAIL=webmasters@gnu.org # please override with --email
-commonarg= # passed to all makeinfo/texi2html invcations.
-dirargs= # passed to all tools (-I dir).
-dirs= # -I's directories.
-htmlarg=
-infoarg=--no-split
-generate_ascii=true
-outdir=manual
-source_extra=
-split=default
-srcfile=
-no_copy_images=
-
-while test $# -gt 0; do
- case $1 in
- -s) shift; srcfile=$1;;
- -o) shift; outdir=$1;;
- -I) shift; dirargs="$dirargs -I '$1'"; dirs="$dirs $1";;
- --common) shift; commonarg=$1;;
- --docbook) docbook=yes;;
- --email) shift; EMAIL=$1;;
- --html) shift; htmlarg=$1;;
- --info) shift; infoarg=$1;;
- --no-ascii) generate_ascii=false;;
- --source) shift; source_extra=$1;;
- --split) shift; split=$1;;
- --texi2html) use_texi2html=1;;
- --no-copy-images) no_copy_images=1;;
- --help) echo "$usage"; exit 0;;
- --version) echo "$version"; exit 0;;
- -*)
- echo "$0: Unknown option \`$1'." >&2
- echo "$0: Try \`--help' for more information." >&2
- exit 1;;
- *)
- if test -z "$PACKAGE"; then
- PACKAGE=$1
- elif test -z "$MANUAL_TITLE"; then
- MANUAL_TITLE=$1
- else
- echo "$0: extra non-option argument \`$1'." >&2
- exit 1
- fi;;
- esac
- shift
-done
-
-# makeinfo uses the dirargs, but texi2dvi doesn't.
-commonarg=" $dirargs $commonarg"
-
-# For most of the following, the base name is just $PACKAGE
-base=$PACKAGE
-
-if test -n "$srcfile"; then
- # but here, we use the basename of $srcfile
- base=`basename "$srcfile"`
- case $base in
- *.txi|*.texi|*.texinfo) base=`echo "$base"|sed 's/\.[texinfo]*$//'`;;
- esac
- PACKAGE=$base
-elif test -s "$srcdir/$PACKAGE.texinfo"; then
- srcfile=$srcdir/$PACKAGE.texinfo
-elif test -s "$srcdir/$PACKAGE.texi"; then
- srcfile=$srcdir/$PACKAGE.texi
-elif test -s "$srcdir/$PACKAGE.txi"; then
- srcfile=$srcdir/$PACKAGE.txi
-else
- echo "$0: cannot find .texinfo or .texi or .txi for $PACKAGE in $srcdir." >&2
- exit 1
-fi
-
-if test ! -r $GENDOCS_TEMPLATE_DIR/gendocs_template; then
- echo "$0: cannot read $GENDOCS_TEMPLATE_DIR/gendocs_template." >&2
- echo "$0: it is available from $templateurl." >&2
- exit 1
-fi
-
-# Function to return size of $1 in something resembling kilobytes.
-calcsize()
-{
- size=`ls -ksl $1 | awk '{print $1}'`
- echo $size
-}
-
-# copy_images OUTDIR HTML-FILE...
-# -------------------------------
-# Copy all the images needed by the HTML-FILEs into OUTDIR. Look
-# for them in the -I directories.
-copy_images()
-{
- test -n "$no_copy_images" && return
- local odir
- odir=$1
- shift
- $PERL -n -e "
-BEGIN {
- \$me = '$prog';
- \$odir = '$odir';
- @dirs = qw($dirs);
-}
-" -e '
-/<img src="(.*?)"/g && ++$need{$1};
-
-END {
- #print "$me: @{[keys %need]}\n"; # for debugging, show images found.
- FILE: for my $f (keys %need) {
- for my $d (@dirs) {
- if (-f "$d/$f") {
- use File::Basename;
- my $dest = dirname ("$odir/$f");
- #
- use File::Path;
- -d $dest || mkpath ($dest)
- || die "$me: cannot mkdir $dest: $!\n";
- #
- use File::Copy;
- copy ("$d/$f", $dest)
- || die "$me: cannot copy $d/$f to $dest: $!\n";
- next FILE;
- }
- }
- die "$me: $ARGV: cannot find image $f\n";
- }
-}
-' -- "$@" || exit 1
-}
-
-case $outdir in
- /*) abs_outdir=$outdir;;
- *) abs_outdir=$srcdir/$outdir;;
-esac
-
-echo "Making output for $srcfile"
-echo " in `pwd`"
-mkdir -p "$outdir/"
-
-cmd="$SETLANG $MAKEINFO -o $PACKAGE.info $commonarg $infoarg \"$srcfile\""
-echo "Generating info... ($cmd)"
-rm -f $PACKAGE.info* # get rid of any strays
-eval "$cmd"
-tar czf "$outdir/$PACKAGE.info.tar.gz" $PACKAGE.info*
-ls -l "$outdir/$PACKAGE.info.tar.gz"
-info_tgz_size=`calcsize "$outdir/$PACKAGE.info.tar.gz"`
-# do not mv the info files, there's no point in having them available
-# separately on the web.
-
-cmd="$SETLANG $TEXI2DVI $dirargs \"$srcfile\""
-printf "\nGenerating dvi... ($cmd)\n"
-eval "$cmd"
-# compress/finish dvi:
-gzip -f -9 $PACKAGE.dvi
-dvi_gz_size=`calcsize $PACKAGE.dvi.gz`
-mv $PACKAGE.dvi.gz "$outdir/"
-ls -l "$outdir/$PACKAGE.dvi.gz"
-
-cmd="$SETLANG $TEXI2DVI --pdf $dirargs \"$srcfile\""
-printf "\nGenerating pdf... ($cmd)\n"
-eval "$cmd"
-pdf_size=`calcsize $PACKAGE.pdf`
-mv $PACKAGE.pdf "$outdir/"
-ls -l "$outdir/$PACKAGE.pdf"
-
-if $generate_ascii; then
- opt="-o $PACKAGE.txt --no-split --no-headers $commonarg"
- cmd="$SETLANG $MAKEINFO $opt \"$srcfile\""
- printf "\nGenerating ascii... ($cmd)\n"
- eval "$cmd"
- ascii_size=`calcsize $PACKAGE.txt`
- gzip -f -9 -c $PACKAGE.txt >"$outdir/$PACKAGE.txt.gz"
- ascii_gz_size=`calcsize "$outdir/$PACKAGE.txt.gz"`
- mv $PACKAGE.txt "$outdir/"
- ls -l "$outdir/$PACKAGE.txt" "$outdir/$PACKAGE.txt.gz"
-fi
-
-# Split HTML at level $2 using program $1. Used for texi2html.
-html_split()
-{
- opt="--split=$2 --node-files $commonarg $htmlarg"
- cmd="$SETLANG $1 --output $PACKAGE.html $opt \"$srcfile\""
- printf "\nGenerating html by $2... ($cmd)\n"
- eval "$cmd"
- split_html_dir=$PACKAGE.html
- (
- cd ${split_html_dir} || exit 1
- if test ! -e index.html; then
- if test -f ${PACKAGE}.html; then
- ln -sf ${PACKAGE}.html index.html
- else
- echo >&2 "$0: ${split_html_dir}/${PACKAGE}.html does not exist"
- fi
- fi
- tar -czf "$abs_outdir/${PACKAGE}.html_$2.tar.gz" -- *.html
- )
- eval html_$2_tgz_size=`calcsize "$outdir/${PACKAGE}.html_$2.tar.gz"`
- rm -f "$outdir"/html_$2/*.html
- mkdir -p "$outdir/html_$2/"
- mv ${split_html_dir}/*.html "$outdir/html_$2/"
- rmdir ${split_html_dir}
-}
-
-if test -z "$use_texi2html"; then
- opt="--no-split --html -o $PACKAGE.html $commonarg $htmlarg"
- cmd="$SETLANG $MAKEINFO $opt \"$srcfile\""
- printf "\nGenerating monolithic html... ($cmd)\n"
- rm -rf $PACKAGE.html # in case a directory is left over
- eval "$cmd"
- html_mono_size=`calcsize $PACKAGE.html`
- gzip -f -9 -c $PACKAGE.html >"$outdir/$PACKAGE.html.gz"
- html_mono_gz_size=`calcsize "$outdir/$PACKAGE.html.gz"`
- copy_images "$outdir/" $PACKAGE.html
- mv $PACKAGE.html "$outdir/"
- ls -l "$outdir/$PACKAGE.html" "$outdir/$PACKAGE.html.gz"
-
- version=`makeinfo --version|sed -n '1s/.* \([0-9][0-9]*\)\.[0-9.]*/\1/p'`
- case $version in
- [0-9]*) ;;
- *) version=4;;
- esac
- # Before Texinfo 5.0, makeinfo did not accept a --split=HOW option,
- # it just always split by node. So if we're splitting by node anyway,
- # leave it out.
- if test $version -lt 5 -o "x$split" != xdefault; then
- split_arg=--split=$split
- opt="--html -o $PACKAGE.html $split_arg $commonarg $htmlarg"
- cmd="$SETLANG $MAKEINFO $opt \"$srcfile\""
- printf "\nGenerating html by $split... ($cmd)\n"
- eval "$cmd"
- split_html_dir=$PACKAGE.html
- copy_images $split_html_dir/ $split_html_dir/*.html
- (
- cd $split_html_dir || exit 1
- tar -czf "$abs_outdir/$PACKAGE.html_$split.tar.gz" -- *
- )
- eval \
- html_${split}_tgz_size=`calcsize "$outdir/$PACKAGE.html_$split.tar.gz"`
- rm -rf "$outdir/html_$split/"
- mv $split_html_dir "$outdir/html_$split/"
- du -s "$outdir/html_$split/"
- ls -l "$outdir/$PACKAGE.html_$split.tar.gz"
- CONDS="/%%IF *HTML_SECTION%%/,/%%ENDIF *HTML_SECTION%%/d;\
- /%%IF *HTML_CHAPTER%%/,/%%ENDIF *HTML_CHAPTER%%/d"
- else
- html_split "$MAKEINFO --html" node
- html_split "$MAKEINFO --html" chapter
- html_split "$MAKEINFO --html" section
- # should take account of --split here.
- CONDS="/%%ENDIF.*%%/d;/%%IF *HTML_SECTION%%/d;/%%IF *HTML_CHAPTER%%/d"
- fi
-else # use texi2html:
- opt="--output $PACKAGE.html $commonarg $htmlarg"
- cmd="$SETLANG $TEXI2HTML $opt \"$srcfile\""
- printf "\nGenerating monolithic html with texi2html... ($cmd)\n"
- rm -rf $PACKAGE.html # in case a directory is left over
- eval "$cmd"
- html_mono_size=`calcsize $PACKAGE.html`
- gzip -f -9 -c $PACKAGE.html >"$outdir/$PACKAGE.html.gz"
- html_mono_gz_size=`calcsize "$outdir/$PACKAGE.html.gz"`
- mv $PACKAGE.html "$outdir/"
-
- html_split "$TEXI2HTML" node
- html_split "$TEXI2HTML" chapter
- html_split "$TEXI2HTML" section
- CONDS="/%%ENDIF.*%%/d;/%%IF *HTML_SECTION%%/d;/%%IF *HTML_CHAPTER%%/d"
-fi
-
-printf "\nMaking .tar.gz for sources...\n"
-d=`dirname $srcfile`
-(
- cd "$d"
- srcfiles=`ls -d *.texinfo *.texi *.txi *.eps $source_extra 2>/dev/null` || true
- tar czfh "$abs_outdir/$PACKAGE.texi.tar.gz" $srcfiles
- ls -l "$abs_outdir/$PACKAGE.texi.tar.gz"
-)
-texi_tgz_size=`calcsize "$outdir/$PACKAGE.texi.tar.gz"`
-
-if test -n "$docbook"; then
- opt="-o - --docbook $commonarg"
- cmd="$SETLANG $MAKEINFO $opt \"$srcfile\" >${srcdir}/$PACKAGE-db.xml"
- printf "\nGenerating docbook XML... ($cmd)\n"
- eval "$cmd"
- docbook_xml_size=`calcsize $PACKAGE-db.xml`
- gzip -f -9 -c $PACKAGE-db.xml >"$outdir/$PACKAGE-db.xml.gz"
- docbook_xml_gz_size=`calcsize "$outdir/$PACKAGE-db.xml.gz"`
- mv $PACKAGE-db.xml "$outdir/"
-
- split_html_db_dir=html_node_db
- opt="$commonarg -o $split_html_db_dir"
- cmd="$DOCBOOK2HTML $opt \"${outdir}/$PACKAGE-db.xml\""
- printf "\nGenerating docbook HTML... ($cmd)\n"
- eval "$cmd"
- (
- cd ${split_html_db_dir} || exit 1
- tar -czf "$abs_outdir/${PACKAGE}.html_node_db.tar.gz" -- *.html
- )
- html_node_db_tgz_size=`calcsize "$outdir/${PACKAGE}.html_node_db.tar.gz"`
- rm -f "$outdir"/html_node_db/*.html
- mkdir -p "$outdir/html_node_db"
- mv ${split_html_db_dir}/*.html "$outdir/html_node_db/"
- rmdir ${split_html_db_dir}
-
- cmd="$DOCBOOK2TXT \"${outdir}/$PACKAGE-db.xml\""
- printf "\nGenerating docbook ASCII... ($cmd)\n"
- eval "$cmd"
- docbook_ascii_size=`calcsize $PACKAGE-db.txt`
- mv $PACKAGE-db.txt "$outdir/"
-
- cmd="$DOCBOOK2PDF \"${outdir}/$PACKAGE-db.xml\""
- printf "\nGenerating docbook PDF... ($cmd)\n"
- eval "$cmd"
- docbook_pdf_size=`calcsize $PACKAGE-db.pdf`
- mv $PACKAGE-db.pdf "$outdir/"
-fi
-
-printf "\nMaking index file...\n"
-
-curdate=`$SETLANG date '+%B %d, %Y'`
-sed \
- -e "s!%%TITLE%%!$MANUAL_TITLE!g" \
- -e "s!%%EMAIL%%!$EMAIL!g" \
- -e "s!%%PACKAGE%%!$PACKAGE!g" \
- -e "s!%%DATE%%!$curdate!g" \
- -e "s!%%HTML_MONO_SIZE%%!$html_mono_size!g" \
- -e "s!%%HTML_MONO_GZ_SIZE%%!$html_mono_gz_size!g" \
- -e "s!%%HTML_NODE_TGZ_SIZE%%!$html_node_tgz_size!g" \
- -e "s!%%HTML_SECTION_TGZ_SIZE%%!$html_section_tgz_size!g" \
- -e "s!%%HTML_CHAPTER_TGZ_SIZE%%!$html_chapter_tgz_size!g" \
- -e "s!%%INFO_TGZ_SIZE%%!$info_tgz_size!g" \
- -e "s!%%DVI_GZ_SIZE%%!$dvi_gz_size!g" \
- -e "s!%%PDF_SIZE%%!$pdf_size!g" \
- -e "s!%%ASCII_SIZE%%!$ascii_size!g" \
- -e "s!%%ASCII_GZ_SIZE%%!$ascii_gz_size!g" \
- -e "s!%%TEXI_TGZ_SIZE%%!$texi_tgz_size!g" \
- -e "s!%%DOCBOOK_HTML_NODE_TGZ_SIZE%%!$html_node_db_tgz_size!g" \
- -e "s!%%DOCBOOK_ASCII_SIZE%%!$docbook_ascii_size!g" \
- -e "s!%%DOCBOOK_PDF_SIZE%%!$docbook_pdf_size!g" \
- -e "s!%%DOCBOOK_XML_SIZE%%!$docbook_xml_size!g" \
- -e "s!%%DOCBOOK_XML_GZ_SIZE%%!$docbook_xml_gz_size!g" \
- -e "s,%%SCRIPTURL%%,$scripturl,g" \
- -e "s!%%SCRIPTNAME%%!$prog!g" \
- -e "$CONDS" \
-$GENDOCS_TEMPLATE_DIR/gendocs_template >"$outdir/index.html"
-
-echo "Done, see $outdir/ subdirectory for new files."
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
-# End:
diff --git a/doc/gendocs_template b/doc/gendocs_template
deleted file mode 100644
index 2f58d5a..0000000
--- a/doc/gendocs_template
+++ b/dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
-<head>
- <title>GNU Pies - Program Invocation and Execution Supervisor</title>
- <meta name="Generator" content="%%SCRIPTNAME%%" />
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <link rev="made" href="mailto:gray@gnu.org.ua" />
- <link rel="stylesheet" type="text/css" href="/software/pies/gray.css" />
- <link rel="icon" type="image/png" href="/graphics/gnu-head-icon.png" />
-</head>
-<body>
-<!--#include virtual="inc/header.html" -->
-<ul class='tabs'>
- <li><a href="pies.html">Main</a></li>
- <li><a href="example.html">Examples</a></li>
- <li><a href="download.html">Downloads</a></li>
- <li><a class="active" href="manual.html">Documentation</a></li>
-</ul>
-
-<div id="main">
-<p>The manual for <b>%%PACKAGE%%</b> is available in the following formats:</p>
-
-<ul>
- <li><a href="manual/%%PACKAGE%%.html">HTML
- (%%HTML_MONO_SIZE%%K bytes)</a> - entirely on one web page.</li>
- <li><a href="manual/html_node/index.html">HTML</a> - with one web page per
- node.</li>
-%%IF HTML_SECTION%%
- <li><a href="manual/html_section/index.html">HTML</a> - with one web page per
- section.</li>
-%%ENDIF HTML_SECTION%%
-%%IF HTML_CHAPTER%%
- <li><a href="manual/html_chapter/index.html">HTML</a> - with one web page per
- chapter.</li>
-%%ENDIF HTML_CHAPTER%%
- <li><a href="manual/%%PACKAGE%%.html.gz">HTML compressed
- (%%HTML_MONO_GZ_SIZE%%K gzipped characters)</a> - entirely on
- one web page.</li>
- <li><a href="manual/%%PACKAGE%%.html_node.tar.gz">HTML compressed
- (%%HTML_NODE_TGZ_SIZE%%K gzipped tar file)</a> -
- with one web page per node.</li>
-%%IF HTML_SECTION%%
- <li><a href="manual/%%PACKAGE%%.html_section.tar.gz">HTML compressed
- (%%HTML_SECTION_TGZ_SIZE%%K gzipped tar file)</a> -
- with one web page per section.</li>
-%%ENDIF HTML_SECTION%%
-%%IF HTML_CHAPTER%%
- <li><a href="manual/%%PACKAGE%%.html_chapter.tar.gz">HTML compressed
- (%%HTML_CHAPTER_TGZ_SIZE%%K gzipped tar file)</a> -
- with one web page per chapter.</li>
-%%ENDIF HTML_CHAPTER%%
- <li><a href="manual/%%PACKAGE%%.info.tar.gz">Info document
- (%%INFO_TGZ_SIZE%%K characters gzipped tar file)</a>.</li>
- <li><a href="manual/%%PACKAGE%%.txt">ASCII text
- (%%ASCII_SIZE%%K characters)</a>.</li>
- <li><a href="manual/%%PACKAGE%%.txt.gz">ASCII text compressed
- (%%ASCII_GZ_SIZE%%K gzipped characters)</a>.</li>
- <li><a href="manual/%%PACKAGE%%.dvi.gz">TeX dvi file
- (%%DVI_GZ_SIZE%%K characters gzipped)</a>.</li>
- <li><a href="manual/%%PACKAGE%%.pdf">PDF file
- (%%PDF_SIZE%%K characters)</a>.</li>
- <li><a href="manual/%%PACKAGE%%.texi.tar.gz">Texinfo source
- (%%TEXI_TGZ_SIZE%%K characters gzipped tar file)</a></li>
-</ul>
-
-</div>
-
-<div class="generator">
-<p>(This page is generated by the <a
-href="%%SCRIPTURL%%">%%SCRIPTNAME%%</a> script.)
-</p>
-</div>
-
-<!--#include virtual="inc/footer.html" -->
-</body>
-</html>
diff --git a/doc/html.init b/doc/html.init
deleted file mode 100644
index a61082e..0000000
--- a/doc/html.init
+++ b/dev/null
@@ -1,164 +0,0 @@
-# Texi2any configuration for pies documentation. -*- perl -*-
-# Copyright (C) 2009-2020 Sergey Poznyakoff
-#
-# GNU 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.
-#
-# GNU 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 GNU Pies. If not, see <http://www.gnu.org/licenses/>.
-use strict;
-
-my $top_html_dir="/software/pies";
-my $graphics_dir="$top_html_dir/graphics";
-
-# Show TOC in place of the @contents directive.
-set_from_init_file('INLINE_CONTENTS', 1);
-# Do not show Texinfo menus.
-set_from_init_file('SHOW_MENU', 0);
-# Inhibit output of CSS lines in page headers.
-set_from_init_file('CSS_LINES', '');
-
-set_from_init_file('BODYTEXT', "");
-
-set_from_init_file('EXTRA_HEAD', qq{
- <link rev="made" href="mailto:gray\@gnu.org.ua">
- <link rel="stylesheet" type="text/css" href="${top_html_dir}/gray.css">
- <link rel="stylesheet" type="text/css" href="${top_html_dir}/texi.css">
- <link rel="icon" type="image/png" href="/graphics/gnu-head-icon.png">});
-
-set_from_init_file('AFTER_BODY_OPEN', qq{
-<!--#include virtual="${top_html_dir}/inc/header.html" -->
-<ul class="tabs">
- <li><a href="${top_html_dir}/pies.html">Main</a></li>
- <li><a href="${top_html_dir}/example.html">Examples</a></li>
- <li><a href="${top_html_dir}/download.html">Downloads</a></li>
- <li><a class="active" href="${top_html_dir}/manual.html">Documentation</a></li>
-</ul>});
-
-sub gray_end_file($)
-{
- my $self = shift;
- my $program_text = '';
- if ($self->get_conf('PROGRAM_NAME_IN_FOOTER')) {
- my $program_string = &{$self->{'format_program_string'}}($self);
- $program_text = "<p>
- $program_string
-</p>";
- }
- my $pre_body_close = $self->get_conf('PRE_BODY_CLOSE');
- $pre_body_close = '' if (!defined($pre_body_close));
- return "</div>
-<div class=\"copyright\">
-$program_text
-
-$pre_body_close
-</div>
-</div>
-</body>
-</html>
-";
-}
-
-texinfo_register_formatting_function('end_file', \&gray_end_file);
-
-set_from_init_file('PRE_BODY_CLOSE',
- 'Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.');
-
-# Print generating program name at the bottom of a page.
-set_from_init_file('PROGRAM_NAME_IN_FOOTER',1);
-
-# Disable horizontal bars
-set_from_init_file('DEFAULT_RULE', '');
-set_from_init_file('BIG_RULE', '');
-
-# Turn off navigation bars at the bottom of each section in chapter split mode,
-# or subsection in section split mode.
-set_from_init_file('HEADERS', undef)
- if ((get_conf('SPLIT') eq 'chapter') or (get_conf('SPLIT') eq 'section'));
-# Use navigation icons
-
-set_from_init_file('ICONS', 1);
-
-set_from_init_file('ACTIVE_ICONS',
- {
- 'Top' => "$graphics_dir/top.png",
- 'Contents' => "$graphics_dir/ctx.png",
- 'Overview' => '',
- 'Index' => "$graphics_dir/idx.png",
- 'This' => '',
- 'Back' => "$graphics_dir/left.png",
- 'FastBack' => "$graphics_dir/bwd.png",
- 'Prev' => "",
- 'Up' => "$graphics_dir/up.png",
- 'Next' => "$graphics_dir/right.png",
- 'NodeUp' => "$graphics_dir/left.png",
- 'NodeNext' => "$graphics_dir/up.png",
- 'NodePrev' => "$graphics_dir/right.png",
- 'Following' => "$graphics_dir/right.png",
- 'Forward' => "$graphics_dir/right.png",
- 'FastForward' => "$graphics_dir/fwd.png",
- 'About' => '',
- 'First' => '',
- 'Last' => '',
- ' ' => ''
- });
-
-sub gray_split_status()
-{
- my $split = get_conf('SPLIT');
- if ($split eq '') {
- return '';
- } elsif ($split eq 'node') {
- return ' <span class="splitstatus">(split by node)</span>';
- } elsif ($split eq 'section') {
- return ' <span class="splitstatus">(split by section)</span>';
- } elsif ($split eq 'chapter') {
- return ' <span class="splitstatus">(split by chapter)</span>';
- }
-}
-
-sub gray_document_title($$)
-{
- my $self = shift;
- my $direction = shift;
- my $status = gray_split_status();
-
- return q{<span class="title">} . $self->{title_string} . '</span>' .$status . q{<span class="title">:</span>};
-}
-
-sub gray_sec_ref($$)
-{
- return q{<span class="navtext">Section:</span>};
-}
-
-sub gray_chap_ref($$)
-{
- return q{<span class="navtext">Chapter:</span>};
-}
-
-
-my @gray_buttons = (\&gray_document_title, ' ',
- \&gray_sec_ref, 'Back', 'Forward', ' ',
- \&gray_chap_ref,
- 'FastBack', ' ', 'Up', ' ', 'FastForward', ' ',
- 'Contents', 'Index' );
-
-set_from_init_file('TOP_BUTTONS', undef);
-set_from_init_file('SECTION_BUTTONS', \@gray_buttons);
-set_from_init_file('CHAPTER_BUTTONS', \@gray_buttons);
-set_from_init_file('NODE_FOOTER_BUTTONS', \@gray_buttons);
-set_from_init_file('SECTION_FOOTER_BUTTONS', \@gray_buttons);
-set_from_init_file('MISC_BUTTONS',
- [\&gray_document_title,
- 'Contents',
- 'Index',
- ' ',
- 'About'
- ]);
diff --git a/doc/inetd.texi b/doc/inetd.texi
index 3a04403..23850c1 100644
--- a/doc/inetd.texi
+++ b/doc/inetd.texi
@@ -1,5 +1,5 @@
@c This is part of the GNU Pies manual.
-@c Copyright (C) 2009--2020 Sergey Poznyakoff
+@c Copyright (C) 2009--2021 Sergey Poznyakoff
@c This file is distributed under GFDL 1.3 or any later version
@c published by the Free Software Foundation.
diff --git a/doc/otherdoc.texi.in b/doc/otherdoc.texi.in
new file mode 100644
index 0000000..fc6ed08
--- a/dev/null
+++ b/doc/otherdoc.texi.in
@@ -0,0 +1,9 @@
+@multitable @columnfractions 0.30 .5
+@item @uref{../$FILE(info_gz),$BASEFILE(info_gz)} @tab Info document compressed ($SIZE(info_gz) characters gzipped tar file)
+@item @uref{../$FILE(ascii),$BASEFILE(ascii)} @tab ASCII text ($SIZE(ascii) characters)
+@item @uref{../$FILE(ascii_gz),$BASEFILE(ascii_gz)} @tab ASCII text, compressed ($SIZE(ascii_gz) characters)
+@item @uref{../$FILE(dvi_gz),$BASEFILE(dvi_gz)} @tab TeX dvi file ($SIZE(dvi_gz) characters gzipped)
+@item @uref{../$FILE(pdf),$BASEFILE(pdf)} @tab PDF file ($SIZE(pdf) characters gzipped)
+@item @uref{../$FILE(texinfo_gz),$BASEFILE(texinfo_gz)} @tab Texinfo source ($SIZE(texinfo_gz) characters gzipped tar file)
+@end multitable
+
diff --git a/doc/pies.texi b/doc/pies.texi
index b2c9716..0e95138 100644
--- a/doc/pies.texi
+++ b/doc/pies.texi
@@ -43,7 +43,7 @@ Published by the Free Software Foundation,
51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA
-Copyright @copyright{} 2005--2020 Sergey Poznyakoff
+Copyright @copyright{} 2005--2021 Sergey Poznyakoff
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -93,8 +93,14 @@ Appendices
* inetd configuration:: @file{Inetd.conf} Format.
* User-Group ACLs::
+* Control API::
* Copying This Manual:: The GNU Free Documentation License.
* Concept Index:: Index of Concepts.
+@ifset WEBDOC
+@ifhtml
+* This Manual in Other Formats::
+@end ifhtml
+@end ifset
@detailmenu
--- The Detailed Node Listing ---
@@ -124,6 +130,7 @@ Component Statement
* Component Privileges::
* Resources::
* Environment::
+* Early Environment Expansion::
* Actions Before Startup::
* Exit Actions::
* Output Redirectors::
@@ -661,6 +668,7 @@ to
In this subsection we assume the reader is sufficiently
acquainted with @command{m4} macro processor.
+@anchor{pp-setup}
@flindex pp-setup
The external preprocessor is invoked with @option{-s} flag, instructing
it to include line synchronization information in its output. This
@@ -727,6 +735,9 @@ component @var{tag} @{
@}
@end example
+@cindex component tag
+@cindex tag (component)
+@anchor{tag}
The component is identified by its @dfn{tag}, which is given as
argument to the @code{component} keyword. Component declarations with
the same tags are merged into a single declaration.
@@ -734,11 +745,12 @@ the same tags are merged into a single declaration.
The following are the basic statements which are allowed within the
@code{component} block:
+@anchor{component mode}
@deffn {Config: component} mode @var{mode}
Declare the type (style) of the component. Following are the basic
values for @var{mode}:
-@table @asis
+@table @code
@kindex exec
@kindex respawn
@item exec
@@ -777,7 +789,7 @@ after all regular components have terminated.
When run as init process (@pxref{Init Process}), the following
@var{mode}s are also allowed:
-@table @asis
+@table @code
@item boot
The process will be executed during system boot. The
@samp{runlevel} settings are ignored.
@@ -836,28 +848,24 @@ any other processes.
@end table
@end deffn
-@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.
-
-To supply command line arguments, use @code{command} statement.
-@end deffn
-
@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}
-statement. Its value will be available to the program as
-@code{argv[0]}.
+Command line to run. @var{string} is the full command line. Its
+first word (in the shell sense) is the name of the program to invoke.
@end deffn
+@deffn {Config: component} program @var{name}
+Full file name of the program to run. When supplied, @command{pies}
+will execute the program @var{name} instead of the first word in the
+@code{command} statement. The latter, however, will be passed to
+the running program as @code{argv[0]}.
+@end deffn
+
@anchor{flags}
@deffn {Config: component} flags (@var{flag-list})
Define flags for this component. The @var{flag-list} is a
comma-separated list of flags. Valid flags are:
-@table @asis
+@table @code
@kwindex disable
@item disable
This component is disabled, i.e.@: @command{pies} will parse and
@@ -891,6 +899,13 @@ component X @{
@}
@end example
+@kwindex expandenv
+@item expandenv
+Expand environment variables in the @samp{command} statement prior to
+running it. When used together with the @samp{shell} flag, this flag
+produces a warning and has no effect. @xref{Early Environment
+Expansion}, for a detailed discussion.
+
@kwindex wait
@item wait
This flag is valid only for @samp{inetd} components. It has the same
@@ -940,6 +955,7 @@ substatements.
* Component Privileges::
* Resources::
* Environment::
+* Early Environment Expansion::
* Actions Before Startup::
* Exit Actions::
* Output Redirectors::
@@ -957,10 +973,10 @@ Prerequisites (@pxref{component prerequisite}) for a component are
declared using the following statement:
@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:
+The argument is either a list of component tags or one of the
+following words:
-@table @asis
+@table @code
@item all
Declare all components defined so far as prerequisites for this one.
@@ -1000,8 +1016,8 @@ components specified in @file{meta1.conf} file (@pxref{include-meta1}).
@subsection Resources
@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
+Impose limits on system resources, as defined by the @var{string}
+argument. It consists of @dfn{commands}, optionally separated by any
amount of whitespace. A command is a single command letter followed
by a number, that specifies the limit. The command letters are
case-insensitive and coincide with those used by the shell @code{ulimit}
@@ -1056,6 +1072,12 @@ up to 1.3 and is still retained for backward compatibility. It is
described in @ref{env legacy syntax}. This subsection describes the
modern compount syntax.
+The @code{env} statement can also be used in global context, in which
+case it modifies environment for the master @command{pies} program,
+i.e. the environment that will be inherited by all components
+(@pxref{Global Configuration}). The global @code{env} is available
+only in compound syntax described here.
+
@deffn {Config: component} env @{ ... @}
The compound @code{env} statement has the following syntax:
@@ -1293,6 +1315,73 @@ env @{
@end table
@end deffn
+@node Early Environment Expansion
+@subsection Early Environment Expansion
+
+ By default any references to environment variables encountered in
+the @code{command} statement are not expanded. If you need to expand
+them, there are two @dfn{flags} (@pxref{flags}) at your disposal:
+@samp{shell} and @samp{expandenv}.
+
+@kwindex shell
+The @samp{shell} flag instructs @command{pies} to pass the command
+line specified by the the @code{command} statement as the argument to
+the @samp{/bin/sh -c} command (or another shell, if specified by the
+@samp{program} statement). This naturaly causes all references to the
+environment variables to be expanded, as in shell. The overhead is
+that two processes are run instead of the one: first the shell and
+second the command itself, being run as its child. This overhead
+can be eliminated by using the @code{exec} statement before the
+command, to instruct the shell to replace itself with the command
+without creating a new process.
+
+ Use this flag if the command you use in the component definition
+is a shell built-in, a pipe or another complex shell statement.
+
+@kwindex expandenv
+ Another way to expand environment variables in the command line is by
+specifying the @samp{expandenv} flag. This flag instructs
+@command{pies} to expand any variable references the same way that the
+Bourne shell would expand them, but without actually invoking the
+shell.
+
+ A variable reference has the form @samp{$@var{variable}} or
+@samp{$@{@var{variable}@}}, where @var{variable} is the variable
+name. The two forms are entirely equivalent. The form with curly
+braces is normally used if the variable name is immediately followed
+by an alphanumeric symbol, which will otherwise be considered part of it.
+This form also allows for specifying the action to take if the
+variable is undefined or expands to an empty value:
+
+@table @asis
+@item $@{@var{variable}:-@var{word}@}
+@dfn{Use Default Values}. If @var{variable} is unset or null, the expansion
+of @var{word} is substituted. Otherwise, the value of @var{variable} is
+substituted.
+
+@item $@{@var{variable}:=@var{word}@}
+@dfn{Assign Default Values}. If @var{variable} is unset or null, the
+expansion of @var{word} is assigned to variable. The value of
+@var{variable} is then substituted.
+
+@item $@{@var{variable}:?@var{word}@}
+@dfn{Display Error if Null or Unset}. If @var{variable} is null or unset,
+the expansion of @var{word} (or a message to that effect if @var{word} is
+not present) is output to the current logging channel. Otherwise, the
+value of @var{variable} is substituted.
+
+@item $@{@var{variable}:+@var{word}@}
+@dfn{Use Alternate Value}. If @var{variable} is null or unset, nothing is
+substituted, otherwise the expansion of @var{word} is substituted.
+@end table
+
+ When the two flags are used together, the preference is given to
+@samp{shell}, and a warning message to that effect is issued.
+
+ Also, please note, that whichever option you chose the environment
+variables available for expansion are those inherited by the parent
+shell and modified by the @code{env} statement (@pxref{Environment}).
+
@node Actions Before Startup
@subsection Actions Before Startup
@@ -1445,7 +1534,7 @@ Redirect standard error (if @code{stderr}) or standard output (if
The type of redirection is specified by @var{type} argument:
-@table @asis
+@table @code
@item file
Redirect to a file. In this case @var{channel} gives the full name of
the file. For example:
@@ -1455,26 +1544,25 @@ stderr file /var/log/component/name.err;
@end example
@item syslog
-Redirect to a syslog channel. The syslog priority is given by the
-@var{channel} argument. 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{syslog} statement (@pxref{syslog}), or from the @code{facility}
-statement (see below), if given.
+Redirect to syslog. The @var{channel} parameter is either the syslog
+facility and priority separated by dot or the priority alone, in which
+case the facility will be taken from the @code{syslog} statement
+(@pxref{syslog}).
Example:
@example
+stdout syslog local1.info;
stderr syslog err;
@end example
-@end table
-@end deffn
-@deffn {Config: component} facility @var{syslog-facility}
-Specify the syslog facility to use in syslog redirectors. Allowed
-@var{syslog-facility} values are: @samp{user}, @samp{daemon},
+Valid facilities are: @samp{user}, @samp{daemon},
@samp{auth}, @samp{authpriv}, @samp{mail}, @samp{cron}, @samp{local0}
-through @samp{local7} (all names case-insensitive), or a facility number.
+through @samp{local7} (all names case-insensitive).
+
+Valid priorities are: @samp{emerg}, @samp{alert}, @samp{crit},
+@samp{err}, @samp{warning}, @samp{notice}, @samp{info}, @samp{debug}.
+@end table
@end deffn
@node Inetd-Style Components
@@ -1507,7 +1595,7 @@ control ownership and file mode of @var{file}. They are a list of
assignments, separated by semicolons. The following values are
allowed:
-@table @asis
+@table @code
@item user
User name of the socket owner.
@@ -1609,7 +1697,7 @@ components that are supported internally by @command{pies} and do not
require external programs. In @command{pies} version @value{VERSION}
those are:
-@table @asis
+@table @code
@cindex echo
@item echo
Send back any received data. Defined in @RFC{862}.
@@ -1741,7 +1829,7 @@ Sets the name of the master TCPMUX service.
The @code{flags} statement (@pxref{flags}) must contain at least one
of the following flags:
-@table @asis
+@table @code
@item tcpmux
A ``dedicated'' TCPMUX subordinate service. When invoked, it must
output the @samp{+ CRLF} response itself.
@@ -2000,8 +2088,6 @@ component @var{tag} @{
# @r{or:}
acl @{ @dots{} @}
- # @r{Override default syslog facility for this component.}
- facility @var{facility};
# @r{Redirect program's standard output to the given}
# @r{file or syslog priority.}
# @xref{Output Redirectors}.
@@ -2115,14 +2201,14 @@ The table below lists all available variables and their expansions:
@caption{Notification Variables}
@multitable @columnfractions 0.5 0.5
@headitem Variable @tab Expansion
-@item canonical_program_name @tab @samp{pies}
-@item program_name @tab Program name of the @command{pies} binary.
-@item package @tab Package name (@samp{GNU Pies}).
-@item instance @tab Instance name (@pxref{instances}).
-@item version @tab Package version (@value{VERSION}).
-@item component @tab Name of the terminated component.
-@item termination @tab Termination cause (see below).
-@item retcode @tab Component exit code (or signal number, if exited
+@item @code{canonical_program_name} @tab @samp{pies}
+@item @code{program_name} @tab Program name of the @command{pies} binary.
+@item @code{package} @tab Package name (@samp{GNU Pies}).
+@item @code{instance} @tab Instance name (@pxref{instances}).
+@item @code{version} @tab Package version (@value{VERSION}).
+@item @code{component} @tab Name of the terminated component.
+@item @code{termination} @tab Termination cause (see below).
+@item @code{retcode} @tab Component exit code (or signal number, if exited
on signal), in decimal.
@end multitable
@end float
@@ -2401,7 +2487,7 @@ an absolute or relative file name. Optional arguments @var{args}
control ownership and file mode of @var{file}. They are a
semicolon-separated list of assignments to the following variables:
-@table @asis
+@table @code
@item user
User name of the socket owner.
@@ -2710,8 +2796,7 @@ syslog channel:
include-meta1 /etc/meta1/meta1.conf
component smtps @{
- facility local1;
- stderr syslog debug;
+ stderr syslog local1.debug;
@}
@end example
@@ -2721,22 +2806,41 @@ component smtps @{
The statements described in this section affect @command{pies}
behavior as a whole.
+@deffn {Config} env @{ @dots{} @}
+Modifies the environment for the running @command{pies} instance. The
+modified environment will be inherited by all processes started by
+@command{pies} in the course of its normal operation.
+
+@xref{Environment}, for a detailed discussion of the @code{env}
+statement syntax.
+@end deffn
+
@anchor{syslog}
@deffn {Config} syslog @{ @dots{} @}
-This block statement configures logging via syslog. It has two
+This block statement configures logging via syslog. It has the following
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} dev @var{address}
+Address of the socket the syslog daemon is listening on. By default,
+@file{/dev/log} is used.
+
+The @var{address} argument is either the file name of the UNIX socket
+file or IPv4 address of the syslog collector optionally followed by
+the colon and port number (or symbolic service name). If the port
+number is not supplied, the @samp{syslog} port (UDP) from
+@file{/etc/services} is used.
@end deffn
@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.
+@samp{local0} through @samp{local7} (case-insensitive).
+@end deffn
+
+@deffn {Config: syslog} tag @var{string}
+Prefix syslog messages with this string. By default, the program name
+is used.
@end deffn
@deffn {Config} umask @var{number}
@@ -2785,7 +2889,7 @@ for their termination. If any components are left running after
procedure.
This means that @command{pies} termination sequence can take
-up to 5*@code{shutdown-timeout} seconds.
+up to 2*@code{shutdown-timeout} seconds.
@end deffn
@node Pies Privileges
@@ -2933,7 +3037,7 @@ sections describe available commands in detail.
@command{pies} instance organized as key-value pairs. When invoked
without arguments, the following data are returned:
-@table @asis
+@table @code
@item package
Canonical package name.
@item version
@@ -3097,7 +3201,7 @@ eklogin IR 13836 /usr/local/sbin/klogind -k -c -e
Use @var{condition} to select the components to list. In its simplest
form, @var{condition} is one of the following @dfn{terms}:
-@table @asis
+@table @code
@item all
Selects all processes, including internal services, such as output
redirectors.
@@ -3125,7 +3229,7 @@ piesctl list mode inetd
@item status @var{arg}
Selects processes with the given status. Argument is one of:
-@table @asis
+@table @code
@item finished
Component is finished.
@@ -3379,11 +3483,12 @@ with another @samp{init} daemons, and the latter is in native
@command{pies} format (@pxref{Syntax}). Either of the files or even
both of them can be missing.
+@anchor{startup states}
The startup process passes through several states. Transition between
states is controlled by @dfn{runlevel}, which also defines the set of
components that must be executed. Startup states are:
-@table @asis
+@table @code
@item sysinit
System initialization state. This state marks the beginning of the
startup process. Only root partition is mounted, and is usually
@@ -3501,7 +3606,7 @@ Command to be executed and its arguments.
Component execution modes are:
-@table @asis
+@table @code
@item respawn
The basic execution mode. A @dfn{respawn} component is
restarted each time it terminates. If it is restarted more than 10
@@ -3596,7 +3701,7 @@ word @samp{pies} (with any amount of white space in between) introduce
a pragmatic comment that modifies the behavior of the configuration
parser. The following such comments are understood:
-@table @asis
+@table @code
@item #pies pragma debug @var{n}
Set debugging level @var{n} (a decimal number). @xref{Pies
Debugging}.
@@ -3827,7 +3932,7 @@ Unset environment variable @var{var}.
@node Docker Entrypoint
@chapter Using Pies as Entrypoint for Docker Container
-
+@cindex docker
Another use for @command{pies} is as an entrypoint in a docker
container. This is similar to the @command{init} mode described in
the previous chapter in that @command{pies} runs with PID 1. However,
@@ -3839,7 +3944,120 @@ detects the fact automatically and switches to the entrypoint mode.
Up to version 1.4.90, automatical detection was not implemented, and
it was necessary to use the @option{--no-init} option to discern
between the @code{entrypoint} and @code{init} modes. This is no longer
-necessary. The option, however, is still retained.
+the case. The option, however, is still retained.
+
+The following @file{Dockerfile} fragment illustrates how to configure
+@command{pies} to be run from a container:
+
+@example
+COPY pies.conf /etc
+ENTRYPOINT [ "/usr/sbin/pies", "--foreground", "--stderr" ]
+@end example
+
+@noindent
+It is supposed, of course, that the configuration file
+@file{pies.conf} is available in the same directory as
+@file{Dockerfile}.
+
+It is a common practice to supply configuration settings via the
+environment variables. To implement it in @file{pies.conf}, use
+either @code{expandenv} or @code{shell} flag (@pxref{Early Environment
+Expansion}). For example:
+
+@example
+ flags expandenv;
+ command "syslogd -n -R $LOGHOST";
+@end example
+
+This will expand the environment variable @env{LOGHOST} and pass its
+value as one of the arguments to @command{syslog}. The usual shell
+syntax is supported. For example, to provide a default value for the
+@option{-R} option above (in case @env{LOGHOST} is empty or
+undefined), use:
+
+@example
+ flags expandenv;
+ command "syslogd -n -R $@{LOGHOST:-172.19.255.255@}";
+@end example
+
+Configuration preprocessing (@pxref{Preprocessor}) can be used to
+conditionally enable parts of the @file{pies.conf} file, depending
+on the value of an environment variable. The technique described
+below assumes that you use GNU @command{m4} as preprocessor.
+
+Define the following two M4 macros:
+
+@deffn {M4 macro} CF_WITH_ENVAR @var{name} @var{text}
+Expands the environment variable @var{name} within @var{text}.
+The macro does so by temporarily redefining the symbol @var{name} to
+the value of the environment variable @var{name} and expanding
+@var{text}.
+
+The definition of the macro is:
+
+@example
+m4_define(`CF_WITH_ENVAR',m4_dnl
+`m4_pushdef(`$1',m4_esyscmd(printf "$`$1'"))m4_dnl
+$2`'m4_dnl
+m4_popdef(`$1')m4_dnl
+')
+@end example
+
+This macro allows you to use environment expansion where it is not
+normally supported. Consider, for example, this fragment:
+
+@example
+component @{
+ CF_WITH_ENVAR(`WORKDIR', `chdir "WORKDIR";')
+ ...
+@}
+@end example
+
+If you set @code{WORKDIR=/var/wd} prior to invoking @command{pies}, it
+will actually expand to
+
+@example
+component @{
+ chdir "/var/wd";
+ ...
+@}
+@end example
+
+@noindent
+@xref{Actions Before Startup, chdir}, for details about the
+@code{chdir} statement.
+@end deffn
+
+@deffn {M4 macro} CF_IF_ENVAR @var{name} @var{if-set} @var{if-unset}
+If the environment variable @var{name} is defined and has a non-empty
+value, expand @var{if-set}, otherwise expand @var{if-unset}. Expand
+each occurrence of @var{name} in @var{if-set} to the actual value of
+the environment variable.
+
+Following is the definition of this macro:
+
+@example
+m4_define(`CF_IF_ENVAR',m4_dnl
+`CF_WITH_ENVAR(`$1',`m4_ifelse($1,`',$3,$2)')')
+@end example
+
+This macro makes it possible to conditionally enable configuration
+file fragments depending on whether some environment variable is
+defined. E.g.:
+
+@example
+CF_IF_ENVAR(`LOGHOST',`
+component logger @{
+ command "syslogd -n -R LOGHOST;
+@}
+')
+@end example
+@end deffn
+
+Place both macros in a single file and include it at the top of your
+@file{pies.conf} using the @code{m4_include} command. Alternatively,
+you can place them in the @file{pp-setup} file (@pxref{pp-setup}),
+in which case they will be included automatically.
@node Configuration Examples
@chapter Configuration Examples
@@ -3864,9 +4082,8 @@ output are redirected to the syslog facility @samp{mail}, priorities
component pmult @{
command "/usr/local/sbin/pmult";
user meta1s;
- facility mail;
- stderr syslog err;
- stdout syslog info;
+ stderr syslog mail.err;
+ stdout syslog mail.info;
@}
@end example
@@ -4246,7 +4463,7 @@ Define the syntax for parsing the configuration files specified by any
@option{--config-file} options that follow this one. Possible values
for @var{type} are:
-@table @asis
+@table @code
@item pies
Native @command{pies} configuration. @xref{Configuration}.
@@ -4323,6 +4540,10 @@ line options used).
@appendix User-Group ACLs
@include usr-acl.texi
+@node Control API
+@appendix Control API
+@include ctl.texi
+
@node Copying This Manual
@appendix GNU Free Documentation License
@include fdl.texi
@@ -4334,4 +4555,12 @@ This is a general index of all issues discussed in this manual
@printindex cp
+@ifset WEBDOC
+@ifhtml
+@node This Manual in Other Formats
+@unnumbered This Manual in Other Formats
+@include otherdoc.texi
+@end ifhtml
+@end ifset
+
@bye
diff --git a/doc/usr-acl.texi b/doc/usr-acl.texi
index 5b56618..61f307d 100644
--- a/doc/usr-acl.texi
+++ b/doc/usr-acl.texi
@@ -1,5 +1,5 @@
@c This is part of the GNU Pies manual.
-@c Copyright (C) 2009--2020 Sergey Poznyakoff
+@c Copyright (C) 2009--2021 Sergey Poznyakoff
@c This file is distributed under GFDL 1.3 or any later version
@c published by the Free Software Foundation.
diff --git a/doc/webdoc.init b/doc/webdoc.init
new file mode 100644
index 0000000..4f7b4fd
--- a/dev/null
+++ b/doc/webdoc.init
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+set_from_init_file('EXTRA_HEAD', qq{
+<link rel="stylesheet" type="text/css" href="css/info.css"/>
+<link rel="stylesheet" type="text/css" href="css/pies.css"/>
+<script src="js/modernizr.js" type="text/javascript"></script>
+<script src="js/info.js" type="text/javascript"></script>
+});
diff --git a/ident/Makefile.am b/ident/Makefile.am
index 4a8437d..ab43f5e 100644
--- a/ident/Makefile.am
+++ b/ident/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of GNU Pies.
-# Copyright (C) 2015-2020 Sergey Poznyakoff
+# Copyright (C) 2015-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/ident/ident.c b/ident/ident.c
index 86f5722..2192c48 100644
--- a/ident/ident.c
+++ b/ident/ident.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/ident/ident.h b/ident/ident.h
index 4a42387..f2ceb84 100644
--- a/ident/ident.h
+++ b/ident/ident.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/ident/identity.h b/ident/identity.h
index a4f85b9..6cbcd7f 100644
--- a/ident/identity.h
+++ b/ident/identity.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/ident/pam.c b/ident/pam.c
index 83c7adf..650f43e 100644
--- a/ident/pam.c
+++ b/ident/pam.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/ident/provider.c b/ident/provider.c
index 2227329..cd78482 100644
--- a/ident/provider.c
+++ b/ident/provider.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/ident/system.c b/ident/system.c
index 4d47ab1..4cab8a0 100644
--- a/ident/system.c
+++ b/ident/system.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f3d8222..832805b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of GNU Pies.
-# Copyright (C) 2005-2020 Sergey Poznyakoff
+# Copyright (C) 2005-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@ noinst_HEADERS = libpies.h grecsasrt.h
libpies_a_SOURCES=\
addrfmt.c\
arraymember.c\
+ closefds.c\
envop.c\
envop.h\
grecsasrt.c\
diff --git a/lib/addrfmt.c b/lib/addrfmt.c
index 2433c87..21c8661 100644
--- a/lib/addrfmt.c
+++ b/lib/addrfmt.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/arraymember.c b/lib/arraymember.c
index 6f15884..45ab70b 100644
--- a/lib/arraymember.c
+++ b/lib/arraymember.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/closefds.c b/lib/closefds.c
new file mode 100644
index 0000000..51da4dd
--- a/dev/null
+++ b/lib/closefds.c
@@ -0,0 +1,131 @@
+/* This file is part of GNU Pies
+ Copyright (C) 2015-2021 Sergey Poznyakoff
+
+ GNU 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.
+
+ GNU 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 GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "libpies.h"
+
+#if defined (HAVE_FUNC_CLOSEFROM)
+# include <unistd.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ closefrom (minfd);
+ return 0;
+}
+
+#elif defined (HAVE_FCNTL_CLOSEM)
+# include <fcntl.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ fcntl (minfd, F_CLOSEM, 0);
+ return 0;
+}
+
+#elif defined (HAVE_LIBPROC_H) && defined (HAVE_FUNC_PROC_PIDINFO)
+#include <libproc.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ pid_t pid = getpid ();
+ struct proc_fdinfo *fdinfo;
+ int i, n, size;
+
+ size = proc_pidinfo (pid, PROC_PIDLISTFDS, 0, NULL, 0);
+ if (size == 0)
+ return 0;
+ else if (size < 0)
+ return -1;
+
+ fdinfo = calloc (size, sizeof (fdinfo[0]));
+ if (!fdinfo)
+ return -1;
+
+ n = proc_pidinfo (pid, PROC_PIDLISTFDS, 0, fdinfo, size);
+ if (n <= 0)
+ {
+ free (fdinfo);
+ return -1;
+ }
+
+ n /= PROC_PIDLISTFD_SIZE;
+
+ for (i = 0; i < n; i++)
+ {
+ if (fdinfo_buf[i].proc_fd >= minfd)
+ close (fdinfo_buf[i].proc_fd);
+ }
+
+ free (fdinfo);
+ return 0;
+}
+
+#elif defined (HAVE_PROC_SELF_FD)
+# include <sys/types.h>
+# include <dirent.h>
+# include <limits.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ DIR *dir;
+ struct dirent *ent;
+
+ dir = opendir ("/proc/self/fd");
+ if (!dir)
+ return -1;
+ while ((ent = readdir (dir)) != NULL)
+ {
+ long n;
+ char *p;
+
+ if (ent->d_name[0] == '.')
+ continue;
+
+ n = strtol (ent->d_name, &p, 10);
+ if (n >= minfd && n < INT_MAX && *p == 0)
+ close ((int) n);
+ }
+ closedir (dir);
+ return 0;
+}
+
+#else
+# define close_fds_sys(fd) (-1)
+#endif
+
+static int
+close_fds_bruteforce (int minfd)
+{
+ int i, n = getmaxfd ();
+
+ for (i = minfd; i < n; i++)
+ close (i);
+
+ return 0;
+}
+
+void
+pies_close_fds (int minfd)
+{
+ if (close_fds_sys (minfd))
+ close_fds_bruteforce (minfd);
+}
diff --git a/lib/envop.c b/lib/envop.c
index 39d8a18..c685ee2 100644
--- a/lib/envop.c
+++ b/lib/envop.c
@@ -1,5 +1,5 @@
/* Environment modification functions for GNU Pies.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/envop.h b/lib/envop.h
index b061216..6587b1a 100644
--- a/lib/envop.h
+++ b/lib/envop.h
@@ -1,5 +1,5 @@
/* Environment and environment operation definitions for GNU Pies.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/grecsasrt.c b/lib/grecsasrt.c
index 7493d67..73982ed 100644
--- a/lib/grecsasrt.c
+++ b/lib/grecsasrt.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/grecsasrt.h b/lib/grecsasrt.h
index c167a3f..5486598 100644
--- a/lib/grecsasrt.h
+++ b/lib/grecsasrt.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/libpies.h b/lib/libpies.h
index 34d3569..b2b7e2b 100644
--- a/lib/libpies.h
+++ b/lib/libpies.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -30,6 +30,8 @@
#else
# define getmaxfd() 256
#endif
+
+void pies_close_fds (int minfd);
void mf_proctitle_init (int argc, char *argv[], char *env[]);
void mf_proctitle_format (const char *fmt, ...);
diff --git a/lib/mkfilename.c b/lib/mkfilename.c
index a9c2910..16726dc 100644
--- a/lib/mkfilename.c
+++ b/lib/mkfilename.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/netrc.c b/lib/netrc.c
index d72636a..99a8271 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/parsetime.c b/lib/parsetime.c
index e7c452f..5eb2e2a 100644
--- a/lib/parsetime.c
+++ b/lib/parsetime.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2007-2020 Sergey Poznyakoff
+ Copyright (C) 2007-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/pp.c b/lib/pp.c
index 3bd7c80..229d449 100644
--- a/lib/pp.c
+++ b/lib/pp.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/proctitle.c b/lib/proctitle.c
index c6524c3..60b127a 100644
--- a/lib/proctitle.c
+++ b/lib/proctitle.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2007-2020 Sergey Poznyakoff
+ Copyright (C) 2007-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/safe_strcmp.c b/lib/safe_strcmp.c
index 6bcd90d..db2f6f7 100644
--- a/lib/safe_strcmp.c
+++ b/lib/safe_strcmp.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/split3.c b/lib/split3.c
index 9360276..97c16f1 100644
--- a/lib/split3.c
+++ b/lib/split3.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2007-2020 Sergey Poznyakoff
+ Copyright (C) 2007-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/strtotok.c b/lib/strtotok.c
index 5b2a238..300d51f 100644
--- a/lib/strtotok.c
+++ b/lib/strtotok.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/url.c b/lib/url.c
index 05399f0..b321609 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/urlconn.c b/lib/urlconn.c
index 2b8a1e7..f0b65a7 100644
--- a/lib/urlconn.c
+++ b/lib/urlconn.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/lib/wildmatch.c b/lib/wildmatch.c
index a4bea32..db88248 100644
--- a/lib/wildmatch.c
+++ b/lib/wildmatch.c
@@ -1,5 +1,5 @@
/* Environment-specific globbing pattern matching for GNU Pies.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/po/POTFILES.in b/po/POTFILES.in
index fba1c8f..a873d1f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,6 +1,6 @@
# List of files in GNU Pies which contain translatable strings.
-# Copyright (C) 2007-2020 Sergey Poznyakoff
+# Copyright (C) 2007-2021 Sergey Poznyakoff
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/src/Makefile.am b/src/Makefile.am
index af73ada..0a5f5dc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of GNU Pies.
-# Copyright (C) 2008-2020 Sergey Poznyakoff
+# Copyright (C) 2008-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@ pies_SOURCES = \
pies.c\
progman.c\
socket.c\
+ syslog.c\
userprivs.c
if PIES_COND_SYSVINIT
@@ -44,6 +45,7 @@ noinst_HEADERS = \
cmdline.h\
meta1parse.h\
pies.h\
+ pies_syslog.h\
prog.h\
piesctl-cl.h\
telinit.h
diff --git a/src/acl.c b/src/acl.c
index 582aa50..b68d237 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/acl.h b/src/acl.h
index 93614c6..7e1f92b 100644
--- a/src/acl.h
+++ b/src/acl.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/cmdline.opt b/src/cmdline.opt
index b2b1a5f..75d47e0 100644
--- a/src/cmdline.opt
+++ b/src/cmdline.opt
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies. -*- c -*-
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@ OPTIONS_BEGIN("pies",
[<process invocation and execution supervisor>],
[<>],
[<gnu>],
- [<copyright_year=2008-2017>],
+ [<copyright_year=2008-2020>],
[<copyright_holder=Sergey Poznyakoff>])
GROUP(Operation Mode)
diff --git a/src/comp.c b/src/comp.c
index 28d4d6d..91663e4 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2016-2020 Sergey Poznyakoff
+ Copyright (C) 2016-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -154,7 +154,6 @@ component_create (const char *name)
{
comp = grecs_zalloc (sizeof (*comp));
comp->listidx = cur;
- comp->facility = log_facility;
comp->redir[RETR_OUT].type = comp->redir[RETR_ERR].type = redir_null;
comp->tag = grecs_strdup (name);
comp->socket_type = SOCK_STREAM;
@@ -293,7 +292,6 @@ component_match (struct component *comp, struct component *ref)
EQ (pass_fd_timeout);
FN (acl, pies_acl_cmp);
FN (tcpmux, safe_strcmp);
- EQ (facility);
FNP (redir[0], redirector_cmp);
FNP (redir[1], redirector_cmp);
#undef MATCH
@@ -403,44 +401,24 @@ list_str_cmp (const void *a, const void *b)
DP is a transitive closure of depmap.
*/
static void
-report_cyclic_dependency (pies_depmap_t dp, size_t idx)
+report_cyclic_dependency (struct depmap_path *path)
{
- size_t i;
-
- i = idx;
- do
+ struct depmap_path_elem *p;
+ for (p = path->head; p; p = p->next)
{
- size_t n;
- pies_depmap_pos_t pos;
-
- logmsg_printf (LOG_NOTICE, "%s -> ", comp_array[i]->tag);
- comp_array[i]->flags |= CF_REMOVE;
- for (n = depmap_first (depmap, depmap_col, i, &pos);
- n != (size_t)-1;
- n = depmap_next (depmap, pos))
- {
- if (n == i)
- continue;
- if (depmap_isset (dp, n, i) && depmap_isset (dp, n, n))
- break;
- }
- depmap_end (pos);
-
- if (n == (size_t)-1)
- break;
- i = n;
+ logmsg_printf (LOG_NOTICE, "%s -> ", comp_array[p->idx]->tag);
+ comp_array[p->idx]->flags |= CF_REMOVE;
}
- while (i != idx);
- logmsg_printf (LOG_NOTICE, "%s\n", comp_array[idx]->tag);
+ logmsg_printf (LOG_NOTICE, "%s\n", comp_array[path->head->idx]->tag);
}
void
component_build_depmap (void)
{
- size_t i;
- pies_depmap_t dp;
-
- free (depmap);
+ struct depmap_path *path;
+ int i;
+
+ depmap_free (depmap);
depmap = depmap_alloc (comp_count);
for (i = 0; i < comp_count; )
{
@@ -482,24 +460,22 @@ component_build_depmap (void)
i++;
}
- dp = depmap_copy (depmap);
- depmap_tc (dp);
- for (i = 0; i < comp_count; i++)
- if (!(comp_array[i]->flags & CF_REMOVE) && depmap_isset (dp, i, i))
- {
- logmsg (LOG_ERR, _("component %s depends on itself"),
- comp_array[i]->tag);
- report_cyclic_dependency (dp, i);
- }
-
-
- for (i = 0; i < comp_count;)
- if (comp_array[i]->flags & CF_REMOVE)
- comp_array_remove (i);
- else
- i++;
-
- free (dp);
+ path = depmap_cycle_detect (depmap);
+ if (path)
+ {
+ struct depmap_path *p;
+
+ logmsg (LOG_ERR, "%s", _("cyclic dependencies detected:"));
+ for (p = path; p; p = p->next)
+ report_cyclic_dependency (p);
+ depmap_path_free (path);
+
+ for (i = 0; i < comp_count;)
+ if (comp_array[i]->flags & CF_REMOVE)
+ comp_array_remove (i);
+ else
+ i++;
+ }
}
void
@@ -746,6 +722,14 @@ component_finish (struct component *comp, grecs_locus_t *locus)
if (comp->flags & CF_SHELL)
{
+ if (comp->flags & CF_EXPANDENV)
+ {
+ grecs_warning (locus, 0,
+ "%s",
+ _("flags \"shell\" and \"expandenv\" used together: "
+ "ignoring \"expandenv\""));
+ comp->flags &= ~CF_EXPANDENV;
+ }
comp->argc = 3;
comp->argv = grecs_calloc (comp->argc + 1, sizeof (comp->argv[0]));
comp->argv[0] = grecs_strdup (comp->program ? comp->program : "/bin/sh");
@@ -753,7 +737,7 @@ component_finish (struct component *comp, grecs_locus_t *locus)
comp->argv[2] = grecs_strdup (comp->command);
comp->argv[3] = NULL;
}
- else if (comp->command)
+ else if (comp->command && !(comp->flags & CF_EXPANDENV))
{
struct wordsplit ws;
if (wordsplit (comp->command, &ws, WRDSF_DEFFLAGS))
@@ -806,6 +790,12 @@ depmap_dump (pies_depmap_t dpm)
{
size_t i, j;
+ if (depmap_dim (dpm) == 0)
+ {
+ printf ("%s\n", _("No components defined"));
+ return;
+ }
+
printf ("%s:\n", _("Dependency map"));
printf (" ");
for (i = 0; i < comp_count; i++)
diff --git a/src/ctl.c b/src/ctl.c
index 615ffef..ac5fbb6 100644
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2007-2020 Sergey Poznyakoff
+ Copyright (C) 2007-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -479,13 +479,13 @@ json_object_set_bool (struct json_value *obj, char const *name, int val)
}
static struct json_value *
-json_error_reply_create (const char *msg)
+json_error_reply_create (int http_code, const char *msg)
{
struct json_value *val;
val = json_reply_create ();
- json_object_set_string (val, "status", "ER");
- json_object_set_string (val, "error_message", "%s", msg);
+ json_object_set_string (val, "status", http_code < 300 ? "OK" : "ER");
+ json_object_set_string (val, "message", "%s", msg);
return val;
}
@@ -607,11 +607,11 @@ ctlio_reply (struct ctlio *io, int code, const char *fmt, ...)
grecs_vasprintf (&str, &len, fmt, ap);
va_end (ap);
- io->output.reply = json_error_reply_create (str);
+ io->output.reply = json_error_reply_create (code, str);
grecs_free (str);
}
else
- io->output.reply = json_error_reply_create (http_text (code));
+ io->output.reply = json_error_reply_create (code, http_text (code));
}
static void
@@ -918,6 +918,8 @@ static void res_instance (struct ctlio *, enum http_method, char const *,
struct json_value *);
static void res_programs (struct ctlio *, enum http_method, char const *,
struct json_value *);
+static void res_alive (struct ctlio *, enum http_method, char const *,
+ struct json_value *);
static void res_conf (struct ctlio *, enum http_method, char const *,
struct json_value *);
@@ -946,6 +948,8 @@ static struct ctlio_resource restab[] = {
{ S(/conf), CTL_ADMIN_STATE, NULL, res_conf },
{ S(/programs), CTL_ADMIN_STATE|CTL_USER_STATE, NULL,
res_programs },
+ { S(/alive), CTL_ADMIN_STATE|CTL_USER_STATE, NULL,
+ res_alive },
#if PIES_SYSVINIT_ENABLED
{ S(/runlevel), CTL_ADMIN_STATE, pred_sysvinit, res_runlevel },
{ S(/environ), CTL_ADMIN_STATE, pred_sysvinit, res_environ },
@@ -1274,7 +1278,7 @@ ctl_accept (int socket, void *data)
io = ctlio_create ();
io->addr = addr;
io->addrlen = addrlen;
- register_socket (fd, NULL, ctlwr, NULL, io);
+ register_socket (fd, NULL, ctlwr, NULL, io, NULL);
return 0;
}
@@ -1301,7 +1305,7 @@ ctl_open (void)
return -1;
}
- register_socket (fd, ctl_accept, NULL, NULL, NULL);
+ register_socket (fd, ctl_accept, NULL, NULL, NULL, NULL);
return 0;
}
@@ -1429,9 +1433,6 @@ auth_prog (struct prog *prog, struct ctlio *io)
return CTL_ADMIN_STATE|CTL_USER_STATE;
switch (prog->type)
{
- case TYPE_REDIRECTOR:
- prog = prog->v.r.master;
- /* FALL THROUGH */
case TYPE_COMPONENT:
if (prog->v.p.comp->adm_acl
&& check_acl (prog->v.p.comp->adm_acl,
@@ -1450,7 +1451,6 @@ auth_prog (struct prog *prog, struct ctlio *io)
static char * const pies_type_str[] = {
[TYPE_COMPONENT] = "component",
- [TYPE_REDIRECTOR] = "redirector",
[TYPE_COMMAND] = "command"
};
@@ -1899,15 +1899,11 @@ prog_serialize (struct json_value *ret, struct prog *prog)
prog->v.p.timestamp + SLEEPTIME);
v = json_new_array ();
- for (i = 0; i < prog->v.p.comp->argc; i++)
- json_array_append (v, json_new_string (prog->v.p.comp->argv[i]));
+ for (i = 0; i < prog->v.p.argc; i++)
+ json_array_append (v, json_new_string (prog->v.p.argv[i]));
json_object_set (ret, "argv", v);
break;
- case TYPE_REDIRECTOR:
- json_object_set_number (ret, "PID", prog->pid);
- break;
-
case TYPE_COMMAND:
json_object_set_string (ret, "command", "%s", prog->v.c.command);
}
@@ -2121,6 +2117,57 @@ res_programs (struct ctlio *io, enum http_method meth,
else
res_programs_select (io, meth, uri, json);
}
+
+static void
+res_alive (struct ctlio *io, enum http_method meth, char const *uri,
+ struct json_value *json)
+{
+ struct prog *prog;
+
+ if (!uri)
+ {
+ ctlio_reply (io, 403, NULL);
+ return;
+ }
+
+ prog = progman_locate (uri + 1);
+
+ if (prog && IS_COMPONENT (prog))
+ {
+ switch (prog->v.p.status)
+ {
+ case status_running:
+ case status_listener:
+ /* FIXME: Presumably these three fall into that category too:
+ status_stopped -- Component is stopped administratively
+ status_stopping -- Component is being stopped
+ status_finished -- A "once" or "startup" component has finished
+ */
+ ctlio_reply (io, 200, NULL);
+ break;
+
+ case status_sleeping:
+ {
+ time_t t;
+ char buf[80];
+
+ t = time (NULL) - prog->v.p.timestamp;
+ if (t <= SLEEPTIME)
+ {
+ snprintf (buf, sizeof buf, "%lu", SLEEPTIME - t);
+ output_set_header (&io->output, "Retry-After", buf);
+ }
+ }
+ /* fall through */
+
+ default:
+ ctlio_reply (io, 503, NULL);
+ break;
+ }
+ }
+ else
+ ctlio_reply (io, 404, NULL);
+}
#if PIES_SYSVINIT_ENABLED
static int
diff --git a/src/depmap.c b/src/depmap.c
index fd5521f..2f1c90a 100644
--- a/src/depmap.c
+++ b/src/depmap.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,135 +16,179 @@
#include "pies.h"
-#ifndef CHAR_BIT
-# define CHAR_BIT 8
-#endif
-#define BITS_PER_WORD (sizeof(unsigned)*CHAR_BIT)
-
-#define WORDSIZE(n) (((n) + BITS_PER_WORD - 1) / BITS_PER_WORD)
-#define SETBIT(x, i) ((x)[(i)/BITS_PER_WORD] |= (1<<((i) % BITS_PER_WORD)))
-#define CLRBIT(x, i) ((x)[(i)/BITS_PER_WORD] &= ~(1<<((i) % BITS_PER_WORD)))
-#define BITISSET(x, i) (((x)[(i)/BITS_PER_WORD] & (1<<((i) % BITS_PER_WORD))) != 0)
-
-void
-TC (unsigned *R, int n)
-{
- register int rowsize;
- register unsigned mask;
- register unsigned *rowj;
- register unsigned *rp;
- register unsigned *rend;
- register unsigned *ccol;
-
- unsigned *relend;
- unsigned *cword;
- unsigned *rowi;
-
- rowsize = WORDSIZE (n) * sizeof (unsigned);
- relend = (unsigned *) ((char *) R + (n * rowsize));
-
- cword = R;
- mask = 1;
- rowi = R;
- while (rowi < relend)
- {
- ccol = cword;
- rowj = R;
-
- while (rowj < relend)
- {
- if (*ccol & mask)
- {
- rp = rowi;
- rend = (unsigned *) ((char *) rowj + rowsize);
-
- while (rowj < rend)
- *rowj++ |= *rp++;
- }
- else
- {
- rowj = (unsigned *) ((char *) rowj + rowsize);
- }
-
- ccol = (unsigned *) ((char *) ccol + rowsize);
- }
-
- mask <<= 1;
- if (mask == 0)
- {
- mask = 1;
- cword++;
- }
- rowi = (unsigned *) ((char *) rowi + rowsize);
- }
-}
+#define INFINITY INT_MAX
struct pies_depmap
{
- size_t nrows; /* Number of rows (== number of mapped elements) */
- size_t rowlen; /* Length of a row in words */
- unsigned r[1]; /* Data rows */
+ size_t dim; /* Number of vertices */
+ int **dist; /* Distance between two vertices */
+ int **next; /* Next vertex on the shortest path from i to j */
+ int closed; /* True if the transitive closure was computed */
+ int map[1]; /* Storage array for dist and next */
};
-pies_depmap_t
-depmap_alloc (size_t count)
+size_t
+depmap_dim (struct pies_depmap *dmap)
{
- size_t size = (count + BITS_PER_WORD - 1) / BITS_PER_WORD;
- pies_depmap_t dmap = grecs_zalloc (sizeof (*dmap) - 1
- + count * size * sizeof (unsigned));
- dmap->nrows = count;
- dmap->rowlen = size;
- return dmap;
+ return dmap->dim;
}
-/* Return size of a depmap row in bytes */
-static inline size_t
-depmap_row_size (pies_depmap_t dpm)
+pies_depmap_t
+depmap_alloc (size_t count)
{
- return dpm->rowlen * sizeof (unsigned);
-}
+ struct pies_depmap *dmap;
+ size_t size;
+ size_t i, j;
+ int *ip;
+
+ if (count < 0)
+ abort ();
+ /* size = sizeof (*dmap) + 2 * count * count * sizeof (int) */
+ if (count > 0)
+ {
+ if (SIZE_MAX / count < count)
+ grecs_alloc_die ();
+ size = count * count;
+ if (SIZE_MAX / 2 < size)
+ grecs_alloc_die ();
+ size += size;
+ if (SIZE_MAX / sizeof (int) < size)
+ grecs_alloc_die ();
+ size *= sizeof (int);
+ if (SIZE_MAX - size < sizeof (*dmap))
+ grecs_alloc_die ();
+ size += sizeof (*dmap);
+ }
+ else
+ size = sizeof (*dmap);
+
+ dmap = grecs_zalloc (size);
+ dmap->dim = count;
+ dmap->closed = 0;
+ dmap->dist = grecs_calloc (sizeof (dmap->dist[0]), count);
+ dmap->next = grecs_calloc (sizeof (dmap->next[0]), count);
+
+ ip = dmap->map;
+ for (i = 0; i < count; i++)
+ {
+ dmap->dist[i] = ip;
+ for (j = 0; j < count; j++)
+ *ip++ = INFINITY;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ dmap->next[i] = ip;
+ for (j = 0; j < count; j++)
+ *ip++ = -1;
+ }
+
+ return dmap;
+}
pies_depmap_t
-depmap_copy (pies_depmap_t dpm)
+depmap_copy (pies_depmap_t src)
{
- pies_depmap_t copy = depmap_alloc (dpm->nrows);
- memcpy (copy->r, dpm->r, dpm->nrows * depmap_row_size (dpm));
- return copy;
+ pies_depmap_t dst = depmap_alloc (src->dim);
+ memcpy (dst->map, src->map, 2 * src->dim * src->dim * sizeof (src->map[0]));
+ dst->closed = src->closed;
+ return dst;
}
-static unsigned *
-depmap_rowptr (pies_depmap_t dmap, size_t row)
+void
+depmap_free (pies_depmap_t dmap)
{
- return dmap->r + dmap->rowlen * row;
+ if (dmap)
+ {
+ free (dmap->dist);
+ free (dmap->next);
+ free (dmap);
+ }
}
void
-depmap_set (pies_depmap_t dmap, size_t row, size_t col)
+depmap_set (pies_depmap_t dmap, size_t i, size_t j)
{
- unsigned *rptr = depmap_rowptr (dmap, row);
- SETBIT (rptr, col);
+ dmap->dist[i][j] = 1;
+ dmap->closed = 0;
}
-void
-depmap_clear (pies_depmap_t dmap, size_t row, size_t col)
+int
+depmap_isset (pies_depmap_t dmap, size_t i, size_t j)
{
- unsigned *rptr = depmap_rowptr (dmap, row);
- CLRBIT (rptr, col);
+ return dmap->dist[i][j] != INFINITY;
}
-int
-depmap_isset (pies_depmap_t dmap, size_t row, size_t col)
+/* Remove nth row and column from the map */
+void
+depmap_remove (pies_depmap_t dmap, size_t n)
{
- unsigned *rptr = depmap_rowptr (dmap, row);
- return BITISSET (rptr, col);
+ if (n < dmap->dim - 1)
+ {
+ size_t i;
+
+ /* Remove nth row */
+ memmove (&dmap->dist[n], &dmap->dist[n + 1],
+ (dmap->dim - n - 1) * sizeof (dmap->dist[0]));
+ memmove (&dmap->next[n], &dmap->next[n + 1],
+ (dmap->dim - n - 1) * sizeof (dmap->next[0]));
+
+ /* Remove nth column */
+ for (i = 0; i < dmap->dim; i++)
+ {
+ memmove (&dmap->dist[i][n], &dmap->dist[i][n+1],
+ (dmap->dim - n - 1) * sizeof (dmap->dist[0][0]));
+ memmove (&dmap->next[i][n], &dmap->next[i][n + 1],
+ (dmap->dim - n - 1) * sizeof (dmap->next[0][0]));
+ }
+ }
+ dmap->dim--;
}
void
depmap_tc (pies_depmap_t dmap)
{
- TC (dmap->r, dmap->nrows);
-}
+ size_t i, j, k;
+
+ if (dmap->closed) return;
+ /*
+ * Initialize next vertex map and reset all distances between
+ * adjacent vertices to 1.
+ */
+ for (i = 0; i < dmap->dim; i++)
+ for (j = 0; j < dmap->dim; j++)
+ {
+ if (depmap_isset (dmap, i, j))
+ {
+ dmap->dist[i][j] = 1;
+ dmap->next[i][j] = j;
+ }
+ else
+ {
+ dmap->next[i][j] = -1;
+ }
+ }
+ /* Modified Floyd-Warshall algorithm */
+ for (k = 0; k < dmap->dim; k++)
+ for (i = 0; i < dmap->dim; i++)
+ for (j = 0; j < dmap->dim; j++)
+ {
+ if (depmap_isset (dmap, i, k) && depmap_isset (dmap, k, j))
+ {
+ int t = dmap->dist[i][k] + dmap->dist[k][j];
+ if (dmap->dist[i][j] > t)
+ {
+ dmap->dist[i][j] = t;
+ dmap->next[i][j] = dmap->next[i][k];
+ }
+ }
+ }
+
+ /* Mark completion */
+ dmap->closed = 1;
+}
+
struct pies_depmap_pos
{
enum pies_depmap_direction dir;
@@ -154,12 +198,12 @@ struct pies_depmap_pos
size_t
depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos)
{
- for (pos->coord[pos->dir]++; pos->coord[pos->dir] < dmap->nrows;
+ for (pos->coord[pos->dir]++; pos->coord[pos->dir] < dmap->dim;
pos->coord[pos->dir]++)
if (depmap_isset (dmap, pos->coord[0], pos->coord[1]))
break;
- return pos->coord[pos->dir] == dmap->nrows ?
+ return pos->coord[pos->dir] == dmap->dim ?
(size_t) -1 : pos->coord[pos->dir];
}
@@ -180,23 +224,157 @@ depmap_end (pies_depmap_pos_t pos)
{
grecs_free (pos);
}
+
+static struct depmap_path *
+depmap_path_alloc (void)
+{
+ struct depmap_path *path = grecs_malloc (sizeof *path);
+ path->len = 0;
+ path->head = path->tail = NULL;
+ path->next = NULL;
+ return path;
+}
-/* Remove nth row and column from the map */
void
-depmap_remove (pies_depmap_t dmap, size_t n)
+depmap_path_free (struct depmap_path *path)
{
- if (n < dmap->nrows - 1)
+ while (path)
{
- size_t i, j;
+ struct depmap_path *next_path = path->next;
+ struct depmap_path_elem *p = path->head;
+ while (p)
+ {
+ struct depmap_path_elem *next = p->next;
+ free (p);
+ p = next;
+ }
+ free (path);
+ path = next_path;
+ }
+}
- /* Remove nth row */
- memmove (depmap_rowptr (dmap, n), depmap_rowptr (dmap, n + 1),
- (dmap->nrows - n - 1) * depmap_row_size (dmap));
- /* Renumber all columns j >= n: j = j - 1 */
- for (i = 0; i < dmap->nrows; i++)
- for (j = n; j < dmap->nrows - 1; j++)
- (depmap_isset (dmap, i, j + 1) ? depmap_set : depmap_clear)
- (dmap, i, j);
+static void
+depmap_path_append (struct depmap_path *path, int idx)
+{
+ struct depmap_path_elem *p = grecs_malloc (sizeof (*p));
+ p->idx = idx;
+ p->next = NULL;
+ if (path->tail)
+ path->tail->next = p;
+ else
+ path->head = p;
+ path->tail = p;
+ path->len++;
+}
+
+/* Returns the path from vertex i to j. */
+static struct depmap_path *
+depmap_path (pies_depmap_t dmap, int i, int j)
+{
+ struct depmap_path *dp;
+ if (dmap->closed && depmap_isset (dmap, i, j))
+ {
+ dp = depmap_path_alloc ();
+ depmap_path_append (dp, i);
+ while (i != j)
+ {
+ i = dmap->next[i][j];
+ depmap_path_append (dp, i);
+ }
+ }
+ else
+ dp = NULL;
+ return dp;
+}
+
+/*
+ * If the vertex i is a part of a cyclic path, return the path adjusted
+ * so that it starts at the vertex with the lowest number among the vertices
+ * traversed by that path.
+ */
+static struct depmap_path *
+depmap_normalized_cyclic_path (pies_depmap_t dmap, int i)
+{
+ struct depmap_path *dp;
+ if (dmap->closed && depmap_isset (dmap, i, i))
+ {
+ struct depmap_path_elem *start, *p;
+
+ dp = depmap_path (dmap, dmap->next[i][i], i);
+ start = dp->head;
+ for (p = start->next; p; p = p->next)
+ {
+ if (p->idx < start->idx)
+ start = p;
+ }
+ if (start != dp->head)
+ {
+ dp->tail->next = dp->head;
+ for (p = dp->head; p->next != start; p = p->next)
+ ;
+ dp->tail = p;
+ dp->tail->next = NULL;
+ dp->head = start;
+ }
+ }
+ else
+ dp = NULL;
+ return dp;
+}
+
+/* Return true (1) if normalized paths A and B are equal. */
+static int
+depmap_path_eq (struct depmap_path *a, struct depmap_path *b)
+{
+ struct depmap_path_elem *ap, *bp;
+ ap = a->head;
+ bp = b->head;
+ while (ap)
+ {
+ if (bp == NULL || ap->idx != bp->idx)
+ return 0;
+ ap = ap->next;
+ bp = bp->next;
+ }
+ return bp == NULL;
+}
+
+static int
+depmap_path_find (struct depmap_path *a, struct depmap_path *b)
+{
+ while (a)
+ {
+ if (depmap_path_eq (a, b))
+ return 1;
+ a = a->next;
+ }
+ return 0;
+}
+
+struct depmap_path *
+depmap_cycle_detect (pies_depmap_t dmap)
+{
+ pies_depmap_t dp = depmap_copy (dmap);
+ size_t i;
+ struct depmap_path *head = NULL, *tail = NULL;
+
+ depmap_tc (dp);
+ for (i = 0; i < dmap->dim; i++)
+ {
+ if (depmap_isset (dp, i, i))
+ {
+ struct depmap_path *path = depmap_normalized_cyclic_path (dp, i);
+ if (!head)
+ head = tail = path;
+ else if (depmap_path_find (head, path))
+ depmap_path_free (path);
+ else
+ {
+ tail->next = path;
+ tail = path;
+ }
+ }
}
- dmap->nrows--;
+ depmap_free (dp);
+ return head;
}
diff --git a/src/diag.c b/src/diag.c
index 24000e2..d8a1864 100644
--- a/src/diag.c
+++ b/src/diag.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,13 +26,7 @@ typedef struct
static int
syslog_printer (LOGSTREAM *str, 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);
- syslog (prio, "%s", buf);
-#endif
+ pies_vsyslog (prio, fmt, ap);
return 0;
}
@@ -41,7 +35,7 @@ syslog_open (int logf, LOGSTREAM *str)
{
if (logf & DIAG_REOPEN_LOG)
{
- openlog (log_tag, LOG_PID, log_facility);
+ pies_syslog_open ();
str->open = 1;
}
else
@@ -53,7 +47,7 @@ static void
syslog_close (LOGSTREAM *str)
{
if (str->open)
- closelog ();
+ pies_syslog_close ();
}
static int
@@ -151,7 +145,7 @@ diag_setup (int flags)
if (flags)
diag_output = flags;
if (diag_output & DIAG_TO_SYSLOG)
- openlog (log_tag, LOG_PID, log_facility);
+ pies_syslog_open ();
}
void
diff --git a/src/inetd-bi.c b/src/inetd-bi.c
index 1925157..cbec7c2 100644
--- a/src/inetd-bi.c
+++ b/src/inetd-bi.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/inetd.c b/src/inetd.c
index 55bc2f1..1d12847 100644
--- a/src/inetd.c
+++ b/src/inetd.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2009-2020 Sergey Poznyakoff
+ Copyright (C) 2009-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -96,7 +96,7 @@ inetd_conf_file (const char *file)
}
wsflags = WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_WS | WRDSF_SQUEEZE_DELIMS;
- while (getline (&buf, &size, fp) >= 0)
+ while (grecs_getline (&buf, &size, fp) >= 0)
{
char *p;
struct component *comp;
diff --git a/src/inetd.in b/src/inetd.in
index f2be09d..7b07ca0 100644
--- a/src/inetd.in
+++ b/src/inetd.in
@@ -1,6 +1,6 @@
#! /bin/sh
# This file is part of GNU Pies.
-# Copyright (C) 2008-2020 Sergey Poznyakoff
+# Copyright (C) 2008-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/src/limits.c b/src/limits.c
index c4796e7..baec791 100644
--- a/src/limits.c
+++ b/src/limits.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/meta1parse.c b/src/meta1parse.c
index 8fe0936..bec271e 100644
--- a/src/meta1parse.c
+++ b/src/meta1parse.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2016-2020 Sergey Poznyakoff
+ Copyright (C) 2016-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/pies.c b/src/pies.c
index 0e84979..cec82ec 100644
--- a/src/pies.c
+++ b/src/pies.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,8 +23,6 @@
int preprocess_only; /* Preprocess config, do nothing more */
int lint_mode; /* Test configuration syntax and exit */
int log_to_stderr_only; /* Use only stderr for logging */
-int log_facility = LOG_USER;
-char *log_tag;
struct pies_privs pies_privs;
int foreground;
int init_process;
@@ -62,6 +60,8 @@ char *default_control_url[2] = {
DEFAULT_PIES_CONTROL_URL,
DEFAULT_INIT_CONTROL_URL
};
+
+static envop_t *pies_envop;
struct config_syntax
{
@@ -707,19 +707,19 @@ cb_env_section_parser (enum grecs_callback_command cmd,
{
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
- struct component *comp = varptr;
+ envop_t **envop_ptr = varptr;
switch (cmd)
{
case grecs_callback_section_begin:
- *(struct component **) cb_data = comp;
+ *(envop_t ***) cb_data = envop_ptr;
break;
case grecs_callback_section_end:
break;
case grecs_callback_set_value:
- return _cb_env (&comp->envop, value, locus);
+ return _cb_env (envop_ptr, value, locus);
}
return 0;
}
@@ -731,7 +731,7 @@ _cb_env_clear (enum grecs_callback_command cmd,
{
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
- struct component *comp = varptr;
+ envop_t **envop_ptr = varptr;
if (!GRECS_VALUE_EMPTY_P (value))
{
@@ -739,7 +739,7 @@ _cb_env_clear (enum grecs_callback_command cmd,
return 1;
}
- if (envop_entry_add (&comp->envop, envop_clear, NULL, NULL))
+ if (envop_entry_add (envop_ptr, envop_clear, NULL, NULL))
grecs_error (locus, errno, "envop_entry_add");
return 0;
@@ -752,7 +752,7 @@ _cb_env_keep (enum grecs_callback_command cmd,
{
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
- struct component *comp = varptr;
+ envop_t **envop_ptr = varptr;
char *p;
if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
@@ -760,9 +760,9 @@ _cb_env_keep (enum grecs_callback_command cmd,
p = strchr (value->v.string, '=');
if (p)
*p++ = 0;
- if (envop_entry_add (&comp->envop, envop_clear, NULL, NULL))
+ if (envop_entry_add (envop_ptr, envop_clear, NULL, NULL))
grecs_error (locus, errno, "envop_entry_add");
- if (envop_entry_add (&comp->envop, envop_keep, value->v.string, p))
+ if (envop_entry_add (envop_ptr, envop_keep, value->v.string, p))
grecs_error (locus, errno, "envop_entry_add");
return 0;
}
@@ -774,7 +774,7 @@ _cb_env_set (enum grecs_callback_command cmd,
{
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
- struct component *comp = varptr;
+ envop_t **envop_ptr = varptr;
char *p;
if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
@@ -782,7 +782,7 @@ _cb_env_set (enum grecs_callback_command cmd,
p = strchr (value->v.string, '=');
if (p)
*p++ = 0;
- if (envop_entry_add (&comp->envop, envop_set, value->v.string, p))
+ if (envop_entry_add (envop_ptr, envop_set, value->v.string, p))
grecs_error (locus, errno, "envop_entry_add");
return 0;
}
@@ -794,11 +794,11 @@ _cb_env_eval (enum grecs_callback_command cmd,
{
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
- struct component *comp = varptr;
+ envop_t **envop_ptr = varptr;
if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
return 1;
- if (envop_entry_add (&comp->envop, envop_set, NULL, value->v.string))
+ if (envop_entry_add (envop_ptr, envop_set, NULL, value->v.string))
grecs_error (locus, errno, "envop_entry_add");
return 0;
}
@@ -810,7 +810,7 @@ _cb_env_unset (enum grecs_callback_command cmd,
{
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
- struct component *comp = varptr;
+ envop_t **envop_ptr = varptr;
char *p;
if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
@@ -818,7 +818,7 @@ _cb_env_unset (enum grecs_callback_command cmd,
p = strchr (value->v.string, '=');
if (p)
*p++ = 0;
- if (envop_entry_add (&comp->envop, envop_unset, value->v.string, p))
+ if (envop_entry_add (envop_ptr, envop_unset, value->v.string, p))
grecs_error (locus, errno, "envop_entry_add");
return 0;
}
@@ -878,7 +878,7 @@ string_to_syslog_priority (const char *key, int *pres)
}
int
-string_to_syslog_facility (const char *key, int *pres)
+string_to_syslog_facility (const char *key, int len, int *pres)
{
static struct tokendef tokdef_fac[] = {
{"auth", LOG_AUTH},
@@ -908,7 +908,52 @@ string_to_syslog_facility (const char *key, int *pres)
{NULL}
};
- return strtotok_ci (tokdef_fac, key, pres);
+ return len == 0
+ ? strtotok_ci (tokdef_fac, key, pres)
+ : strtotok_len_ci (tokdef_fac, key, len, pres);
+}
+
+static int
+string_to_syslog_fp (const char *str, grecs_locus_t *locus, int *pres)
+{
+ size_t len = strcspn (str, ".");
+ int f = pies_log_facility, p = LOG_ERR;
+
+ if (string_to_syslog_facility (str, len, &f) == 0)
+ {
+ if (str[len])
+ {
+ if (string_to_syslog_priority (str + len + 1, &p))
+ {
+ grecs_error (locus, 0, "%s", _("unknown syslog priority"));
+ return 1;
+ }
+ }
+ }
+ else if (str[len])
+ {
+ grecs_error (locus, 0, "%s", _("unknown syslog facility"));
+ return 1;
+ }
+ else if (string_to_syslog_priority (str, &p))
+ {
+ grecs_error (locus, 0, "%s", _("unknown syslog priority"));
+ return 1;
+ }
+ *pres = f | p;
+ return 0;
+}
+
+static int
+cb_syslog_dev (enum grecs_callback_command cmd,
+ grecs_node_t *node,
+ void *varptr, void *cb_data)
+{
+ grecs_value_t *value = node->v.value;
+
+ if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
+ return 1;
+ return pies_syslog_set_dev (value->v.string);
}
static int
@@ -933,7 +978,7 @@ cb_syslog_facility (enum grecs_callback_command cmd,
else
*(int *) varptr = n;
}
- else if (string_to_syslog_facility (str, varptr))
+ else if (string_to_syslog_facility (str, 0, varptr))
grecs_error (locus, 0, _("unknown syslog facility %s"), str);
return 0;
}
@@ -963,12 +1008,8 @@ _cb_redir (enum grecs_callback_command cmd,
break;
}
rp->type = redir_syslog;
- if (string_to_syslog_priority (value->v.string, &rp->v.prio))
- {
- grecs_error (locus, 0, _("unknown syslog priority %s"),
- value->v.string);
- return 1;
- }
+ if (string_to_syslog_fp (value->v.string, locus, &rp->v.prio))
+ return 1;
break;
case GRECS_TYPE_ARRAY:
@@ -997,14 +1038,9 @@ _cb_redir (enum grecs_callback_command cmd,
break;
case redir_syslog:
- if (string_to_syslog_priority (value->v.arg.v[1]->v.string,
- &rp->v.prio))
- {
- grecs_error (locus, 0,
- _("unknown syslog priority %s"),
- value->v.arg.v[1]->v.string);
- return 1;
- }
+ if (string_to_syslog_fp (value->v.arg.v[1]->v.string,
+ locus, &rp->v.prio))
+ return 1;
break;
case redir_file:
@@ -1143,6 +1179,7 @@ str_to_cf (const char *string, int *flags)
{ "siggroup", CF_SIGGROUP },
{ "nullinput", CF_NULLINPUT },
{ "shell", CF_SHELL },
+ { "expandenv", CF_EXPANDENV },
{ NULL }
};
@@ -1344,13 +1381,6 @@ struct grecs_keyword component_keywords[] = {
NULL, offsetof (struct component, rmfile),
NULL,
},
- {"facility",
- N_("arg"),
- N_("Override default syslog facility for this component."),
- grecs_type_string, GRECS_DFLT,
- NULL, offsetof (struct component, facility),
- cb_syslog_facility,
- },
{"stdout",
/* TRANSLATORS: file and syslog are keywords. Do not translate them. */
N_("type: {file | syslog}> <channel: string"),
@@ -1408,7 +1438,7 @@ struct grecs_keyword component_keywords[] = {
NULL,
N_("Modify program environment."),
grecs_type_section, GRECS_DFLT,
- NULL, 0,
+ NULL, offsetof (struct component, envop),
cb_env_section_parser, NULL, cb_env_keywords
},
{"env",
@@ -1416,7 +1446,7 @@ struct grecs_keyword component_keywords[] = {
N_("Modify program environment (legacy syntax).\n"
"Argument is a list of quoted assignments separated by white space."),
grecs_type_string, GRECS_DFLT,
- NULL, 0,
+ NULL, offsetof (struct component, envop),
cb_env_section_parser, NULL, NULL
},
{"chdir",
@@ -1536,16 +1566,22 @@ struct grecs_keyword control_keywords[] = {
/* syslog */
static struct grecs_keyword syslog_kw[] = {
+ {"dev",
+ N_("name"),
+ N_("Syslog device: either absolute pathname of the socket file, "
+ "or an IPv4 address and optional port, separated with a colon"),
+ grecs_type_string, GRECS_CONST,
+ NULL, 0, cb_syslog_dev },
{"facility",
N_("name"),
N_("Set syslog facility. Arg is one of the following: user, daemon, "
"auth, authpriv, mail, cron, local0 through local7 (case-insensitive), "
"or a facility number."),
grecs_type_string, GRECS_DFLT,
- &log_facility, 0, cb_syslog_facility},
+ &pies_log_facility, 0, cb_syslog_facility},
{"tag", N_("string"), N_("Tag syslog messages with this string"),
grecs_type_string, GRECS_DFLT,
- &log_tag},
+ &pies_log_tag},
#if 0
/* This is reserved for future use */
{
@@ -1589,6 +1625,13 @@ struct grecs_keyword pies_keywords[] = {
grecs_type_section, GRECS_DFLT,
NULL, 0,
component_section_parser, NULL, component_keywords},
+ {"env",
+ NULL,
+ N_("Modify program environment."),
+ grecs_type_section, GRECS_DFLT,
+ &pies_envop, 0,
+ cb_env_section_parser, NULL, cb_env_keywords
+ },
{"control",
NULL,
N_("Define control socket"),
@@ -2332,7 +2375,6 @@ main (int argc, char **argv)
extern char **environ;
struct grecs_list_entry *ep;
int diag_flags;
- int i;
set_program_name (argv[0]);
#ifdef ENABLE_NLS
@@ -2354,7 +2396,7 @@ main (int argc, char **argv)
/* Set default logging */
if (SYSVINIT_ACTIVE)
{
- log_facility = LOG_DAEMON;
+ pies_log_facility = LOG_DAEMON;
diag_flags = DIAG_TO_STDERR | DIAG_REOPEN_LOG;
}
else
@@ -2383,7 +2425,7 @@ main (int argc, char **argv)
instance++;
}
setenv ("PIES_INSTANCE", instance, 1);
- log_tag = grecs_strdup (instance);
+ pies_log_tag = grecs_strdup (instance);
set_conf_file_names (instance);
@@ -2412,6 +2454,32 @@ main (int argc, char **argv)
set_state_file_names (instance);
set_mailer_argcv ();
+ if (pies_envop)
+ {
+ environ_t *env;
+
+ if ((env = environ_create (environ)) == NULL)
+ {
+ logmsg (LOG_CRIT, "environ_create: %s", strerror (errno));
+ exit (EX_OSERR);
+ }
+ if (envop_exec (pies_envop, env))
+ {
+ logmsg (LOG_CRIT, "environ_exec: %s", strerror (errno));
+ exit (EX_OSERR);
+ }
+ environ = environ_ptr (env);
+
+ if (debug_level >= 4)
+ {
+ int i;
+ logmsg_printf (LOG_DEBUG, "environment: ");
+ for (i = 0; environ[i]; i++)
+ logmsg_printf (LOG_DEBUG, "%s ", environ[i]);
+ logmsg_printf (LOG_DEBUG, "\n");
+ }
+ }
+
if (lint_mode)
exit (0);
@@ -2606,14 +2674,8 @@ main (int argc, char **argv)
if (action == ACTION_RESTART)
{
- int minfd = DIAG_OUTPUT (DIAG_TO_STDERR) ? 2 : 0;
- int i;
-
- for (i = getmaxfd (); i > minfd; i--)
- close (i);
-
+ pies_close_fds (3);
signal_setup (SIG_DFL);
-
execv (pies_master_argv[0], pies_master_argv);
}
diff --git a/src/pies.h b/src/pies.h
index 1a03702..838296b 100644
--- a/src/pies.h
+++ b/src/pies.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,6 +58,7 @@
#include "libpies.h"
#include "envop.h"
#include "grecs/json.h"
+#include "pies_syslog.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
@@ -209,6 +210,8 @@ enum pies_comp_mode
#define CF_SIGGROUP 0x100 /* Send signals to the process group */
#define CF_NULLINPUT 0x200 /* Provide null input stream */
#define CF_SHELL 0x400 /* Invoke via sh -c */
+#define CF_EXPANDENV 0x800 /* Expand environment variables in the command
+ line */
#define CF_REMOVE 0xf000 /* Marked for removal */
@@ -267,7 +270,6 @@ struct component
char *max_ip_connections_message;
/* Redirectors: */
- int facility; /* Syslog facility. */
struct redirector redir[2]; /* Repeaters for stdout and stderr */
/* Actions to execute on various exit codes: */
struct grecs_list *act_list;
@@ -294,8 +296,6 @@ enum pies_action {
};
extern char *instance;
-extern char *log_tag;
-extern int log_facility;
extern unsigned long shutdown_timeout;
extern struct component default_component;
extern pies_acl_t pies_acl;
@@ -392,6 +392,8 @@ enum pies_depmap_direction
pies_depmap_t depmap_alloc (size_t count);
pies_depmap_t depmap_copy (pies_depmap_t dpm);
+size_t depmap_dim (struct pies_depmap *dmap);
+void depmap_free (pies_depmap_t dmap);
void depmap_set (pies_depmap_t dmap, size_t row, size_t col);
int depmap_isset (pies_depmap_t dmap, size_t row, size_t col);
void depmap_clear (pies_depmap_t dmap, size_t row, size_t col);
@@ -403,6 +405,23 @@ size_t depmap_first (pies_depmap_t dmap, enum pies_depmap_direction dir,
size_t depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos);
void depmap_end (pies_depmap_pos_t pos);
+
+struct depmap_path_elem
+{
+ int idx;
+ struct depmap_path_elem *next;
+};
+
+struct depmap_path
+{
+ size_t len;
+ struct depmap_path_elem *head, *tail;
+ struct depmap_path *next;
+};
+
+void depmap_path_free (struct depmap_path *path);
+struct depmap_path *depmap_cycle_detect (pies_depmap_t dmap);
+
int assert_grecs_value_type (grecs_locus_t *locus,
const grecs_value_t *value, int type);
@@ -452,11 +471,13 @@ void *register_socket (int fd,
socket_handler_t rd,
socket_handler_t wr,
socket_handler_t ex,
- void *data);
+ void *data,
+ void (*free_data)(void*));
void deregister_socket (int fd);
void update_socket (int fd, int evt, socket_handler_t f);
-int register_program_socket (int socktype, int fd, void *data);
+int register_program_socket (int socktype, int fd, void *data,
+ void (*free_data)(void*));
int pass_fd (const char *socket, int fd, unsigned time_out);
int create_socket (struct pies_url *url, int socket_type,
const char *user, mode_t umask);
@@ -501,6 +522,7 @@ void diag_setup (int flags);
void diagmsg (int logf, int prio, const char *fmt, ...)
PIES_PRINTFLIKE(3,4);
+void vlogmsg (int prio, const char *fmt, va_list ap);
void logmsg (int prio, const char *fmt, ...) PIES_PRINTFLIKE(2,3);
void logmsg_printf (int prio, const char *fmt, ...) PIES_PRINTFLIKE(2,3);
void logmsg_vprintf (int prio, const char *fmt, va_list ap);
diff --git a/src/pies_syslog.h b/src/pies_syslog.h
new file mode 100644
index 0000000..fecb487
--- a/dev/null
+++ b/src/pies_syslog.h
@@ -0,0 +1,37 @@
+/* This file is part of GNU Pies.
+ Copyright (C) 2007-2021 Sergey Poznyakoff
+
+ GNU 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.
+
+ GNU 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 GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#define PIES_LOG_BUF_SIZE 1024
+#define PIES_LOG_DEV "/dev/log"
+#define PIES_LOG_MAX_QUEUE 64
+
+int pies_syslog_set_dev (char const *dev);
+int pies_syslog_open (void);
+void pies_syslog_close (void);
+void pies_syslog (int pri, char const *fmt, ...);
+void pies_vsyslog (int pri, char const *fmt, va_list ap);
+void pies_syslog_flush (void);
+void pies_syslog_message (int prio, char const *text, char const *tag, pid_t pid);
+
+extern char *pies_fallback_file;
+extern char *pies_log_tag;
+extern int pies_log_facility;
+extern size_t pies_log_max_queue;
+
+
+
+
+
diff --git a/src/piesctl-cl.opt b/src/piesctl-cl.opt
index 498ef22..ea71d59 100644
--- a/src/piesctl-cl.opt
+++ b/src/piesctl-cl.opt
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies. -*- c -*-
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@ OPTIONS_BEGIN("piesctl",
[<GNU pies control program>],
[<COMMAND [ARG...]>],
[<gnu>],
- [<copyright_year=2008-2017>],
+ [<copyright_year=2008-2020>],
[<copyright_holder=Sergey Poznyakoff>])
OPTION(instance,i,NAME,
diff --git a/src/piesctl.c b/src/piesctl.c
index d790ab2..44b3d0f 100644
--- a/src/piesctl.c
+++ b/src/piesctl.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2015-2020 Sergey Poznyakoff
+ Copyright (C) 2015-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -744,7 +744,7 @@ shttp_print_error (struct shttp_connection *conn)
{
struct json_value *jv;
- if (conn->result && (jv = json_value_lookup (conn->result, "error_message")))
+ if (conn->result && (jv = json_value_lookup (conn->result, "message")))
{
if (jv->type == json_string)
grecs_error (NULL, 0, "%s", jv->v.s);
@@ -1075,10 +1075,6 @@ print_comp (FILE *fp, struct json_value *v, size_t n)
else
fbuf[fidx++] = '-';
}
- else if (strcmp (type, "redirector") == 0)
- {
- fbuf[fidx++] = 'R';
- }
else if (strcmp (type, "command") == 0)
{
fbuf[fidx++] = 'E';
@@ -1125,12 +1121,6 @@ print_comp (FILE *fp, struct json_value *v, size_t n)
}
}
}
- else if (strcmp (type, "redirector") == 0)
- {
- p = getval (v, "PID", json_number, 0);
- if (p)
- fprintf (fp, "%10.0f ", p->v.n);
- }
else if (strcmp (type, "command") == 0)
{
p = getval (v, "command", json_string, 0);
@@ -1487,7 +1477,7 @@ shttp_print_response_status (struct shttp_connection *conn)
{
if (strcmp (v->v.s, "OK") == 0)
fputs (v->v.s, stdout);
- else if (json_object_get_type (elt, "error_message",
+ else if (json_object_get_type (elt, "message",
json_string, &v) == 0)
fputs (v->v.s, stdout);
else
@@ -1766,7 +1756,7 @@ telinit_format_environ (struct shttp_connection *conn)
}
else if (strcmp (val->v.s, "ER") == 0)
{
- if (json_object_get_type (conn->result, "error_message",
+ if (json_object_get_type (conn->result, "message",
json_string, &val) == 0)
fputs (val->v.s, stderr);
else
@@ -1967,7 +1957,7 @@ conf_reload (struct cmdline_parser_state *state)
val = json_object_require_type (conn->result, "status", json_string);
if (strcmp (val->v.s, "ER") == 0)
{
- if (json_object_get_type (conn->result, "error_message",
+ if (json_object_get_type (conn->result, "message",
json_string, &val) == 0)
fputs (val->v.s, stdout);
else
@@ -2042,7 +2032,7 @@ conf_file_clear (struct cmdline_parser_state *state)
val = json_object_require_type (conn->result, "status", json_string);
if (strcmp (val->v.s, "OK"))
{
- if (json_object_get_type (conn->result, "error_message",
+ if (json_object_get_type (conn->result, "message",
json_string, &val) == 0)
fputs (val->v.s, stderr);
else
@@ -2094,7 +2084,7 @@ conf_file_del (struct cmdline_parser_state *state)
val = json_object_require_type (conn->result, "status", json_string);
if (strcmp (val->v.s, "OK") == 0)
{
- if (json_object_get_type (conn->result, "error_message",
+ if (json_object_get_type (conn->result, "message",
json_string, &val) == 0)
{
fputs (val->v.s, stderr);
@@ -2119,7 +2109,7 @@ conf_file_del (struct cmdline_parser_state *state)
}
else
{
- if (json_object_get_type (conn->result, "error_message",
+ if (json_object_get_type (conn->result, "message",
json_string, &val) == 0)
fputs (val->v.s, stderr);
else
diff --git a/src/pp-setup b/src/pp-setup
index 102d52d..1a28cd8 100644
--- a/src/pp-setup
+++ b/src/pp-setup
@@ -1,6 +1,6 @@
divert(-1) dnl -*- m4 -*-
# This file is part of GNU Pies.
-# Copyright (C) 2007-2020 Sergey Poznyakoff
+# Copyright (C) 2007-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/src/prog.h b/src/prog.h
index aac5b17..f1b3427 100644
--- a/src/prog.h
+++ b/src/prog.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
enum prog_type
{
TYPE_COMPONENT,
- TYPE_REDIRECTOR,
TYPE_COMMAND
};
@@ -53,8 +52,10 @@ struct prog
struct
{
struct component *comp;
+ size_t argc;
+ char **argv; /* Actual command line (NULL-terminated) */
int socket;
- struct prog *redir[2]; /* Pointers to redirectors */
+ int redir[2]; /* FDs of redirectors */
time_t timestamp; /* Time of last startup */
size_t failcount; /* Number of failed starts since timestamp */
enum prog_status status; /* Current component status */
@@ -71,13 +72,6 @@ struct prog
struct
{
char *tag;
- struct component *comp;
- struct prog *master;
- } r;
-
- struct
- {
- char *tag;
char *command;
} c;
} v;
diff --git a/src/progman.c b/src/progman.c
index 2a073af..ae79de0 100644
--- a/src/progman.c
+++ b/src/progman.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2007-2020 Sergey Poznyakoff
+ Copyright (C) 2007-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -94,9 +94,6 @@ prog_tag (struct prog const *prog)
case TYPE_COMPONENT:
return prog->v.p.comp->tag;
- case TYPE_REDIRECTOR:
- return prog->v.r.tag;
-
case TYPE_COMMAND:
return prog->v.c.tag;
}
@@ -147,6 +144,21 @@ unlink_prog (struct prog *pp)
progtail = pp->prev;
}
+static inline void
+prog_stop_redirectors (struct prog *p)
+{
+ if (p->v.p.redir[RETR_OUT] != -1)
+ {
+ deregister_socket (p->v.p.redir[RETR_OUT]);
+ p->v.p.redir[RETR_OUT] = -1;
+ }
+ if (p->v.p.redir[RETR_ERR] != -1)
+ {
+ deregister_socket (p->v.p.redir[RETR_ERR]);
+ p->v.p.redir[RETR_ERR] = -1;
+ }
+}
+
void
destroy_prog (struct prog **pp)
{
@@ -156,31 +168,14 @@ destroy_prog (struct prog **pp)
switch (p->type)
{
case TYPE_COMPONENT:
+ environ_free (p->v.p.env);
+ if (p->v.p.comp->flags & CF_EXPANDENV)
+ argv_free (p->v.p.argv);
component_ref_decr (p->v.p.comp);
if (p->v.p.status == status_listener && p->v.p.socket != -1)
deregister_socket (p->v.p.socket);
/* FIXME: Remove also all dependent progs (esp. tcpmux) */
- if (p->v.p.redir[RETR_OUT])
- p->v.p.redir[RETR_OUT]->v.r.master = NULL;
- if (p->v.p.redir[RETR_ERR])
- p->v.p.redir[RETR_ERR]->v.r.master = NULL;
- break;
-
- case TYPE_REDIRECTOR:
- {
- struct prog *master = p->v.r.master;
- component_ref_decr (p->v.r.comp);
- if (master)
- {
- if (p == master->v.p.redir[0])
- master->v.p.redir[0] = NULL;
- else if (p == master->v.p.redir[1])
- master->v.p.redir[1] = NULL;
- }
- /* else
- logmsg (LOG_NOTICE, _("orphan redirector: %s"), p->tag);*/
- free (p->v.r.tag);
- }
+ prog_stop_redirectors (p);
break;
case TYPE_COMMAND:
@@ -191,50 +186,6 @@ destroy_prog (struct prog **pp)
*pp = NULL;
}
-static char *
-redir_tag (struct prog *master, int type)
-{
- static char *redirstr[2] = { "stdout", "stderr" };
- char *str = NULL;
- size_t len = 0;
- char const *tag = prog_tag (master);
- if (type < ARRAY_SIZE(redirstr))
- grecs_asprintf (&str, &len, "%s/%s", tag, redirstr[type]);
- else
- grecs_asprintf (&str, &len, "%s/%d", tag, type);
- return str;
-}
-
-static struct prog *
-register_redir (int type, struct prog *master)
-{
- char *tag = redir_tag (master, type);
- struct prog *pp = grecs_zalloc (sizeof (*pp));
-
- pp->type = TYPE_REDIRECTOR;
- pp->v.r.tag = tag;
- pp->v.r.master = master;
- pp->v.r.comp = master->v.p.comp;
- component_ref_incr (pp->v.r.comp);
- link_prog (pp, NULL);
- return pp;
-}
-
-void
-update_redir (int type, struct prog *master, pid_t pid)
-{
- struct prog *pp;
- if (master->v.p.redir[type])
- {
- pp = master->v.p.redir[type];
- prog_stop (pp, SIGKILL); /* Just in case */
- }
-
- pp = register_redir (type, master);
- master->v.p.redir[type] = pp;
- pp->pid = pid;
-}
-
/* Given the component COMP find the element of the program list
after which to link in the new prog associated with that COMP.
The progs in the resulting list must be arranged exactly in the
@@ -254,7 +205,7 @@ find_prog_ref (struct component *comp)
{
comp = comp->prev;
if (!comp)
- return NULL; /* FIXME: Skip redirectors? */
+ return NULL;
}
if (comp->prog)
@@ -361,12 +312,6 @@ progman_waiting_p (void)
return 0;
}
-RETSIGTYPE
-redir_exit (int sig)
-{
- _exit (0);
-}
-
int
redirect_to_file (struct prog *master, int stream)
{
@@ -400,20 +345,113 @@ redirect_to_file (struct prog *master, int stream)
}
return fd;
}
+
+struct read_buffer
+{
+ struct prog *master;
+ int stream;
+ char text[PIES_LOG_BUF_SIZE];
+ size_t len;
+ int overflow;
+};
-static void
-close_fds (fd_set *fdset)
+static int
+redirect_read (int fd, void *data)
{
- int i;
+ struct read_buffer *rb = data;
+ int n;
+ int prio = rb->master->v.p.comp->redir[rb->stream].v.prio;
+ char const *tag = prog_tag (rb->master);
+ pid_t pid = rb->master->pid;
+ char *p;
+ static char *retr_stream_name[] = {
+ [RETR_OUT] = "stdout",
+ [RETR_ERR] = "stderr"
+ };
- for (i = FD_SETSIZE-1; i >= 0; i--)
+ n = read (fd, rb->text + rb->len, sizeof (rb->text) - rb->len);
+ if (n == -1)
{
- if (fdset && FD_ISSET (i, fdset))
- continue;
- close (i);
+ if (errno != EINTR)
+ {
+ logmsg (LOG_INFO, _("%s: %s while reading %s"),
+ tag, strerror (errno), retr_stream_name[rb->stream]);
+ rb->master->v.p.redir[rb->stream] = -1;
+ close (fd);
+ deregister_socket (fd);
+ return -1;
+ }
+ }
+ else if (n == 0)
+ {
+ logmsg (LOG_INFO, _("%s: EOF on %s"),
+ tag, retr_stream_name[rb->stream]);
+ rb->master->v.p.redir[rb->stream] = -1;
+ close (fd);
+ deregister_socket (fd);
}
+ else
+ {
+ rb->len += n;
+
+ while (rb->len)
+ {
+ p = memchr (rb->text, '\n', rb->len);
+ if (p)
+ {
+ *p++ = 0;
+ if (rb->overflow)
+ rb->overflow = 0;
+ else
+ pies_syslog_message (prio, rb->text, tag, pid);
+ n = rb->len - (p - rb->text);
+ if (n > 0)
+ memmove (rb->text, p, n);
+ rb->len = n;
+ }
+ else if (rb->len == sizeof (rb->text))
+ {
+ rb->text[sizeof (rb->text) - 1] = 0;
+ pies_syslog_message (prio, rb->text, tag, pid);
+ rb->len = 0;
+ rb->overflow = 1;
+ }
+ else
+ break;
+ }
+ }
+ return 0;
+}
+
+static void
+read_buffer_free (void *p)
+{
+ free (p);
}
+int
+redirect_to_syslog (struct prog *master, int stream, int *fd)
+{
+ struct read_buffer *rb;
+ int p[2];
+
+ if (pipe (p))
+ {
+ logmsg (LOG_CRIT, "pipe: %s", strerror (errno));
+ return -1;
+ }
+
+ rb = grecs_zalloc (sizeof (*rb));
+ rb->master = master;
+ rb->stream = stream;
+ rb->len = 0;
+ rb->overflow = 0;
+
+ register_socket (p[0], redirect_read, NULL, NULL, rb, read_buffer_free);
+ *fd = p[0];
+ return p[1];
+}
+
void
free_redirector (struct redirector *rp)
{
@@ -422,73 +460,22 @@ free_redirector (struct redirector *rp)
}
int
-open_redirector (struct prog *master, int stream)
+open_redirector (struct prog *master, int stream, int *fd)
{
- int p[2];
- FILE *fp;
- char *buf = NULL;
- size_t size = 0;
- pid_t pid;
- int prio;
- char *tag;
- fd_set fdset;
-
switch (master->v.p.comp->redir[stream].type)
{
case redir_null:
- return -1;
-
+ *fd = -1;
+ break;
+
case redir_file:
+ *fd = -1;
return redirect_to_file (master, stream);
case redir_syslog:
- break;
- }
-
- if (pipe (p))
- {
- logmsg (LOG_CRIT, "pipe: %s", strerror (errno));
- return -1;
- }
-
- switch (pid = fork ())
- {
- case 0:
- /* Redirector process */
- tag = redir_tag (master, stream);
- mf_proctitle_format ("%s redirector", tag);
- free (tag);
-
- FD_ZERO (&fdset);
- FD_SET (p[0], &fdset);
- close_fds (&fdset);
-
- diag_setup (0);
- signal_setup (redir_exit);
-
- close (p[1]);
- fp = fdopen (p[0], "r");
- if (fp == NULL)
- _exit (1);
- openlog (prog_tag (master), LOG_PID, master->v.p.comp->facility);
- prio = master->v.p.comp->redir[stream].v.prio;
- while (getline (&buf, &size, fp) > 0)
- syslog (prio, "%s", buf);
- _exit (0);
-
- case -1:
- logmsg (LOG_CRIT,
- _("cannot run redirector `%s': fork failed: %s"),
- prog_tag (master), strerror (errno));
- return -1;
-
- default:
- debug (1, (_("redirector for %s started, pid=%lu"),
- prog_tag (master), (unsigned long) pid));
- update_redir (stream, master, pid);
- close (p[0]);
- return p[1];
+ return redirect_to_syslog (master, stream, fd);
}
+ return -1;
}
@@ -806,50 +793,110 @@ prog_open_socket (struct prog *prog)
}
static void
-prog_start_prologue (struct prog *prog)
+progman_ws_error (const char *fmt, ...)
{
- if (prog->v.p.comp->dir)
+ va_list ap;
+
+ va_start (ap, fmt);
+ vlogmsg (LOG_ERR, fmt, ap);
+ va_end (ap);
+}
+
+/*
+ * Initialization of the environment and argc/argv members of
+ * struct prog. For regular components this is done in the main
+ * process, so that argv can be reported correctly via the ctl
+ * interface. For tcpmux components, which are run from the
+ * listener child, this is not so. That does little harm as
+ * these components are not visible from the ctl interface anyway.
+ */
+static int
+prog_init (struct prog *prog)
+{
+ if ((prog->v.p.env = environ_create (environ)) == NULL)
{
- debug (1, (_("chdir %s"), prog->v.p.comp->dir));
- if (chdir (prog->v.p.comp->dir))
- logmsg (LOG_ERR, _("%s: cannot change to directory %s: %s"),
- prog_tag (prog), prog->v.p.comp->dir, strerror (errno));
+ logmsg (LOG_CRIT, "environ_create: %s", strerror (errno));
+ return -1;
}
-
- prog->v.p.env = environ_create (environ);
+
if (prog->v.p.comp->flags & CF_SOCKENV)
{
size_t i;
for (i = 0; sockenv_var[i]; i++)
environ_unset (prog->v.p.env, sockenv_var[i], NULL);
}
- envop_exec (prog->v.p.comp->envop, prog->v.p.env);
+
+ if (envop_exec (prog->v.p.comp->envop, prog->v.p.env))
+ {
+ logmsg (LOG_CRIT, "environ_exec: %s", strerror (errno));
+ return -1;
+ }
+
if (SYSVINIT_ACTIVE)
{
size_t i;
for (i = 0; sysvinit_environ_hint[i]; i++)
- environ_add (prog->v.p.env, sysvinit_environ_hint[i]);
+ {
+ if (environ_add (prog->v.p.env, sysvinit_environ_hint[i]))
+ {
+ logmsg (LOG_CRIT, "environ_add: %s", strerror (errno));
+ return -1;
+ }
+ }
}
- debug_environ (prog, 4);
+ debug_environ (prog, 4);
- pies_priv_setup (&prog->v.p.comp->privs);
- if (prog->v.p.comp->umask)
- umask (prog->v.p.comp->umask);
+ if (prog->v.p.comp->flags & CF_EXPANDENV)
+ {
+ struct wordsplit ws;
- set_limits (prog_tag (prog),
- prog->v.p.comp->limits ?
- prog->v.p.comp->limits : pies_limits);
+ ws.ws_env = (const char **) environ_ptr (prog->v.p.env);
+ ws.ws_error = progman_ws_error;
+ if (wordsplit (prog->v.p.comp->command, &ws,
+ WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS | WRDSF_NOCMD | WRDSF_ENV | WRDSF_ERROR))
+ {
+ logmsg (LOG_ERR, _("%s: can't split command line: %s"),
+ prog_tag (prog), wordsplit_strerror (&ws));
+ return -1;
+ }
+ wordsplit_get_words (&ws, &prog->v.p.argc, &prog->v.p.argv);
+ wordsplit_free (&ws);
+ }
+ else
+ {
+ prog->v.p.argc = prog->v.p.comp->argc;
+ prog->v.p.argv = prog->v.p.comp->argv;
+ }
- if (debug_level >= 1)
+ if (debug_level >= 1 && prog->v.p.argv)
{
int i;
- struct component *comp = prog->v.p.comp;
-
logmsg_printf (LOG_DEBUG, "executing");
- for (i = 0; i < comp->argc; i++)
- logmsg_printf (LOG_DEBUG, " %s", quotearg (comp->argv[i]));
+ for (i = 0; i < prog->v.p.argc; i++)
+ logmsg_printf (LOG_DEBUG, " %s", quotearg (prog->v.p.argv[i]));
logmsg_printf (LOG_DEBUG, "\n");
}
+ return 0;
+}
+
+static void
+prog_start_prologue (struct prog *prog)
+{
+ if (prog->v.p.comp->dir)
+ {
+ debug (1, (_("chdir %s"), prog->v.p.comp->dir));
+ if (chdir (prog->v.p.comp->dir))
+ logmsg (LOG_ERR, _("%s: cannot change to directory %s: %s"),
+ prog_tag (prog), prog->v.p.comp->dir, strerror (errno));
+ }
+
+ pies_priv_setup (&prog->v.p.comp->privs);
+ if (prog->v.p.comp->umask)
+ umask (prog->v.p.comp->umask);
+
+ set_limits (prog_tag (prog),
+ prog->v.p.comp->limits ?
+ prog->v.p.comp->limits : pies_limits);
}
static void
@@ -865,14 +912,18 @@ prog_execute (struct prog *prog)
}
execvp (prog->v.p.comp->program ?
- prog->v.p.comp->program : prog->v.p.comp->argv[0],
- prog->v.p.comp->argv);
- openlog (log_tag, LOG_PID, prog->v.p.comp->facility);
+ prog->v.p.comp->program : prog->v.p.argv[0],
+ prog->v.p.argv);
+ //FIXME: pies_syslog?
syslog (LOG_CRIT, _("cannot start `%s': %s"), prog_tag (prog),
strerror (errno));
_exit (EX_SOFTWARE);
}
+/*
+ * Run a TCPMUX component. This function is invoked from a child
+ * process.
+ */
void
progman_run_comp (struct component *comp, int fd,
union pies_sockaddr_storage *sa, socklen_t salen)
@@ -882,6 +933,8 @@ progman_run_comp (struct component *comp, int fd,
prog->v.p.sa_storage = *sa;
prog->v.p.sa_len = salen;
prog->v.p.cclass = conn_class_lookup (comp->tag, sa, salen);
+ if (prog_init (prog))
+ _exit (127);
prog_start_prologue (prog);
prog_sockenv (prog);
prog_execute (prog);
@@ -892,7 +945,6 @@ prog_start (struct prog *prog)
{
pid_t pid;
int redir[2];
- fd_set fdset;
if (prog->pid > 0 || !IS_COMPONENT (prog))
return;
@@ -972,8 +1024,13 @@ prog_start (struct prog *prog)
return;
}
- redir[RETR_OUT] = open_redirector (prog, RETR_OUT);
- redir[RETR_ERR] = open_redirector (prog, RETR_ERR);
+ if (prog_init (prog))
+ return; //FIXME
+
+ redir[RETR_OUT] = open_redirector (prog, RETR_OUT,
+ &prog->v.p.redir[RETR_OUT]);
+ redir[RETR_ERR] = open_redirector (prog, RETR_ERR,
+ &prog->v.p.redir[RETR_ERR]);
switch (pid = fork ())
{
@@ -1054,13 +1111,8 @@ prog_start (struct prog *prog)
}
/* Close unneeded descripitors */
- FD_ZERO (&fdset);
- FD_SET (0, &fdset);
- FD_SET (1, &fdset);
- FD_SET (2, &fdset);
- if (prog->v.p.comp->mode == pies_comp_pass_fd)
- FD_SET (prog->v.p.socket, &fdset);
- close_fds (&fdset);
+ pies_close_fds ((prog->v.p.comp->mode == pies_comp_pass_fd
+ ? prog->v.p.socket : 2) + 1);
prog_execute (prog);
@@ -1289,7 +1341,7 @@ prog_create_socket (struct prog *prog, void *data)
comp->privs.user, comp->umask);
if (fd == -1)
destroy_prog (&prog);
- else if (register_program_socket (comp->socket_type, fd, prog))
+ else if (register_program_socket (comp->socket_type, fd, prog, NULL))
{
close (fd);
destroy_prog (&prog);
@@ -1538,15 +1590,6 @@ prog_start_prerequisites (struct prog *prog)
}
void
-prog_stop_redirectors (struct prog *prog)
-{
- if (prog->v.p.redir[RETR_OUT])
- prog_stop (prog->v.p.redir[RETR_OUT], SIGTERM);
- if (prog->v.p.redir[RETR_ERR])
- prog_stop (prog->v.p.redir[RETR_ERR], SIGTERM);
-}
-
-void
prog_stop_dependents (struct prog *prog)
{
struct component *comp;
@@ -2004,7 +2047,7 @@ run_command (struct action *act, struct prog *prog, unsigned retcode,
else
setenv ("PIES_STATUS", umaxtostr (STATUS_CODE (retcode), buf), 1);
- close_fds (NULL);
+ pies_close_fds (3);
argv[0] = "/bin/sh";
argv[1] = "-c";
@@ -2182,17 +2225,6 @@ progman_cleanup (int expect_term)
break;
- case TYPE_REDIRECTOR:
- /* It was a redirector of an already finished process. */
- print_status (prog_tag (prog), pid, status,
- expect_term ||
- (prog->v.r.master &&
- prog->v.r.master->v.p.status == status_stopping));
- debug (1, (_("removing redirector %s, pid=%lu"),
- prog_tag (prog), (unsigned long)pid));
- destroy_prog (&prog);
- break;
-
case TYPE_COMMAND:
print_status (prog_tag (prog), pid, status, expect_term);
destroy_prog (&prog);
@@ -2279,7 +2311,7 @@ prog_activate_listener (struct prog *prog)
comp->privs.user, comp->umask);
if (fd == -1)
return -1;
- else if (register_program_socket (comp->socket_type, fd, prog))
+ else if (register_program_socket (comp->socket_type, fd, prog, NULL))
{
close (fd);
return -1;
diff --git a/src/socket.c b/src/socket.c
index 4338a49..9eb5028 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2007-2020 Sergey Poznyakoff
+ Copyright (C) 2007-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -404,6 +404,7 @@ struct sockinst
int fd;
int dead;
socket_handler_t handler[3];
+ void (*free_data)(void *);
void *data;
};
@@ -437,7 +438,8 @@ register_socket (int fd,
socket_handler_t rd,
socket_handler_t wr,
socket_handler_t ex,
- void *data)
+ void *data,
+ void (*free_data) (void *))
{
struct sockinst *sip = grecs_malloc (sizeof *sip);
sip->fd = fd;
@@ -445,6 +447,7 @@ register_socket (int fd,
sip->handler[PIES_EVT_RD] = rd;
sip->handler[PIES_EVT_WR] = wr;
sip->handler[PIES_EVT_EX] = ex;
+ sip->free_data = free_data;
sip->data = data;
sip->next = NULL;
sip->prev = si_tail;
@@ -515,6 +518,10 @@ deregister_socket (int fd)
struct sockinst *sp = find_socket (fd);
if (!sp)
return;
+ if (sp->free_data)
+ sp->free_data (sp->data);
+ sp->data = NULL;
+ sp->free_data = NULL;
if (si_iterating)
sp->dead = 1;
else
@@ -523,14 +530,14 @@ deregister_socket (int fd)
int
-register_program_socket (int socktype, int fd, void *data)
+register_program_socket (int socktype, int fd, void *data, void (*free_data) (void*))
{
if (socktype == SOCK_STREAM && listen (fd, 8) == -1)
{
logmsg (LOG_ERR, "listen: %s", strerror (errno));
return 1;
}
- register_socket (fd, progman_accept, NULL, NULL, data);
+ register_socket (fd, progman_accept, NULL, NULL, data, free_data);
return 0;
}
diff --git a/src/sysdep.c b/src/sysdep.c
index 845ba13..0e7ee37 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2014-2020 Sergey Poznyakoff
+ Copyright (C) 2014-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/syslog.c b/src/syslog.c
new file mode 100644
index 0000000..0323efb
--- a/dev/null
+++ b/src/syslog.c
@@ -0,0 +1,510 @@
+/* This file is part of GNU Pies.
+ Copyright (C) 2007-2021 Sergey Poznyakoff
+
+ GNU 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.
+
+ GNU 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 GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include "pies.h"
+#include "pies_syslog.h"
+
+struct log_message
+{
+ char *text; /* Message text (no terminating nul). */
+ size_t len; /* Number of bytes in text */
+ size_t drop_count; /* Count of messages dropped so far (first message only) */
+ int trunc;
+ struct log_message *next;
+};
+
+struct log_message_in
+{
+ struct log_message msg;
+ char buf[PIES_LOG_BUF_SIZE];
+};
+
+/* Global variables */
+/* Fallback log file is used to log critical messages when syslog
+ daemon is unavailable. If NULL, stderr will be used. */
+char *pies_fallback_file = "/tmp/pies_logger.log";
+/* Log tag */
+char *pies_log_tag = "pies";
+/* Log facility */
+int pies_log_facility = LOG_USER;
+/* Maximum capacity of the log message queue */
+size_t pies_log_max_queue = PIES_LOG_MAX_QUEUE;
+
+/* Name of the syslog device. If starts with a slash, it is assumed
+ to be a UNIX socket name. Otherwise, it is assumed to be a host name
+ or IPv4 address of the syslog daemon, optionally followed by a colon
+ and port number or service name.
+ If NULL, PIES_LOG_DEV is assumed.
+ The user can modify this value using the pies_syslog_set_dev call.
+*/
+static char *log_dev;
+/* Log socket descriptor. */
+static int log_fd = -1;
+
+/* Socket address */
+static union {
+ struct sockaddr_in s_in;
+ struct sockaddr_un s_un;
+} log_sa;
+
+/* Socket address length. */
+static socklen_t log_salen = 0;
+
+/* Socked address family. */
+static int log_family;
+
+static inline int
+pri_facility (int pri)
+{
+ return pri & ~0x7;
+}
+
+static inline int
+pri_severity (int pri)
+{
+ return pri & 0x7;
+}
+
+/* Fallback logger */
+static void
+fallback_log (char const *fmt, ...)
+{
+ FILE *fp = NULL;
+ va_list ap;
+
+ if (pies_fallback_file)
+ fp = fopen (pies_fallback_file, "a");
+ if (!fp)
+ fp = stderr;
+ fprintf (fp, "pies[%lu]: ", (unsigned long) getpid());
+ va_start (ap, fmt);
+ vfprintf (fp, fmt, ap);
+ va_end (ap);
+ fputc ('\n', fp);
+ if (fp != stderr)
+ fclose (fp);
+}
+
+static int
+reopen_logger (void)
+{
+ int fd;
+ int flags;
+
+ if (log_salen == 0)
+ {
+ char *dev = log_dev ? log_dev : PIES_LOG_DEV;
+ if (dev[0] == '/')
+ {
+ size_t len = strlen (dev);
+ if (len >= sizeof log_sa.s_un.sun_path)
+ {
+ fallback_log ("%s: UNIX socket name too long", dev);
+ return -1;
+ }
+ strcpy (log_sa.s_un.sun_path, dev);
+ log_sa.s_un.sun_family = AF_UNIX;
+ log_family = PF_UNIX;
+ log_salen = sizeof (log_sa.s_un);
+ }
+ else
+ {
+ struct addrinfo hints;
+ struct addrinfo *res;
+ int rc;
+ char *node;
+ char *service;
+
+ node = strdup (dev);
+ if (!node)
+ return -1;
+
+ service = strchr (node, ':');
+ if (service)
+ *service++ = 0;
+ else
+ service = "syslog";
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+
+ rc = getaddrinfo (node, service, &hints, &res);
+ free(node);
+ if (rc)
+ {
+ fallback_log ("%s: invalid socket address", dev);
+ return -1;
+ }
+
+ memcpy (&log_sa, res->ai_addr, res->ai_addrlen);
+ log_family = PF_INET;
+ log_salen = res->ai_addrlen;
+ freeaddrinfo (res);
+ }
+ }
+
+ fd = socket (log_family, SOCK_DGRAM, 0);
+
+ if (fd == -1)
+ {
+ fallback_log ("socket: %s", strerror (errno));
+ return -1;
+ }
+
+ if ((flags = fcntl (fd, F_GETFL)) == -1 ||
+ fcntl (fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
+ (flags = fcntl (fd, F_GETFD)) == -1 ||
+ fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ {
+ close (fd);
+ return -1;
+ }
+
+ if (connect(fd, (struct sockaddr*)&log_sa, log_salen))
+ {
+ fallback_log("socket: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ log_fd = fd;
+
+ return 0;
+}
+
+static struct log_message_in *
+log_message_in_create (void)
+{
+ struct log_message_in *msg;
+
+ if ((msg = malloc (sizeof (*msg))) != NULL)
+ {
+ msg->msg.text = msg->buf;
+ msg->msg.len = 0;
+ msg->msg.drop_count = 0;
+ msg->msg.trunc = 0;
+ msg->msg.next = NULL;
+ }
+ return msg;
+}
+
+static void
+log_message_in_format (struct log_message_in *msg,
+ int prio, char const *msgtext, char const *tag,
+ pid_t pid)
+{
+ char tbuf[sizeof ("Jan 1 00:00:00")];
+ char hostbuf[256];
+ struct timeval tv;
+ struct tm tm;
+ int n;
+
+ gettimeofday (&tv, NULL);
+ localtime_r (&tv.tv_sec, &tm);
+ strftime (tbuf, sizeof (tbuf), "%b %d %H:%M:%S", &tm);
+
+ /* Supply default facility, unless prio already contains one.
+ Note: this means that we cannot use LOG_KERN, but that doesn't
+ really matter as we're not a kernel, anyway. */
+ if (pri_facility (prio) == 0)
+ prio |= pies_log_facility;
+
+ if (log_family == PF_UNIX)
+ {
+ n = snprintf (msg->buf, sizeof (msg->buf),
+ "<%d>%s %s[%lu]: %s",
+ prio,
+ tbuf,
+ tag,
+ (unsigned long)pid,
+ msgtext);
+ }
+ else
+ {
+ gethostname (hostbuf, sizeof (hostbuf));
+ n = snprintf (msg->buf, sizeof (msg->buf),
+ "<%d>%s %s %s[%lu]: %s",
+ prio,
+ tbuf,
+ hostbuf,
+ tag,
+ (unsigned long)pid,
+ msgtext);
+ }
+ if (n >= sizeof (msg->buf))
+ {
+ /* Overflow */
+ n = sizeof (msg->buf);
+ msg->msg.trunc = 1;
+ }
+ msg->msg.len = n;
+}
+
+static struct log_message *
+log_message_create (int prio, char const *msgtext, char const *tag, pid_t pid)
+{
+ struct log_message_in *msg;
+
+ if ((msg = log_message_in_create ()) != NULL)
+ log_message_in_format (msg, prio, msgtext, tag, pid);
+ return &msg->msg;
+}
+
+/* Log message queue */
+static struct log_message *log_queue_head, *log_queue_tail;
+static size_t log_queue_length;
+
+static void
+log_message_putback (struct log_message *msg)
+{
+ msg->next = log_queue_head;
+ log_queue_head = msg;
+ if (!log_queue_tail)
+ log_queue_tail = msg;
+ log_queue_length++;
+}
+
+static struct log_message *
+log_message_dequeue (void)
+{
+ struct log_message *msg = log_queue_head;
+ if (msg)
+ {
+ log_queue_head = msg->next;
+ if (!log_queue_head)
+ log_queue_tail = log_queue_head;
+ msg->next = NULL;
+ log_queue_length--;
+ }
+ return msg;
+}
+
+static void
+log_message_enqueue (struct log_message *inmsg)
+{
+ int truncated = 0;
+
+ do
+ {
+ if (log_queue_length == pies_log_max_queue)
+ {
+ struct log_message *msg;
+ struct log_message_in *tmp;
+ char buf[PIES_LOG_BUF_SIZE];
+
+ /* Dequeue first message */
+ msg = log_message_dequeue ();
+
+ if (msg->drop_count == 0)
+ {
+ /* If it is not a drop message, free it and create a new
+ drop message */
+ free (msg);
+ tmp = log_message_in_create ();
+ tmp->msg.drop_count = 1;
+ }
+ else
+ /* Otherwise, cast it to log_message_in */
+ tmp = (struct log_message_in *)msg;
+
+ /* Dequeue and drop the first message */
+ free (log_message_dequeue ());
+ tmp->msg.drop_count++;
+
+ /* Reformat the message text */
+ snprintf (buf, sizeof (buf), "%zu messages dropped",
+ tmp->msg.drop_count);
+ log_message_in_format (tmp,
+ pies_log_facility|LOG_CRIT,
+ buf,
+ pies_log_tag,
+ getpid ());
+
+ log_message_putback (&tmp->msg);
+ }
+
+ if (log_queue_tail)
+ log_queue_tail->next = inmsg;
+ else
+ log_queue_head = inmsg;
+ log_queue_tail = inmsg;
+ log_queue_length++;
+
+ if (truncated)
+ /* Make sure at most one truncation message is added */
+ truncated = 0;
+ else if (inmsg->trunc)
+ {
+ inmsg = log_message_create (pies_log_facility|LOG_CRIT,
+ "-- message truncated --",
+ pies_log_tag, getpid ());
+ truncated = 1;
+ }
+ }
+ while (truncated);
+
+ pies_syslog_flush ();
+}
+
+/*
+ * Flush the message queue to the socket.
+ * Some fragments borrowed from the excellent syslog_async written by
+ * Simon Kelley (http://www.thekelleys.org.uk/syslog-async).
+ */
+void
+pies_syslog_flush (void)
+{
+ struct log_message *msg;
+ int rc;
+
+ while ((msg = log_message_dequeue ()) != NULL)
+ {
+ if (log_fd == -1)
+ reopen_logger ();
+
+ rc = send (log_fd, msg->text, msg->len, MSG_NOSIGNAL);
+ if (rc != -1)
+ {
+ free (msg);
+ continue;
+ }
+ log_message_putback (msg);
+
+ if (errno == EINTR)
+ continue;//Should not happen??
+ if (errno == EAGAIN)
+ break;
+
+ /* *BSD, returns this instead of blocking? */
+ if (errno == ENOBUFS)
+ break;
+
+ /* A stream socket closed at the other end goes into EPIPE
+ forever, close and re-open. */
+ if (errno == EPIPE)
+ {
+ close (log_fd);
+ log_fd = -1;
+ continue;
+ }
+
+ if (errno == ECONNREFUSED || /* connection went down */
+ errno == ENOTCONN || /* nobody listening */
+ errno == EDESTADDRREQ || /* BSD equivalents of the above */
+ errno == ECONNRESET)
+ {
+ /* The reader is gone. Try reconnecting. If failed,
+ retry when called next time. */
+
+ if (connect (log_fd, (struct sockaddr *)&log_sa, log_salen) != -1)
+ /* Connected successfully: retry now */
+ continue;
+
+ if (errno == ENOENT ||
+ errno == EALREADY ||
+ errno == ECONNREFUSED ||
+ errno == EISCONN ||
+ errno == EINTR ||
+ errno == EAGAIN)
+ /* try again when woken up again */
+ break;
+ }
+
+ /* Else ? */
+ break;
+ }
+}
+
+void
+logger_log (int prio, char const *msgtext, char const *tag, pid_t pid)
+{
+ struct log_message *msg;
+
+ msg = log_message_create (prio, msgtext, tag, pid);
+ if (msg)
+ log_message_enqueue (msg);
+}
+
+/* Upper level logger API */
+void
+pies_vsyslog (int pri, char const *fmt, va_list ap)
+{
+ char buf[PIES_LOG_BUF_SIZE];
+ vsnprintf (buf, sizeof(buf), fmt, ap);
+ logger_log (pri, buf, pies_log_tag, getpid ());
+}
+
+void
+pies_syslog (int pri, char const *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ pies_vsyslog (pri, fmt, ap);
+ va_end (ap);
+}
+
+int
+pies_syslog_open (void)
+{
+ return reopen_logger ();
+}
+
+void
+pies_syslog_close (void)
+{
+ if (log_fd != -1)
+ {
+ close (log_fd);
+ log_fd = -1;
+ }
+}
+
+void
+pies_syslog_message (int prio, char const *text, char const *tag, pid_t pid)
+{
+ log_message_enqueue (log_message_create (prio, text, tag, pid));
+}
+
+int
+pies_syslog_set_dev (char const *dev)
+{
+ char *p = strdup (dev);
+ if (!p)
+ return -1;
+ pies_syslog_close ();
+ free (log_dev);
+ log_dev = p;
+ log_salen = 0;
+ return 0;
+}
diff --git a/src/sysvinit.c b/src/sysvinit.c
index 688d692..e5fedf1 100644
--- a/src/sysvinit.c
+++ b/src/sysvinit.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2013-2020 Sergey Poznyakoff
+ Copyright (C) 2013-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -625,7 +625,7 @@ create_fifo (void)
strerror (errno));
return;
}
- register_socket (fd, sysvinit_fifo_handler, NULL, NULL, NULL);
+ register_socket (fd, sysvinit_fifo_handler, NULL, NULL, NULL, NULL);
}
static char *try_console[] = { NULL, "/dev/console", "/dev/tty0" };
@@ -1167,7 +1167,7 @@ inittab_parse (const char *file)
fp = fopen (file, "r");
if (fp)
{
- while (getline (&ctx.buf, &ctx.size, fp) >= 0)
+ while (grecs_getline (&ctx.buf, &ctx.size, fp) >= 0)
{
enum inittab_status st = inittab_parse_line (&ctx);
if (st == inittab_err)
diff --git a/src/telinit.opt b/src/telinit.opt
index 1d781a7..4d3a426 100644
--- a/src/telinit.opt
+++ b/src/telinit.opt
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies. -*- c -*-
- Copyright (C) 2016-2020 Sergey Poznyakoff
+ Copyright (C) 2016-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/userprivs.c b/src/userprivs.c
index 4473b34..fd65ba8 100644
--- a/src/userprivs.c
+++ b/src/userprivs.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2007-2020 Sergey Poznyakoff
+ Copyright (C) 2007-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/utmp.c b/src/utmp.c
index cdd1f25..38e05bd 100644
--- a/src/utmp.c
+++ b/src/utmp.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2013-2020 Sergey Poznyakoff
+ Copyright (C) 2013-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/Makefile.am b/tests/Makefile.am
index bfdc692..25719bf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of GNU Pies.
-# Copyright (C) 2008-2020 Sergey Poznyakoff
+# Copyright (C) 2008-2021 Sergey Poznyakoff
#
# GNU Pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -54,7 +54,9 @@ TESTSUITE_AT = \
control.at\
cyclic.at\
env.at\
+ envglobal.at\
envop.at\
+ expandenv.at\
inet.at\
maxinst.at\
passfd.at\
diff --git a/tests/accept.at b/tests/accept.at
index e12be02..ae2af92 100644
--- a/tests/accept.at
+++ b/tests/accept.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2019-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2019-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -51,4 +51,4 @@ cat log.err >&2
the time
stop
])
-AT_CLEANUP \ No newline at end of file
+AT_CLEANUP
diff --git a/tests/atlocal.in b/tests/atlocal.in
index c043843..f18034c 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -1,6 +1,6 @@
# @configure_input@ -*- shell-script -*-
# Configurable variable values for GNU Pies test suite.
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# Copyright (C) 2016-2021 Sergey Poznyakoff
PATH=@abs_builddir@:@abs_top_builddir@/src:$srcdir:$PATH
XFAILFILE=$abs_builddir/.badversion
diff --git a/tests/builtin.at b/tests/builtin.at
index 88e24d7..cfda075 100644
--- a/tests/builtin.at
+++ b/tests/builtin.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chargen.c b/tests/chargen.c
index 59aa0d8..19f99da 100644
--- a/tests/chargen.c
+++ b/tests/chargen.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies testsuite.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/control.at b/tests/control.at
index 5171862..f85e735 100644
--- a/tests/control.at
+++ b/tests/control.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cyclic.at b/tests/cyclic.at
index 7c24acf..2d580a0 100644
--- a/tests/cyclic.at
+++ b/tests/cyclic.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2019-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2019-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,6 +15,7 @@
# along with GNU pies. If not, see <http://www.gnu.org/licenses/>.
AT_SETUP([Detecting cyclic dependencies])
+
AT_CHECK([
PIES_XFAIL_CHECK
# The following matrices describe the test.conf configuration file below.
@@ -105,10 +106,38 @@ Legend:
1: g
2: h
],
-[pies: component a depends on itself
+[pies: cyclic dependencies detected:
pies: a -> d -> c -> e -> a
-pies: component b depends on itself
pies: b -> b
])
+AT_CHECK([
+AT_DATA([test.conf],[
+component a {
+ command "a";
+ prerequisites (b,c);
+}
+component b {
+ command "b";
+ prerequisites (c);
+}
+component c {
+ command "c";
+ prerequisites (d);
+}
+component d {
+ command "d";
+ prerequisites (a);
+}
+])
+pies --config-file test.conf --dump-depmap | trimws
+],
+[0],
+[No components defined
+],
+[pies: cyclic dependencies detected:
+pies: a -> c -> d -> a
+pies: a -> b -> c -> d -> a
+])
+
AT_CLEANUP
diff --git a/tests/env.at b/tests/env.at
index dba80a3..88412fd 100644
--- a/tests/env.at
+++ b/tests/env.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2020-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/envglobal.at b/tests/envglobal.at
new file mode 100644
index 0000000..357649b
--- a/dev/null
+++ b/tests/envglobal.at
@@ -0,0 +1,86 @@
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2020-2021 Sergey Poznyakoff
+#
+# GNU 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.
+#
+# GNU 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 GNU pies. If not, see <http://www.gnu.org/licenses/>.
+dnl ENVTEST(NAME,KW,ENV,OUT)
+m4_pushdef([ENVTEST],
+[AT_SETUP([$1])
+AT_KEYWORDS([env $2])
+AT_CHECK([
+PIES_XFAIL_CHECK
+PIES_CONTROL_INIT
+cat > envtest.conf <<_EOT
+env {
+ $3
+}
+component envtest {
+ command "$abs_builddir/envtest -clone";
+ chdir $PWD;
+ stdout file "$PWD/log";
+ return-code 0 {
+ action disable;
+ exec "$abs_top_builddir/src/piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown";
+ }
+}
+_EOT
+
+to 5 \
+ envtest -exec \
+ $abs_top_builddir/src/pies --foreground --stderr \
+ --config-file control.conf --config-file envtest.conf --debug 1 2>errlog
+cat log
+],
+[0],
+[$4])
+AT_CLEANUP])
+
+dnl #############################
+dnl Start tests
+dnl #############################
+
+AT_BANNER([Global environment statement])
+
+ENVTEST([clear],[clear],[clear;],[])
+
+ENVTEST([keep],[keep],[keep "LC_*";],
+[LC_ALL="C"
+LC_CTYPE="C"
+LC_MESSAGES="C"
+LC_NUMERIC="C"
+])
+
+ENVTEST([set],[set],[set "FOO=bar";],
+[FOO="bar"
+HOME="/home/user"
+LC_ALL="C"
+LC_CTYPE="C"
+LC_MESSAGES="C"
+LC_NUMERIC="C"
+LOGIN="user"
+PATH="/usr/local/bin:/usr/bin:/bin"
+PIES_INSTANCE="pies"
+PWD="/home"
+USER="user"
+])
+
+ENVTEST([unset],[unset],[unset "LC_*"; unset PWD;],
+[HOME="/home/user"
+LOGIN="user"
+PATH="/usr/local/bin:/usr/bin:/bin"
+PIES_INSTANCE="pies"
+USER="user"
+])
+
+m4_popdef([ENVTEST])
+
diff --git a/tests/envop.at b/tests/envop.at
index 5575772..3cdd43e 100644
--- a/tests/envop.at
+++ b/tests/envop.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2020-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/envtest.c b/tests/envtest.c
index 45d69a2..7c65f2a 100644
--- a/tests/envtest.c
+++ b/tests/envtest.c
@@ -1,5 +1,5 @@
/* Environment test program for GNU Pies.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/expandenv.at b/tests/expandenv.at
new file mode 100644
index 0000000..70c6a26
--- a/dev/null
+++ b/tests/expandenv.at
@@ -0,0 +1,50 @@
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
+#
+# GNU 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.
+#
+# GNU 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 GNU pies. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([flags expandenv])
+
+AT_CHECK([
+PIES_XFAIL_CHECK
+PIES_CONTROL_INIT
+statefile=$PWD/state
+cat > pies.conf <<EOT
+component test {
+ mode respawn;
+ flags expandenv;
+ command "\$auxdir/respawn \$statefile 3";
+ chdir "$PWD";
+ return-code 1 {
+ action disable;
+ exec "piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown";
+ }
+}
+EOT
+
+export auxdir
+export statefile
+
+set -e
+to 5 \
+ pies --foreground --stderr \
+ --config-file control.conf --config-file pies.conf --debug 4 2>errlog
+
+cat state
+],
+[0],
+[3
+])
+
+AT_CLEANUP
diff --git a/tests/inet.at b/tests/inet.at
index cc4fceb..c40ee59 100644
--- a/tests/inet.at
+++ b/tests/inet.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/iobuf.h b/tests/iobuf.h
index df5f3ba..823c034 100644
--- a/tests/iobuf.h
+++ b/tests/iobuf.h
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/lines.c b/tests/lines.c
index 0ad9d13..f667ed4 100644
--- a/tests/lines.c
+++ b/tests/lines.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies testsuite.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/maxinst.at b/tests/maxinst.at
index f389e0b..23f3ea7 100644
--- a/tests/maxinst.at
+++ b/tests/maxinst.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2019-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2019-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/nt.c b/tests/nt.c
index 258970f..74ab6ba 100644
--- a/tests/nt.c
+++ b/tests/nt.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies testsuite.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/passfd.at b/tests/passfd.at
index 74d5ab3..3a829f1 100644
--- a/tests/passfd.at
+++ b/tests/passfd.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -54,4 +54,4 @@ cat log.err >&2
the time
stop
])
-AT_CLEANUP \ No newline at end of file
+AT_CLEANUP
diff --git a/tests/readtime.c b/tests/readtime.c
index ed3f2f8..b6142f3 100644
--- a/tests/readtime.c
+++ b/tests/readtime.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies testsuite.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/recvfd.c b/tests/recvfd.c
index 8e0ec79..0bc8335 100644
--- a/tests/recvfd.c
+++ b/tests/recvfd.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies testsuite.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/redirect.at b/tests/redirect.at
index 5cc5548..04bb724 100644
--- a/tests/redirect.at
+++ b/tests/redirect.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/respawn.at b/tests/respawn.at
index 68da448..7698d92 100644
--- a/tests/respawn.at
+++ b/tests/respawn.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ret-exec.at b/tests/ret-exec.at
index 8f6152e..c875a36 100644
--- a/tests/ret-exec.at
+++ b/tests/ret-exec.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ret-notify.at b/tests/ret-notify.at
index 29ff677..9be2dd4 100644
--- a/tests/ret-notify.at
+++ b/tests/ret-notify.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/shell.at b/tests/shell.at
index 8375e22..15017a6 100644
--- a/tests/shell.at
+++ b/tests/shell.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/shutdown.at b/tests/shutdown.at
index d89f6f9..3c79192 100644
--- a/tests/shutdown.at
+++ b/tests/shutdown.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2019-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2019-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/startup.at b/tests/startup.at
index 5a6b19a..b0a6c28 100644
--- a/tests/startup.at
+++ b/tests/startup.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2019-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2019-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/testsuite.at b/tests/testsuite.at
index eab643d..731c4c3 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -69,6 +69,7 @@ m4_include([ret-notify.at])
m4_include([startup.at])
m4_include([shutdown.at])
m4_include([shell.at])
+m4_include([expandenv.at])
m4_include([inet.at])
m4_include([maxinst.at])
m4_include([builtin.at])
@@ -76,4 +77,5 @@ m4_include([passfd.at])
m4_include([accept.at])
m4_include([envop.at])
-m4_include([env.at]) \ No newline at end of file
+m4_include([env.at])
+m4_include([envglobal.at])
diff --git a/tests/to.c b/tests/to.c
index 2e87133..b89db5e 100644
--- a/tests/to.c
+++ b/tests/to.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies testsuite.
- Copyright (C) 2019-2020 Sergey Poznyakoff
+ Copyright (C) 2019-2021 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/tests/version.at b/tests/version.at
index c92cbaa..b0856c5 100644
--- a/tests/version.at
+++ b/tests/version.at
@@ -1,5 +1,5 @@
-# This file is part of GNU pies testsuite. -*- Autotest -*-
-# Copyright (C) 2016-2020 Sergey Poznyakoff
+# This file is part of GNU pies testsuite. -*- autotest -*-
+# Copyright (C) 2016-2021 Sergey Poznyakoff
#
# GNU pies is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by

Return to:

Send suggestions and report system problems to the System administrator.