summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--trans/fifo.c521
1 files changed, 521 insertions, 0 deletions
diff --git a/trans/fifo.c b/trans/fifo.c
new file mode 100644
index 00000000..e1cf096d
--- /dev/null
+++ b/trans/fifo.c
@@ -0,0 +1,521 @@
+/* A translator for fifos
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ 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 2, 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <cthreads.h>
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+#include <hurd/pipe.h>
+
+/* Global options. These defaults are the standard ones, I think... */
+int wait_for_reader = 1;
+int one_reader = 1;
+
+/* What kinds of pipes we use. */
+struct pipe_class *fifo_pipe_class;
+
+/* The current fifo that new opens will see, or NULL if there is none. */
+struct pipe *active_fifo = NULL;
+
+/* Lock this when changing ACTIVE_FIFO. */
+struct mutex active_fifo_lock;
+/* Signal this when ACTIVE_FIFO may have changed. */
+struct condition active_fifo_changed;
+
+/* ---------------------------------------------------------------- */
+
+#define USAGE "Usage: %s [OPTION...]\n"
+
+static void
+usage(int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ program_invocation_name);
+ else
+ {
+ printf(USAGE, program_invocation_name);
+ printf("\
+\n\
+ -r, --multiple-readers Allow multiple simultaneous readers\n\
+ -n, --noblock Don't block on open if there are no readers\n\
+ -d, --dgram Reads reflect write record boundaries\n\
+ --help Give this usage message\n\
+");
+ }
+
+ exit(status);
+}
+
+#define SHORT_OPTIONS "&"
+
+static struct option options[] =
+{
+ {"multiple-readers", no_argument, 0, 'r'},
+ {"noblock", no_argument, 0, 'n'},
+ {"dgram", no_argument, 0, 'd'},
+ {"help", no_argument, 0, '&'},
+ {0, 0, 0, 0}
+};
+
+/* ---------------------------------------------------------------- */
+
+struct port_bucket *port_bucket;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+void
+main (int argc, char **argv)
+{
+ int opt;
+ error_t err;
+ mach_port_t bootstrap;
+
+ fifo_pipe_class = stream_pipe_class;
+
+ while ((opt = getopt_long(argc, argv, SHORT_OPTIONS, options, 0)) != EOF)
+ switch (opt)
+ {
+ case 'r': one_reader = 0; break;
+ case 'n': wait_for_reader = 0; break;
+ case 'd': fifo_pipe_class = seqpack_pipe_class;
+ case '&': usage(0);
+ default: usage(1);
+ }
+
+ port_bucket = ports_create_bucket ();
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+
+ if (argc != 1)
+ {
+ fprintf(stderr, "Usage: %s", program_invocation_name);
+ exit(1);
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(1, 0, "must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup(bootstrap,
+ trivfs_cntl_portclasses[0], port_bucket,
+ trivfs_protid_portclasses[0], port_bucket,
+ NULL);
+ if (err)
+ error(3, err, "Contacting parent");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (port_bucket,
+ trivfs_demuxer,
+ 0, 0, 0, MACH_PORT_NULL);
+
+ exit(0);
+}
+
+/* ---------------------------------------------------------------- */
+
+static error_t
+open_hook (struct trivfs_peropen *po)
+{
+ error_t err = 0;
+ int flags = po->openmodes;
+
+ mutex_lock (&active_fifo_lock);
+
+ if (flags & O_READ)
+ /* When opening for read, what we do depends on what mode this server is
+ running in. The default (if ONE_READER is set) is to only allow one
+ reader at a time, with additional opens for read blocking here until
+ the old reader goes away; otherwise, we allow multiple readers. */
+ {
+ if (one_reader)
+ /* Wait until there isn't any active fifo, so we can make one. */
+ while (active_fifo != NULL || active_fifo->readers > 0)
+ if (flags & O_NONBLOCK)
+ {
+ err = EWOULDBLOCK;
+ break;
+ }
+ else
+ condition_wait (&active_fifo_changed, &active_fifo_lock);
+
+ if (!err && active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ err = pipe_create (fifo_pipe_class, &active_fifo);
+ if (!err)
+ {
+ pipe_add_reader (active_fifo);
+ condition_signal (&active_fifo_changed);
+ /* We'll unlock ACTIVE_FIFO_LOCK below; the writer code won't make
+ us block because we've ensured that there's a reader for it. */
+ }
+ }
+
+ if (!err && (flags & O_WRITE))
+ /* Open the active_fifo for writing. If WAIT_FOR_READER is true, then we
+ block until there's someone to read what we wrote, otherwise, if
+ there's no fifo, we create one, which we just write into and leave it
+ for someone to read later. */
+ {
+ if (wait_for_reader)
+ /* Wait until there's a fifo to write to. */
+ while (active_fifo == NULL || active_fifo->readers == 0)
+ if (flags & O_NONBLOCK)
+ {
+ err = EWOULDBLOCK;
+ break;
+ }
+ else
+ condition_wait (&active_fifo_changed, &active_fifo_lock);
+
+ if (!err && active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ err = pipe_create (fifo_pipe_class, &active_fifo);
+ if (!err)
+ pipe_add_writer (active_fifo);
+ }
+
+ po->hook = active_fifo;
+
+ mutex_unlock (&active_fifo_lock);
+
+ return err;
+}
+
+static void
+close_hook (struct trivfs_peropen *po)
+{
+ int was_active, going_away = 0;
+ int flags = po->openmodes;
+ struct pipe *pipe = po->hook;
+
+ mutex_lock (&active_fifo_lock);
+ was_active = (active_fifo == pipe);
+
+ if (was_active)
+ {
+ /* We're the last reader; when we're gone there is no more joy. */
+ going_away = ((flags & O_READ) && pipe->readers == 1);
+ }
+ else
+ /* Let others have their fun. */
+ mutex_unlock (&active_fifo_lock);
+
+ if (flags & O_READ)
+ pipe_remove_reader (pipe);
+ if (flags & O_WRITE)
+ pipe_remove_writer (pipe);
+ /* At this point, PIPE may be gone, so we can't look at it again. */
+
+ if (was_active)
+ {
+ if (going_away)
+ active_fifo = NULL;
+ condition_signal (&active_fifo_changed);
+ mutex_unlock (&active_fifo_lock);
+ }
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) = open_hook;
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+void
+trivfs_modify_stat (struct stat *st)
+{
+ st->st_size = 0;
+ st->st_blocks = 0;
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFIFO;
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+}
+
+error_t
+trivfs_goaway (int flags, mach_port_t realnode,
+ struct port_class *control_class,
+ struct port_class *protid_class)
+{
+ exit(0);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+error_t
+trivfs_S_io_map(struct trivfs_protid *cred,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EINVAL;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, size_t *data_len,
+ off_t offs, size_t amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ mutex_lock (&pipe->lock);
+ err = pipe_read (pipe, cred->po->openmodes & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ mutex_lock (&pipe->lock);
+ *amount = pipe_readable (pipe, 1);
+ mutex_unlock (&pipe->lock);
+ err = 0;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return ESPIPE;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *type, int *tag)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+ || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+ return EBADF;
+ else
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, size_t data_len,
+ off_t offs, size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ mutex_lock (&pipe->lock);
+ err = pipe_write (pipe, NULL, data, data_len, amount);
+ mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Truncate file. */
+error_t
+trivfs_S_file_truncate (struct trivfs_protid *cred, off_t size)
+{
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *owner = 0;
+ return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return EINVAL;
+}