diff options
author | Michael I. Bushnell <mib@gnu.org> | 1996-04-15 16:50:17 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1996-04-15 16:50:17 +0000 |
commit | 7b5a9ce49806d354d86298c50b6cacd16e49279a (patch) | |
tree | 9d9519717368064c74ad10276ff55490b6243015 | |
parent | 039684585136d2f39f18c849039bf04be638a4b3 (diff) |
Initial revision
-rw-r--r-- | trans/new-fifo.c | 810 |
1 files changed, 810 insertions, 0 deletions
diff --git a/trans/new-fifo.c b/trans/new-fifo.c new file mode 100644 index 00000000..44dd5afc --- /dev/null +++ b/trans/new-fifo.c @@ -0,0 +1,810 @@ +/* A translator for fifos + + Copyright (C) 1995, 1996 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 <argp.h> +#include <unistd.h> +#include <error.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> + +#include <cthreads.h> +#include <hurd.h> +#include <argz.h> +#include <hurd/fshelp.h> +#include <hurd/ports.h> +#include <hurd/trivfs.h> +#include <hurd/fsys.h> +#include <hurd/pipe.h> +#include <hurd/paths.h> + +#define DEFAULT_SERVER _SERVERS "fifo"; + +struct port_bucket *port_bucket; +struct port_class *fifo_port_class, *server_port_class, *fsys_port_class; + +/* ---------------------------------------------------------------- */ + +static const struct argp_option options[] = +{ + {"multiple-readers", 'r', 0, 0, "Allow multiple simultaneous readers"}, + {"noblock", 'n', 0, 0, "Don't block on open"}, + {"dgram", 'd', 0, 0, "Reflect write record boundaries"}, + {"server", 's', 0, 0, "Operate in server mode"}, + {"standalone", 'S', 0, 0, "Don't attempt to use a fifo server"}, + {"use-server", 'U', "NAME",0, "Attempt use server NAME"}, + {0,0} +}; + +/* Per translator variables. */ +struct fifo_trans +{ + /* True if this not a real translator, but instead a translator server, + which responds to requests to create translators. */ + int server; + + /* True if opens for writing should hang until there's a reader. */ + int wait_for_reader; + /* True if opens for reading should hang until there's a writer. */ + int wait_for_writer; + /* True if opens for read should hang until there are no other readers. */ + int one_reader; + + /* If non-null, the name of a fifo server to do the translation in our + stead. */ + char *use_server; + + /* The translator from which this was initialized. */ + struct fifo_trans *parent; + + /* 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; + /* Lock this when changing ACTIVE_FIFO. */ + struct mutex active_fifo_lock; + /* Signal this when ACTIVE_FIFO may have changed. */ + struct condition active_fifo_changed; +}; + +/* Return a new FIFO_TRANS in TRANS, initializing it from FROM if it's + non-null, where possible. */ +static void +fifo_trans_create (struct fifo_trans *from, struct fifo_trans **trans) +{ + struct fifo_trans *new = malloc (sizeof (struct fifo_trans)); + + new->server = 0; + mutex_init (&new->active_fifo_lock); + condition_init (&new->active_fifo_changed); + + new->parent = from; + + if (from) + /* Inherit things that can be inherited. */ + { + new->wait_for_reader = from->wait_for_reader; + new->wait_for_writer = from->wait_for_writer; + new->one_reader = from->one_reader; + new->use_server = from->use_server; + new->fifo_pipe_class = from->fifo_pipe_class; + } + else + /* Otherwise just use default values. */ + { + new->wait_for_reader = 1; + new->wait_for_writer = 1; + new->one_reader = 1; + new->use_server = DEFAULT_SERVER; + new->fifo_pipe_class = stream_pipe_class; + } + + *trans = new; +} + +static void +fifo_trans_free (struct fifo_trans *trans) +{ + free (trans); +} + +static error_t +fifo_trans_start (struct fifo_trans *trans, mach_port_t requestor) +{ + struct trivfs_control *control; + struct port_class *class = + (trans->server ? server_port_class : fifo_port_class); + error_t + err = trivfs_startup (requestor, 0, + fsys_port_class, port_bucket, class, port_bucket, + &control); + if (!err) + control->hook = trans; + return err; +} + +/* Parse our options. SERVER is true if we are a fifo server providing + service for others (in which case we don't print error messages). The + results of the parse are put into TRANS. */ +static error_t +fifo_trans_parse_args (struct fifo_trans *trans, int argc, char **argv, + int print_errs) +{ + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case 'r': trans->one_reader = 0; break; + case 'n': trans->wait_for_reader = trans->wait_for_writer = 0; break; + case 'd': trans->fifo_pipe_class = seqpack_pipe_class; + case 's': trans->server = 1; break; + case 'U': trans->use_server = arg; break; + case 'S': trans->use_server = 0; break; + default: return EINVAL; + } + return 0; + } + struct argp argp = {options, parse_opt}; + return argp_parse (&argp, argc, argv, print_errs ? 0 : ARGP_SILENT, 0); +} + +/* ---------------------------------------------------------------- */ + +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) +{ + error_t err; + mach_port_t bootstrap; + struct fifo_trans *trans; + /* Clean up a fsys control node. */ + void clean_fsys (void *vfsys) + { + struct trivfs_control *fsys = vfsys; + if (fsys->hook) + fifo_trans_free (fsys->hook); + trivfs_clean_cntl (fsys); + } + + fifo_trans_create (0, &trans); + + if (fifo_trans_parse_args (trans, argc, argv, 1) != 0) + exit (1); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error(1, 0, "must be started as a translator"); + + if (!trans->server && trans->use_server) + /* Attempt to contact a fifo server to do our work for us. */ + { + err = fshelp_delegate_translation (trans->use_server, bootstrap, argv); + if (!err) + exit (0); + } + + port_bucket = ports_create_bucket (); + fifo_port_class = ports_create_class (trivfs_clean_protid, 0); + server_port_class = ports_create_class (trivfs_clean_protid, 0); + fsys_port_class = ports_create_class (clean_fsys, 0); + + trivfs_protid_portclasses[0] = fifo_port_class; + trivfs_protid_portclasses[1] = server_port_class; + trivfs_cntl_portclasses[0] = fsys_port_class; + + /* Reply to our parent */ + fifo_trans_start (trans, bootstrap); + + /* Launch. */ + do + { + ports_enable_class (fifo_port_class); + ports_manage_port_operations_multithread (port_bucket, + trivfs_demuxer, + 30*1000, 5*60*1000, 0, 0); + } + while (ports_count_class (fifo_port_class) > 0); + + exit (0); +} + +/* ---------------------------------------------------------------- */ + +static error_t +fifo_trans_open (struct fifo_trans *trans, int flags, void **hook) +{ + error_t err = 0; + + if (flags & (O_READ | O_WRITE)) + { + mutex_lock (&trans->active_fifo_lock); + +/* Wait until the active fifo has changed so that CONDITION is true. */ +#define WAIT(condition, noblock_err) \ + while (!err && !(condition)) \ + if (flags & O_NONBLOCK) \ + { \ + err = noblock_err; \ + break; \ + } \ + else if (hurd_condition_wait (&trans->active_fifo_changed, \ + &trans->active_fifo_lock)) \ + err = EINTR; + + 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 WAIT_FOR_WRITER is true, then once we've + created a fifo, we also block until someone opens it for writing; + otherwise, the first read will block until someone writes + something. */ + { + if (trans->one_reader) + /* Wait until there isn't any active fifo, so we can make one. */ + WAIT (!trans->active_fifo || !trans->active_fifo->readers, + EWOULDBLOCK); + if (!err && trans->active_fifo == NULL) + /* No other readers, and indeed, no fifo; make one. */ + err = pipe_create (trans->fifo_pipe_class, &trans->active_fifo); + if (!err) + { + pipe_add_reader (trans->active_fifo); + condition_broadcast (&trans->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 (trans->wait_for_writer) + /* Wait until there's a writer. */ + { + WAIT (trans->active_fifo->writers, 0); + if (err) + /* Back out the new pipe creation. */ + { + pipe_remove_reader (trans->active_fifo); + trans->active_fifo = NULL; + condition_broadcast (&trans->active_fifo_changed); + } + } + else + /* Otherwise prevent an immediate eof. */ + trans->active_fifo->flags &= ~PIPE_BROKEN; + } + } + + if (!err && (flags & O_WRITE)) + /* Open the trans->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 (trans->wait_for_reader) + /* Wait until there's a fifo to write to. */ + WAIT (trans->active_fifo && trans->active_fifo->readers, 0); + if (!err && trans->active_fifo == NULL) + /* No other readers, and indeed, no fifo; make one. */ + { + err = pipe_create (trans->fifo_pipe_class, &trans->active_fifo); + if (!err) + trans->active_fifo->flags &= ~PIPE_BROKEN; + } + if (!err) + { + pipe_add_writer (trans->active_fifo); + condition_broadcast (&trans->active_fifo_changed); + } + } + + *hook = trans->active_fifo; + } + + mutex_unlock (&trans->active_fifo_lock); + + return err; +} + +static void +fifo_trans_close (struct fifo_trans *trans, struct trivfs_peropen *po) +{ + int was_active, going_away = 0; + int flags = po->openmodes; + struct pipe *pipe = po->hook; + + if (!pipe) + return; + + mutex_lock (&trans->active_fifo_lock); + was_active = (trans->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 (&trans->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) + trans->active_fifo = NULL; + condition_broadcast (&trans->active_fifo_changed); + mutex_unlock (&trans->active_fifo_lock); + } +} + +static error_t +open_hook (struct trivfs_peropen *po) +{ + struct fifo_trans *trans = po->cntl->hook; + + if (! trans->server) + /* We're opening a normal fifo node. */ + return fifo_trans_open (trans, po->openmodes, &po->hook); + else if (po->openmodes & (O_READ|O_WRITE|O_APPEND)) + return EPERM; + else + /* We're a fifo server serving a new fifo. */ + return 0; +} + +static void +close_hook (struct trivfs_peropen *po) +{ + struct fifo_trans *trans = po->cntl->hook; + + if (! trans->server) + /* We're closing a normal fifo node. */ + fifo_trans_close (trans, po); +} + +/* 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 trivfs_protid *cred, struct stat *st) +{ + struct fifo_trans *trans = cred->po->cntl->hook; + if (! trans->server) + /* A fifo node */ + { + struct pipe *pipe = cred->po->hook; + + st->st_mode &= ~S_IFMT; + st->st_mode |= S_IFIFO; + + if (pipe) + { + mutex_lock (&pipe->lock); + st->st_size = pipe_readable (pipe, 1); + st->st_blocks = st->st_size >> 9; + mutex_unlock (&pipe->lock); + } + else + st->st_size = st->st_blocks = 0; + + /* As we try to be clever with large transfers, ask for them. */ + st->st_blksize = vm_page_size * 16; + } +} + +error_t +trivfs_goaway (struct trivfs_control *fsys, int flags) +{ + error_t err; + int num_opens; + int force = flags & FSYS_GOAWAY_FORCE; + int unlink = flags & FSYS_GOAWAY_UNLINK; + struct fifo_trans *trans = fsys->hook; + + err = ports_inhibit_port_rpcs (fsys); + if (err == EINTR || (err && !force)) + return err; + + num_opens = ports_count_class (fsys->protid_class); + if (num_opens > 0 && !force && !unlink) + /* Still some opens, and we're not being forced to go away, so don't.*/ + { + ports_enable_class (fsys->protid_class); + ports_resume_port_rpcs (fsys); + return EBUSY; + } + + /* Kill the control connection. */ + mach_port_deallocate (mach_task_self (), fsys->underlying); + fsys->underlying = MACH_PORT_NULL; + ports_destroy_right (fsys); + + if (force) + /* Kill opens. */ + { + error_t maybe_trash_protid (void *vcred) + { + struct trivfs_protid *cred = vcred; + if (cred->po->cntl == fsys) + { + ports_destroy_right (cred); + ports_interrupt_rpcs (cred); + } + return 0; + } + ports_bucket_iterate (((struct port_info *)fsys)->bucket, + maybe_trash_protid); + } + + if (! trans->parent) + /* The root translator, go away, bye bye. */ + exit (0); + + /* Let things continue; what should die will. */ + ports_enable_class (fsys->protid_class); + ports_resume_port_rpcs (fsys); + + return 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; + assert (pipe); + 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; + assert (pipe); + 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 *select_type, int *tag) +{ + struct pipe *pipe; + error_t err = 0; + int ready = 0; + + if (!cred) + return EOPNOTSUPP; + + pipe = cred->po->hook; + + if (*select_type & SELECT_READ) + if (cred->po->openmodes & O_READ) + { + mutex_lock (&pipe->lock); + if (pipe_wait_readable (pipe, 1, 1) != EWOULDBLOCK) + ready |= SELECT_READ; /* Data immediately readable (or error). */ + mutex_unlock (&pipe->lock); + } + else + ready |= SELECT_READ; /* Error immediately available... */ + + if (*select_type & SELECT_WRITE) + if (cred->po->openmodes & O_WRITE) + { + mutex_lock (&pipe->lock); + if (pipe_wait_writable (pipe, 1) != EWOULDBLOCK) + ready |= SELECT_WRITE; /* Data immediately writable (or error). */ + mutex_unlock (&pipe->lock); + } + else + ready |= SELECT_WRITE; /* Error immediately available... */ + + if (ready) + *select_type = ready; + else + /* Wait for something to change. */ + { + ports_interrupt_self_on_port_death (cred, reply); + err = pipe_pair_select (pipe, pipe, select_type, 1); + } + + return err; +} + +/* ---------------------------------------------------------------- */ + +/* 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, cred->po->openmodes & O_NONBLOCK, NULL, + data, data_len, amount); + mutex_unlock (&pipe->lock); + } + + return err; +} + +/* ---------------------------------------------------------------- */ + +/* Truncate file. */ +error_t +trivfs_S_file_set_size (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + off_t size) +{ + return size == 0 ? 0 : EINVAL; +} + +/* ---------------------------------------------------------------- */ +/* 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; +} + +/* ---------------------------------------------------------------- */ + +/* Ask SERVER to provide fsys translation service for us. REQUESTOR is + the bootstrap port supplied to the original translator, and ARGV are + the command line arguments. If the recipient accepts the request, he + (or some delegate) should send fsys_startup to REQUESTOR to start the + filesystem up. */ +error_t +trivfs_S_fsys_forward (mach_port_t server, + mach_port_t reply, + mach_msg_type_name_t replytype, + mach_port_t requestor, + char *argz, size_t argz_len) +{ + error_t err; + struct fifo_trans *server_trans, *trans; + int argc = argz_count (argz, argz_len); + char **argv = alloca (sizeof (char *) * (argc + 1)); + /* SERVER should be our root node. */ + struct trivfs_protid *cred = + ports_lookup_port (port_bucket, server, server_port_class); + + if (!cred) + return EOPNOTSUPP; + + server_trans = cred->po->cntl->hook; + assert (server_trans->server); + + argz_extract (argz, argz_len, argv); + + /* Make a new translator, inheriting from its server. */ + fifo_trans_create (server_trans, &trans); + + /* Parse the new arguments to change the defaults. */ + err = fifo_trans_parse_args (trans, argc, argv, 0); + + if (!err) + /* Set our new translator along it's merry way... */ + fifo_trans_start (trans, requestor); + + ports_port_deref (cred); + + return err; +} |