This is a brief description of runpipe v1.0 beta. There are probably some
bugs wandering around the program. I think I've caught all potential
security holes, but reports of problems should be sent to me (see address
at the end of this document).

This program was inspired by the "planner" package which evolved from a
package apprently first written by Tony Rems (rembo@unisoft.UUCP) some
time before February 1991. This package appeared in comp.sources.misc,
and is available by anonymous FTP at:

ftp://caliban.physics.utoronto.ca/pub/planner.tar.Z


What the planner package does is to create a named pipe replacing the
.plan file in somebody's home directory, and then to sit on the pipe
waiting for a read request. When something attempts to read from the pipe
the planner package attaches a user-supplied program to the other end of
the pipe, and the read request then reads the output of that program.

The runpipe package is a generalization of that idea. While planner could
only monitor one pipe at a time, and only for attempts to read the pipe,
the runpipe daemon watches any number of pipes for reading, writing, or
both. A client program is used to add or delete entries to the daemon's
table, or to list table entries.

Runpipe runs in one of three modes, determined at compile time:

User mode: the daemon installs itself in the user's space, and refuses
connections from any other user. When an action is detected on one of the
pipes the daemon is watching it runs the appropriate program or script
with all the privileges of the owner of the daemon.

System mode: the daemon runs as root. It accepts requests only from
clients in the correct group, a group which should contain no regular
users. The client is setgid to that group. As requests are made it logs
the uid of the user who ran the client program. When an action is
detected on a pipe it runs the appropriate program or script on the pipe
with the privileges of the user who submitted the request to the daemon.
It is possible to configure the daemon not to run processes as root,
which it otherwise would do if root submitted a request.

Paranoid mode: the daemon runs as a non-existent unprivileged user, such
as the "nobody" user name. The daemon watches the pipes, and when action
is detected it runs processes as "nobody". It will not have privileges to
read protected files or write log files or messages to the user file
space, even if asked to do so by the user submitting the client request.

In all cases the daemon first changes directories to the one which was
current when the client was invoked. This may not be possible in paranoid
mode if the "nobody" user is not allowed to enter that directory.


To build the daemon and client, first edit the file "configure", and then
issue the command "make".

You must be root to build the system or paranoid mode daemon and client,
and must be root to start the daemon in one of these two modes.


To run the runpipe daemon, just enter "runpiped". It will check to make
sure there isn't already a running daemon on its socket and then install
itself.

To submit requests to the daemon, use the "runpipe" client program in one
of the following formats:

runpipe <pipename> r <command and arguments>
runpipe <pipename> w <command and arguments>
runpipe <pipename> b <command and arguments>
runpipe <pipename> d
runpipe t

The first three lines command the daemon to monitor the pipe <pipename>
for read/write/read-write actions respectively. When the appropriate
action is detected, it runs <command and arguments> on the other end of
the pipe.

for instance:

runpipe /tmp/passwd r cat /etc/passwd

would create a pipe called "passwd" in the /tmp directory, and any
attempt to read that pipe would cause the /etc/passwd file to be output
on the pipe.


The fourth invocation deletes the entry from the runpiped table
associated with the pipe "pipename".

The final invocation lists all entries in the runpiped table which were
submitted by the current userid.


   Suggested applications:
   =======================

The runpipe daemon is probably most useful as a way to produce a file on
disk whose output is produced by a program, instead of simply the
contents of a text file. By setting the daemon to watch a user's
$HOME/.plan it would be possible to have finger requests logged (by
running 'netstat'), or, say, to run 'fortune' and produce a different
.plan every time it is run. Plan files that count down to a given day can
also be implemented this way.

By monitoring $HOME/.signature it would be possible to have a USENET sig
file change. This could produce different signatures depending on system
circumstances, date, time, or whatever.


   Tips for using runpipe:
   =======================

Use complete path names within scripts. Your environment is not saved in
the runpiped tables, so you cannot count on any command being found if it
isn't in /bin, /usr/bin, or the current working directory when the client
was invoked.


It is possible to compile the client program so that it will create a
pipe if it doesn't exist when the request is made. In that case, the pipe
will be created with the runpipe group identity if the client/daemon pair
was compiled for system or paranoid mode operation. This shouldn't be a
problem, but might look funny in a long directory listing.


Some programs will refuse to read a pipe. For instance, with the example
above, you could not do:

less /tmp/passwd

To do this, instead use:

less < /tmp/passwd


The httpd seems not to be able to read from pipes, nor can anonymous FTP
retrieve the contents of a pipe as if it were a text file.

Only one external process can attach to a given pipe at a time. If a
second request is made to read from the pipe while the program on the
pipe is still executing from an earlier request, the second request will
receive a blank file and exit. There is a two second reset time after the
pipe closes before it will open for a following read. If a request comes
in during that interval, it will block during the reset time, and then
receive the output from the assigned process as normal.


Several different pipes can be read at the same time. The above
restriction applies only to multiple reads from the same pipe.



Still to do:

There is a mechanism coded but commented out to cause the daemon to kill
any process it invokes which runs for longer than a certain time. This is
to keep it from running forever, preventing any further successful
attachments to the pipe. This isn't working yet because when the process
is killed it doesn't seem to be closing its file descriptors. As a
result, the external process keeps reading/writing the pipe, and the
daemon detects that after the reset interval and just starts the program
again.


======

This program is distributed under GPL.

Please send any suggestions, comments, or proposed code changes to the
author:

Christopher Neufeld      neufeld@physics.utoronto.ca
