aboutsummaryrefslogtreecommitdiff
path: root/pflocal
diff options
context:
space:
mode:
Diffstat (limited to 'pflocal')
-rw-r--r--pflocal/ChangeLog305
-rw-r--r--pflocal/Makefile33
-rw-r--r--pflocal/connq.c275
-rw-r--r--pflocal/connq.h58
-rw-r--r--pflocal/io.c622
-rw-r--r--pflocal/mig-decls.h59
-rw-r--r--pflocal/mig-mutate.h33
-rw-r--r--pflocal/pf.c123
-rw-r--r--pflocal/pflocal.c130
-rw-r--r--pflocal/sock.c504
-rw-r--r--pflocal/sock.h161
-rw-r--r--pflocal/socket.c425
-rw-r--r--pflocal/sserver.c84
-rw-r--r--pflocal/sserver.h33
14 files changed, 2845 insertions, 0 deletions
diff --git a/pflocal/ChangeLog b/pflocal/ChangeLog
new file mode 100644
index 00000000..c7c36fc0
--- /dev/null
+++ b/pflocal/ChangeLog
@@ -0,0 +1,305 @@
+1999-09-13 Roland McGrath <roland@baalperazim.frob.com>
+
+ * io.c: Reverted changes related to io_map_segment.
+
+1999-09-07 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * io.c (S_io_map_segment): New function.
+
+1999-07-11 Roland McGrath <roland@baalperazim.frob.com>
+
+ * sock.h: Add #include <sys/mman.h>.
+
+1999-07-10 Roland McGrath <roland@baalperazim.frob.com>
+
+ * io.c: Add #include <sys/mman.h> for munmap decl.
+
+1999-07-09 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * pf.c (S_socket_whatis_address): Use mmap instead of vm_allocate.
+
+1999-07-03 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * io.c (TRASH_IDS): Use munmap instead of vm_deallocate.
+
+1999-03-01 Mark Kettenis <kettenis@gnu.org>
+
+ * pf.c (S_socket_whatis_address): Implement. Since we cannot tell
+ what our adress is, return an empty string as the file name.
+
+Tue Feb 16 05:52:35 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * io.c (S_io_revoke): New function. Don't attempt to implement.
+ (Other systems don't even permit non-file revokes.)
+
+1998-10-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * io.c (S_io_select): Add braces to silence gcc warning.
+
+1998-07-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * pflocal.c (main): Fix return type to int, and use return.
+
+Wed Aug 20 14:06:11 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * pflocal.c (main): New args for
+ ports_manage_port_operations_multithread.
+ * sserver.c (handle_sock_requests): Likewise.
+
+Mon Oct 7 12:53:24 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * connq.c (connq_listen): Unlock CQ->lock when returning EWOULDBLOCK.
+
+Thu Sep 12 16:43:09 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): New variable.
+ (pflocal): Omit special dependency.
+
+Tue Jul 23 19:44:29 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sock.c (sock_create): Remove NEXT_SOCK_ID.
+
+Sat Jul 13 20:20:55 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * io.c (S_io_reauthenticate): Repeat sock_create_port and
+ auth_server_authenticate for as long as we get EINTR.
+
+Sun Jul 7 21:30:33 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * io.c (S_io_reauthenticate): Don't use unsafe MOVE_SEND in call
+ to auth_server_authenticate.
+
+Mon Jul 1 18:45:35 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sock.c (sock_create): Initialize ID field to MACH_PORT_NULL.
+
+Thu Jun 27 17:58:09 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (LCLHDRS): Add sserver.h.
+
+Thu Jun 20 16:33:06 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (pflocal): Depend on ../libfshelp/libfshelp.a.
+
+Wed May 15 20:27:38 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sock.c (sock_free): Destroy SOCK's id port if necessary.
+
+Tue May 14 14:05:33 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * io.c (S_io_identity): New function.
+ * sock.h (struct sock): Make the id field a receive right, not an int.
+
+Thu May 9 20:20:20 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * io.c (S_io_reauthenticate): Use new auth_server_authenticate
+ protocol.
+
+Thu May 9 12:14:37 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * io.c (S_io_select): Remove TAG arg.
+
+Mon Apr 15 12:52:32 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (MIGSFLAGS): Look for mig-mutate.h in $(srcdir).
+
+Fri Jan 26 16:46:37 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * socket.c (S_socket_recv): Test for MSG_OOB in IN_FLAGS, not FLAGS.
+ Return EINVAL if we get MSG_OOB, not EOPNOTSUPP.
+
+Thu Jan 25 17:34:50 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sock.c (sock_create_port, addr_create): Use ports_create_port
+ instead of ports_allocate_port.
+ * pflocal.c (trivfs_goaway): Handle errors from
+ ports_inhibit_bucket_rpcs.
+ (thread_cancel): Function deleted.
+
+Tue Jan 23 21:31:40 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * socket.c (S_socket_connect): Handle connectionless protocols
+ correctly.
+
+ * socket.c (S_socket_send): Allow DEST_ADDR to be null if the
+ socket is connected.
+
+ * sock.c (sock_bind): Don't change SOCK's ref count if we're
+ returning an error.
+
+Thu Jan 4 15:44:13 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * io.c (S_io_select): Reworked to avoid calling
+ ports_interrupt_self_on_port_death() if there's data immediately
+ available. Also, don't block if we can return EOF/EPIPE.
+
+Thu Dec 28 13:46:32 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * io.c (S_io_select): Use handy macro to avoid unthinkable line break.
+
+Tue Dec 26 17:30:18 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * io.c (S_io_select): Add reply port parameter, and request
+ notification if it dies.
+ * mig-mutate.h (IO_SELECT_REPLY_PORT): New def.
+
+Mon Nov 13 14:03:03 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * socket.c (S_socket_bind, S_socket_connect): Drop ADDR's send right.
+
+Thu Nov 9 13:18:44 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * socket.c (S_socket_connect): Drop our reference to ADDR.
+
+Sun Nov 5 10:01:15 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * pf.c (S_socket_create_address): Removing BINDING argument.
+
+ * pflocal.c (main): Add FLAGS argument to trivfs_startup call.
+
+Tue Sep 19 14:07:24 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * io.c (S_io_pathconf): New function.
+ (S_io_set_all_openmodes, S_io_set_some_openmodes,
+ S_io_clear_some_openmodes): The user specifies O_NONBLOCK, not
+ SOCK_NONBLOCK.
+ (S_io_get_openmodes): Always return O_APPEND.
+
+Wed Sep 6 11:53:48 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * sserver.c (sock_demuxer): Use ports_interrupt_server and
+ ports_notify_server instead of our own version.
+ (do_mach_notify_no_senders, do_mach_notify_port_deleted,
+ do_mach_notify_msg_accepted, do_mach_notify_port_destroyed,
+ do_mach_notify_port_deleted, do_mach_notify_send_once,
+ do_mach_notify_dead_name): Functions deleted.
+ * io.c (S_interrupt_operation): Function deleted.
+ * Makefile (MIGSTUBS): Remove notifyServer.o and interruptServer.o.
+
+ * io.c (S_io_read, S_io_readable): Don't return EPIPE on EOF.
+
+Tue Sep 5 14:22:18 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * io.c (S_io_stat): Only attempt to use the read pipe if it exists.
+
+Thu Aug 31 16:31:18 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * io.c (S_io_select): Change the way selects are done, now that
+ writes can block.
+ (S_io_write): Pass in the new NOBLOCK parameter to pipe_write.
+ * socket.c (S_socket_send): Pass in the new NOBLOCK parameter to
+ pipe_send.
+
+Tue Aug 29 14:33:14 1995 Miles Bader <miles@geech.gnu.ai.mit.edu>
+
+ * io.c (S_io_select): Use pipe_select instead of pipe_wait.
+
+ * connq.c (struct connq): Remove interrupt_seq_num field.
+ (connq_listen): Use hurd_condition_wait to detect interrupts
+ instead of previous ad-hoc mechanism.
+ (connq_interrupt, connq_interrupt_sock): Functions deleted.
+ * connq.h (connq_interrupt, connq_interrupt_sock): Decls deleted.
+ * io.c (S_interrupt_operation): Use ports_interrupt_rpc to
+ interrupt waiting threads.
+
+ * sock.c (sock_acquire_read_pipe, sock_acquire_write_pipe):
+ `aquire' -> `acquire'.
+ * socket.c (S_socket_send, S_socket_recv): Ditto.
+ * sock.h: Ditto.
+
+Tue Aug 29 14:30:59 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * io.c (S_io_select): Fix typo in masking off SELECT_URG.
+ Don't check open modes and return EBADF.
+
+Thu Aug 24 10:35:58 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * Makefile (pflocal): Put all dependencies here.
+ (OBJS): Remove error.o.
+ (HURDLIBS): Removed.
+ Removed all rules dealing with error.o.
+
+Mon Aug 21 16:37:32 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * pflocal.c (trivfs_goaway, trivfs_modify_stat): Update arguments.
+
+Fri Aug 11 15:33:28 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * sock.h (struct sock): Store the pipe class in a separate field,
+ as READ_PIPE is no longer always defined.
+ * sock.c (sock_create, sock_connect): Set/use the PIPE_CLASS field.
+ (sock_connect, sock_aquire_write_pipe): Use pipe_aquire_writer
+ instead of pipe_aquire.
+ (sock_aquire_read_pipe): Use pipe_aquire_reader instead of
+ pipe_aquire. Handle the case where there is no read pipe (in
+ which case return EPIPE).
+ (sock_shutdown): Make shutting down the read half just like the
+ write half -- the pipe goes away...
+ (sock_create): Don't bump the read pipe ref count ourself.
+ (sock_free): Use sock_shutdown to trash the read pipe too.
+
+ * socket.c
+ (S_socket_recv): Use pipe_release_reader instead of pipe_release.
+ (S_socket_send): Use pipe_release_writer instead of pipe_release.
+ (S_socket_recv): Reflect EPIPE as EOF.
+
+ * io.c (S_io_read, S_interrupt_operation, S_io_readable, S_io_select):
+ Use pipe_release_reader instead of pipe_release.
+ (S_io_write): Use pipe_release_writer instead of pipe_release.
+ (S_io_readable, S_io_read): Reflect EPIPE as EOF.
+
+Mon Jul 31 13:59:15 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * connq.c (connq_compress): New function.
+ (connq_interrupt_sock): Use connq_compress to compress the queue.
+
+Sun Jul 30 10:30:24 1995 Miles Bader <miles@duality.gnu.ai.mit.edu>
+
+ * connq.c (connq_interrupt_sock): Reset CQ's tail to the end of
+ the compressed queue.
+
+Sat Jul 29 00:00:57 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * socket.c (S_socket_send): Only free SOURCE_ADDR if the send
+ fails, as otherwise it's consumed; also free all the ports in
+ PORTS if the send fails.
+
+ * io.c (S_interrupt_operation): Allow socket trying to connect to
+ be interrupted.
+ * connq.c (connq_interrupt_sock): New function.
+ * socket.c (S_socket_connect): Use the CONNECT_QUEUE field to
+ allow only a single connection attempt at once.
+ Check for already-connected sockets here instead of waiting for
+ the final rendezvous.
+ * connq.h (connq_interrupt_sock): New declaration.
+
+ * connq.c (connq_listen, connq_connect, connq_interrupt,
+ connq_set_length): Reverse the roles of the HEAD and TAIL fields,
+ and make sure they're used correctly.
+ (qprev): Deleted function.
+
+ * sock.h (struct sock, all uses changed): Add the CONNECT_QUEUE
+ field, and rename the CONNQ field to LISTEN_QUEUE.
+ * sock.c (sock_create): Initialize the CONNECT_QUEUE field and
+ rename CONNQ to LISTEN_QUEUE.
+
+ * connq.c (connq_set_length): When shrinking the queue, actually
+ do so, and don't leak memory.
+
+ * socket.c (S_socket_connect): Return ECONNREFUSED when trying to
+ connect to a non-existant address, instead of EADDRNOTAVAIL.
+
+ * connq.c (struct connq): Add the INTERRUPT_SEQ_NUM field, used to
+ detect interupts.
+ (connq_listen): Detect when we get interrupted, and return EINTR.
+ (connq_interrupt): New function.
+ * connq.h (connq_interrupt): New declaration.
+ * io.c (S_interrupt_operation): Call connq_interrupt when appropiate.
+
+ * connq.c (connq_connect): Initialize REQ before using it.
+ (connq_request_init): Swap the arguments.
+ (connq_listen): Don't lock the accepted request just to get its sock.
+
+ * socket.c (S_socket_connect): Actually use the connq operations
+ to connect, like the listening socket is expecting, instead of
+ connecting directly to it.
diff --git a/pflocal/Makefile b/pflocal/Makefile
new file mode 100644
index 00000000..d5768b32
--- /dev/null
+++ b/pflocal/Makefile
@@ -0,0 +1,33 @@
+# Makefile for pflocal
+#
+# Copyright (C) 1995, 1996 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 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.
+
+dir := pflocal
+makemode := server
+
+target = pflocal
+
+SRCS = connq.c io.c pflocal.c socket.c pf.c sock.c sserver.c
+LCLHDRS = connq.h sock.h mig-decls.h mig-mutate.h sserver.h
+
+MIGSTUBS = ioServer.o socketServer.o
+OBJS = $(SRCS:.c=.o) $(MIGSTUBS)
+HURDLIBS = pipe trivfs fshelp ports threads ihash shouldbeinlibc
+
+MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+include ../Makeconf
diff --git a/pflocal/connq.c b/pflocal/connq.c
new file mode 100644
index 00000000..862c9a14
--- /dev/null
+++ b/pflocal/connq.c
@@ -0,0 +1,275 @@
+/* Listen queue functions
+
+ 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 <cthreads.h>
+
+#include "connq.h"
+
+/* A queue for queueing incoming connections. */
+struct connq
+{
+ /* True if all connection requests should be treated as non-blocking. */
+ int noqueue;
+
+ /* The connection request queue. */
+ struct connq_request **queue;
+ unsigned length;
+ /* Head is the position in QUEUE of the first request, and TAIL is the
+ first free position in the queue. If HEAD == TAIL, then the queue is
+ empty. Starting at HEAD, successive positions can be calculated by
+ using qnext(). */
+ unsigned head, tail;
+
+ /* Threads that have done an accept on this queue wait on this condition. */
+ struct condition listeners;
+ unsigned num_listeners;
+
+ struct mutex lock;
+};
+
+/* Returns the position CQ's queue after POS. */
+static inline unsigned
+qnext (struct connq *cq, unsigned pos)
+{
+ return (pos + 1 == cq->length) ? 0 : pos + 1;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* A data block allocated by a thread waiting on a connq, which is used to
+ get information from and to the thread. */
+struct connq_request
+{
+ /* The socket that's waiting to connect. */
+ struct sock *sock;
+
+ /* What the waiting thread blocks on. */
+ struct condition signal;
+ struct mutex lock;
+
+ /* Set to true when this request has been dealt with, to guard against
+ spurious conditions being signaled. */
+ int completed;
+
+ /* After the waiting thread is unblocked, this is the result, either 0 if
+ SOCK has been connected, or an error. */
+ error_t err;
+};
+
+static inline void
+connq_request_init (struct connq_request *req, struct sock *sock)
+{
+ req->err = 0;
+ req->sock = sock;
+ req->completed = 0;
+ condition_init (&req->signal);
+ mutex_init (&req->lock);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Create a new listening queue, returning it in CQ. The resulting queue
+ will be of zero length, that is it won't allow connections unless someone
+ is already listening (change this with connq_set_length). */
+error_t
+connq_create (struct connq **cq)
+{
+ struct connq *new = malloc (sizeof (struct connq));
+
+ if (!new)
+ return ENOMEM;
+
+ new->noqueue = 1; /* By default, don't queue requests. */
+ new->length = 0;
+ new->head = new->tail = 0;
+ new->queue = NULL;
+ new->num_listeners = 0;
+
+ mutex_init (&new->lock);
+ condition_init (&new->listeners);
+
+ *cq = new;
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Wait for a connection attempt to be made on CQ, and return the connecting
+ socket in SOCK, and a request tag in REQ. If REQ is NULL, the request is
+ left in the queue, otherwise connq_request_complete must be called on REQ
+ to allow the requesting thread to continue. If NOBLOCK is true,
+ EWOULDBLOCK is returned when there are no immediate connections
+ available. */
+error_t
+connq_listen (struct connq *cq, int noblock,
+ struct connq_request **req, struct sock **sock)
+{
+ mutex_lock (&cq->lock);
+
+ if (noblock && cq->head == cq->tail)
+ {
+ mutex_unlock (&cq->lock);
+ return EWOULDBLOCK;
+ }
+
+ cq->num_listeners++;
+
+ while (cq->head == cq->tail)
+ if (hurd_condition_wait (&cq->listeners, &cq->lock))
+ {
+ cq->num_listeners--;
+ mutex_unlock (&cq->lock);
+ return EINTR;
+ }
+
+ if (req != NULL)
+ /* Dequeue the next request, if desired. */
+ {
+ *req = cq->queue[cq->head];
+ cq->head = qnext (cq, cq->head);
+ if (sock != NULL)
+ *sock = (*req)->sock;
+ }
+
+ cq->num_listeners--;
+
+ mutex_unlock (&cq->lock);
+
+ return 0;
+}
+
+/* Return the error code ERR to the thread that made the listen request REQ,
+ returned from a previous connq_listen. */
+void
+connq_request_complete (struct connq_request *req, error_t err)
+{
+ mutex_lock (&req->lock);
+ req->err = err;
+ req->completed = 1;
+ condition_signal (&req->signal);
+ mutex_unlock (&req->lock);
+}
+
+/* Try to connect SOCK with the socket listening on CQ. If NOBLOCK is true,
+ then return EWOULDBLOCK immediately when there are no immediate
+ connections available. Neither SOCK nor CQ should be locked. */
+error_t
+connq_connect (struct connq *cq, int noblock, struct sock *sock)
+{
+ error_t err = 0;
+ unsigned next;
+
+ mutex_lock (&cq->lock);
+
+ /* Check for listeners after we've locked CQ for good. */
+ if ((noblock || cq->noqueue) && cq->num_listeners == 0)
+ {
+ mutex_unlock (&cq->lock);
+ return EWOULDBLOCK;
+ }
+
+ next = qnext (cq, cq->tail);
+ if (next == cq->tail)
+ /* The queue is full. */
+ err = ECONNREFUSED;
+ else
+ {
+ struct connq_request req;
+
+ connq_request_init (&req, sock);
+
+ cq->queue[cq->tail] = &req;
+ cq->tail = next;
+
+ /* Hold REQ.LOCK before we signal the condition so that we're sure
+ to be woken up. */
+ mutex_lock (&req.lock);
+ condition_signal (&cq->listeners);
+ mutex_unlock (&cq->lock);
+
+ while (!req.completed)
+ condition_wait (&req.signal, &req.lock);
+ err = req.err;
+
+ mutex_unlock (&req.lock);
+ }
+
+ return err;
+}
+
+/* `Compresses' CQ, by removing any NULL entries. CQ should be locked. */
+static void
+connq_compress (struct connq *cq)
+{
+ unsigned pos;
+ unsigned comp_tail = cq->head;
+
+ /* Now compress the queue to remove any null entries we put in. */
+ for (pos = cq->head; pos != cq->tail; pos = qnext (cq, pos))
+ if (cq->queue[pos] != NULL)
+ /* This position has a non-NULL request, so move it to the end of the
+ compressed queue. */
+ {
+ cq->queue[comp_tail] = cq->queue[pos];
+ comp_tail = qnext (cq, comp_tail);
+ }
+
+ /* Move back tail to only include what we kept in the queue. */
+ cq->tail = comp_tail;
+}
+
+/* Set CQ's queue length to LENGTH. Any sockets already waiting for a
+ connections that are past the new length will fail with ECONNREFUSED. */
+error_t
+connq_set_length (struct connq *cq, int length)
+{
+ mutex_lock (&cq->lock);
+
+ if (length > cq->length)
+ /* Growing the queue is simple... */
+ cq->queue = realloc (cq->queue, sizeof (struct connq_request *) * length);
+ else
+ /* Shrinking it less so. */
+ {
+ int i;
+ struct connq_request **new_queue =
+ malloc (sizeof (struct connq_request *) * length);
+
+ for (i = 0; i < cq->length && cq->head != cq->tail; i++)
+ {
+ if (i < length)
+ /* Keep this connect request in the queue. */
+ new_queue[length - i] = cq->queue[cq->head];
+ else
+ /* Punt this one. */
+ connq_request_complete (cq->queue[cq->head], ECONNREFUSED);
+ cq->head = qnext (cq, cq->head);
+ }
+
+ free (cq->queue);
+ cq->queue = new_queue;
+ }
+
+ cq->noqueue = 0; /* Turn on queueing. */
+
+ mutex_unlock (&cq->lock);
+
+ return 0;
+}
diff --git a/pflocal/connq.h b/pflocal/connq.h
new file mode 100644
index 00000000..8486eb17
--- /dev/null
+++ b/pflocal/connq.h
@@ -0,0 +1,58 @@
+/* Connection queues
+
+ 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. */
+
+#ifndef __CONNQ_H__
+#define __CONNQ_H__
+
+#include <errno.h>
+
+/* Unknown types */
+struct connq;
+struct connq_request;
+struct sock;
+
+/* Create a new listening queue, returning it in CQ. The resulting queue
+ will be of zero length, that is it won't allow connections unless someone
+ is already listening (change this with connq_set_length). */
+error_t connq_create (struct connq **cq);
+
+/* Wait for a connection attempt to be made on CQ, and return the connecting
+ socket in SOCK, and a request tag in REQ. If REQ is NULL, the request is
+ left in the queue, otherwise connq_request_complete must be called on REQ
+ to allow the requesting thread to continue. If NOBLOCK is true,
+ EWOULDBLOCK is returned when there are no immediate connections
+ available. CQ should be unlocked. */
+error_t connq_listen (struct connq *cq, int noblock,
+ struct connq_request **req, struct sock **sock);
+
+/* Return the error code ERR to the thread that made the listen request REQ,
+ returned from a previous connq_listen. */
+void connq_request_complete (struct connq_request *req, error_t err);
+
+/* Set CQ's queue length to LENGTH. Any sockets already waiting for a
+ connections that are past the new length will fail with ECONNREFUSED. */
+error_t connq_set_length (struct connq *cq, int length);
+
+/* Try to connect SOCK with the socket listening on CQ. If NOBLOCK is true,
+ then return EWOULDBLOCK immediately when there are no immediate
+ connections available. Neither SOCK nor CQ should be locked. */
+error_t connq_connect (struct connq *cq, int noblock, struct sock *sock);
+
+#endif /* __CONNQ_H__ */
diff --git a/pflocal/io.c b/pflocal/io.c
new file mode 100644
index 00000000..133c75c9
--- /dev/null
+++ b/pflocal/io.c
@@ -0,0 +1,622 @@
+/* Socket I/O operations
+
+ Copyright (C) 1995, 1996, 1998, 1999 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 <string.h> /* For bzero() */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <hurd.h> /* for getauth() */
+#include <hurd/hurd_types.h>
+#include <hurd/auth.h>
+#include <hurd/pipe.h>
+#include <mach/notify.h>
+
+#include "sock.h"
+#include "connq.h"
+#include "sserver.h"
+
+#include "io_S.h"
+#include "interrupt_S.h"
+
+/* 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 amount. */
+error_t
+S_io_read (struct sock_user *user,
+ char **data, mach_msg_type_number_t *data_len,
+ off_t offset, mach_msg_type_number_t amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ err = 0;
+ *data_len = 0;
+ }
+ else if (!err)
+ {
+ err =
+ pipe_read (pipe, user->sock->flags & SOCK_NONBLOCK, NULL,
+ data, data_len, amount);
+ pipe_release_reader (pipe);
+ }
+
+ 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
+S_io_write (struct sock_user *user,
+ char *data, mach_msg_type_number_t data_len,
+ off_t offset, mach_msg_type_number_t *amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_write_pipe (user->sock, &pipe);
+ if (!err)
+ {
+ struct addr *source_addr;
+
+ /* We could provide a source address for all writes, but we only do so
+ for connectionless sockets because that's the only place it's
+ required, and it's more efficient not to. */
+ if (pipe->class->flags & PIPE_CLASS_CONNECTIONLESS)
+ err = sock_get_addr (user->sock, &source_addr);
+ else
+ source_addr = NULL;
+
+ if (!err)
+ {
+ err = pipe_write (pipe, user->sock->flags & SOCK_NONBLOCK,
+ source_addr, data, data_len, amount);
+ if (source_addr)
+ ports_port_deref (source_addr);
+ }
+
+ pipe_release_writer (pipe);
+ }
+
+ 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
+S_io_readable (struct sock_user *user, mach_msg_type_number_t *amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ err = 0;
+ *amount = 0;
+ }
+ else if (!err)
+ {
+ *amount = pipe_readable (user->sock->read_pipe, 1);
+ pipe_release_reader (pipe);
+ }
+
+ return err;
+}
+
+/* Change current read/write offset */
+error_t
+S_io_seek (struct sock_user *user,
+ off_t offset, int whence, off_t *new_offset)
+{
+ return user ? ESPIPE : EOPNOTSUPP;
+}
+
+/* Return a new port with the same semantics as the existing port. */
+error_t
+S_io_duplicate (struct sock_user *user,
+ mach_port_t *new_port, mach_msg_type_name_t *new_port_type)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_create_port (user->sock, new_port);
+ if (! err)
+ *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ return err;
+}
+
+/* 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. */
+error_t
+S_io_select (struct sock_user *user,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ error_t err = 0;
+ struct sock *sock;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ *select_type &= SELECT_READ | SELECT_WRITE;
+
+ sock = user->sock;
+ mutex_lock (&sock->lock);
+
+ if (sock->listen_queue)
+ /* Sock is used for accepting connections, not I/O. For these, you can
+ only select for reading, which will block until a connection request
+ comes along. */
+ {
+ mutex_unlock (&sock->lock);
+
+ *select_type &= SELECT_READ;
+
+ if (*select_type & SELECT_READ)
+ {
+ /* Wait for a connect. Passing in NULL for REQ means that the
+ request won't be dequeued. */
+ if (connq_listen (sock->listen_queue, 1, NULL, NULL) == 0)
+ /* We can satisfy this request immediately. */
+ return 0;
+ else
+ /* Gotta wait... */
+ {
+ ports_interrupt_self_on_port_death (user, reply);
+ return connq_listen (sock->listen_queue, 0, NULL, NULL);
+ }
+ }
+ }
+ else
+ /* Sock is a normal read/write socket. */
+ {
+ int valid;
+ int ready = 0;
+ struct pipe *read_pipe = sock->read_pipe;
+ struct pipe *write_pipe = sock->write_pipe;
+
+ if (! write_pipe)
+ ready |= SELECT_WRITE;
+ if (! read_pipe)
+ ready |= SELECT_READ;
+ ready &= *select_type; /* Only keep things requested. */
+ *select_type &= ~ready;
+
+ valid = *select_type;
+ if (valid & SELECT_READ)
+ {
+ pipe_acquire_reader (read_pipe);
+ if (pipe_wait_readable (read_pipe, 1, 1) != EWOULDBLOCK)
+ ready |= SELECT_READ; /* Data immediately readable (or error). */
+ mutex_unlock (&read_pipe->lock);
+ }
+ if (valid & SELECT_WRITE)
+ {
+ pipe_acquire_writer (write_pipe);
+ if (pipe_wait_writable (write_pipe, 1) != EWOULDBLOCK)
+ ready |= SELECT_WRITE; /* Data immediately writable (or error). */
+ mutex_unlock (&write_pipe->lock);
+ }
+
+ mutex_unlock (&sock->lock);
+
+ if (ready)
+ /* No need to block, we've already got some results. */
+ *select_type = ready;
+ else
+ /* Wait for something to change. */
+ {
+ ports_interrupt_self_on_port_death (user, reply);
+ err = pipe_pair_select (read_pipe, write_pipe, select_type, 1);
+ }
+
+ if (valid & SELECT_READ)
+ pipe_remove_reader (read_pipe);
+ if (valid & SELECT_WRITE)
+ pipe_remove_writer (write_pipe);
+ }
+
+ return err;
+}
+
+/* Return the current status of the object. Not all the fields of the
+ io_statuf_t are meaningful for all objects; however, the access and
+ modify times, the optimal IO size, and the fs type are meaningful
+ for all objects. */
+error_t
+S_io_stat (struct sock_user *user, struct stat *st)
+{
+ struct sock *sock;
+ struct pipe *rpipe, *wpipe;
+
+ void copy_time (time_value_t *from, time_t *to_sec, unsigned long *to_usec)
+ {
+ *to_sec = from->seconds;
+ *to_usec = from->microseconds;
+ }
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ bzero (st, sizeof (struct stat));
+
+ st->st_fstype = FSTYPE_SOCKET;
+ st->st_mode = S_IFSOCK;
+ st->st_fsid = getpid ();
+ st->st_ino = sock->id;
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+
+ mutex_lock (&sock->lock); /* Make sure the pipes don't go away... */
+
+ rpipe = sock->read_pipe;
+ wpipe = sock->write_pipe;
+
+ if (rpipe)
+ {
+ mutex_lock (&rpipe->lock);
+ copy_time (&rpipe->read_time, &st->st_atime, &st->st_atime_usec);
+ /* This seems useful. */
+ st->st_size = pipe_readable (rpipe, 1);
+ mutex_unlock (&rpipe->lock);
+ }
+
+ if (wpipe)
+ {
+ mutex_lock (&wpipe->lock);
+ copy_time (&wpipe->write_time, &st->st_mtime, &st->st_mtime_usec);
+ mutex_unlock (&wpipe->lock);
+ }
+
+ copy_time (&sock->change_time, &st->st_ctime, &st->st_ctime_usec);
+
+ mutex_unlock (&sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_get_openmodes (struct sock_user *user, int *bits)
+{
+ unsigned flags;
+ if (!user)
+ return EOPNOTSUPP;
+ flags = user->sock->flags;
+ *bits =
+ O_APPEND /* pipes always append */
+ | (flags & SOCK_NONBLOCK ? O_NONBLOCK : 0)
+ | (flags & SOCK_SHUTDOWN_READ ? 0 : O_READ)
+ | (flags & SOCK_SHUTDOWN_WRITE ? 0 : O_WRITE);
+ return 0;
+}
+
+error_t
+S_io_set_all_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= SOCK_NONBLOCK;
+ else
+ user->sock->flags &= ~SOCK_NONBLOCK;
+ mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_set_some_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= SOCK_NONBLOCK;
+ mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_clear_some_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags &= ~SOCK_NONBLOCK;
+ mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+#define NIDS 10
+
+error_t
+S_io_reauthenticate (struct sock_user *user, mach_port_t rendezvous)
+{
+ error_t err;
+ mach_port_t auth_server;
+ mach_port_t new_user_port;
+ uid_t uids_buf[NIDS], aux_uids_buf[NIDS];
+ uid_t *uids = uids_buf, *aux_uids = aux_uids_buf;
+ gid_t gids_buf[NIDS], aux_gids_buf[NIDS];
+ gid_t *gids = gids_buf, *aux_gids = aux_gids_buf;
+ unsigned num_uids = NIDS, num_aux_uids = NIDS;
+ unsigned num_gids = NIDS, num_aux_gids = NIDS;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ do
+ err = sock_create_port (user->sock, &new_user_port);
+ while (err == EINTR);
+ if (err)
+ return err;
+
+ auth_server = getauth ();
+ err = mach_port_insert_right (mach_task_self (), new_user_port,
+ new_user_port, MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ do
+ err =
+ auth_server_authenticate (auth_server,
+ rendezvous, MACH_MSG_TYPE_COPY_SEND,
+ new_user_port, MACH_MSG_TYPE_COPY_SEND,
+ &uids, &num_uids, &aux_uids, &num_aux_uids,
+ &gids, &num_gids, &aux_gids, &num_aux_gids);
+ while (err == EINTR);
+ mach_port_deallocate (mach_task_self (), rendezvous);
+ mach_port_deallocate (mach_task_self (), auth_server);
+ mach_port_deallocate (mach_task_self (), new_user_port);
+
+ /* Throw away the ids we went through all that trouble to get... */
+#define TRASH_IDS(ids, buf, num) \
+ if (buf != ids) \
+ munmap (ids, num * sizeof (uid_t));
+
+ TRASH_IDS (uids, uids_buf, num_uids);
+ TRASH_IDS (gids, gids_buf, num_gids);
+ TRASH_IDS (aux_uids, aux_uids_buf, num_aux_uids);
+ TRASH_IDS (aux_gids, aux_gids_buf, num_aux_gids);
+
+ return err;
+}
+
+error_t
+S_io_restrict_auth (struct sock_user *user,
+ mach_port_t *new_port,
+ mach_msg_type_name_t *new_port_type,
+ uid_t *uids, unsigned num_uids,
+ uid_t *gids, unsigned num_gids)
+{
+ if (!user)
+ return EOPNOTSUPP;
+ *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ return sock_create_port (user->sock, new_port);
+}
+
+error_t
+S_io_pathconf (struct sock_user *user, int name, int *value)
+{
+ if (user == NULL)
+ return EOPNOTSUPP;
+ else if (name == _PC_PIPE_BUF)
+ {
+ mutex_lock (&user->sock->lock);
+ if (user->sock->write_pipe == NULL)
+ *value = 0;
+ else
+ *value = user->sock->write_pipe->write_atomic;
+ mutex_unlock (&user->sock->lock);
+ return 0;
+ }
+ else
+ return EINVAL;
+}
+
+error_t
+S_io_identity (struct sock_user *user,
+ mach_port_t *id, mach_msg_type_name_t *id_type,
+ mach_port_t *fsys_id, mach_msg_type_name_t *fsys_id_type,
+ int *fileno)
+{
+ static mach_port_t server_id = MACH_PORT_NULL;
+ error_t err = 0;
+ struct sock *sock;
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ if (server_id == MACH_PORT_NULL)
+ {
+ static struct mutex server_id_lock = MUTEX_INITIALIZER;
+
+ mutex_lock (&server_id_lock);
+ if (server_id == MACH_PORT_NULL) /* Recheck with the lock held. */
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &server_id);
+ mutex_unlock (&server_id_lock);
+
+ if (err)
+ return err;
+ }
+
+ sock = user->sock;
+
+ mutex_lock (&sock->lock);
+ if (sock->id == MACH_PORT_NULL)
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &sock->id);
+ mutex_unlock (&sock->lock);
+
+ if (! err)
+ {
+ *id = sock->id;
+ *id_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fsys_id = server_id;
+ *fsys_id_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fileno = sock->id; /* Might as well */
+ }
+
+ return err;
+}
+
+
+/* Stubs for currently unsupported rpcs. */
+
+error_t
+S_io_revoke (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_async(struct sock_user *user,
+ mach_port_t notify_port,
+ mach_port_t *async_id_port,
+ mach_msg_type_name_t *async_id_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_mod_owner(struct sock_user *user, pid_t owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_owner(struct sock_user *user, pid_t *owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_icky_async_id (struct sock_user *user,
+ mach_port_t *icky_async_id_port,
+ mach_msg_type_name_t *icky_async_id_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_map (struct sock_user *user,
+ mach_port_t *memobj_rd, mach_msg_type_name_t *memobj_rd_type,
+ mach_port_t *memobj_wt, mach_msg_type_name_t *memobj_wt_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_map_cntl (struct sock_user *user,
+ mach_port_t *mem, mach_msg_type_name_t *mem_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_release_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_eofnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_prenotify (struct sock_user *user, vm_offset_t start, vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_postnotify (struct sock_user *user, vm_offset_t start, vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readsleep (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+
+error_t
+S_io_sigio (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_server_version (struct sock_user *user,
+ char *name, int *maj, int *min, int *edit)
+{
+ return EOPNOTSUPP;
+}
diff --git a/pflocal/mig-decls.h b/pflocal/mig-decls.h
new file mode 100644
index 00000000..fe4aefdf
--- /dev/null
+++ b/pflocal/mig-decls.h
@@ -0,0 +1,59 @@
+/* Type decls for mig-produced server stubs
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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.
+
+ The GNU Hurd 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. */
+
+#ifndef __MIG_DECLS_H__
+#define __MIG_DECLS_H__
+
+#include "sock.h"
+
+/* For mig */
+
+typedef struct sock_user *sock_user_t;
+typedef struct addr *addr_t;
+
+extern inline
+sock_user_t begin_using_sock_user_port(mach_port_t port)
+{
+ return (sock_user_t)ports_lookup_port (0, port, sock_user_port_class);
+}
+
+extern inline void
+end_using_sock_user_port (sock_user_t sock_user)
+{
+ if (sock_user != NULL)
+ ports_port_deref (sock_user);
+}
+
+extern inline
+addr_t begin_using_addr_port(mach_port_t port)
+{
+ return (addr_t)ports_lookup_port (0, port, addr_port_class);
+}
+
+extern inline void
+end_using_addr_port (addr_t addr)
+{
+ if (addr != NULL)
+ ports_port_deref (addr);
+}
+
+#endif /* __MIG_DECLS_H__ */
diff --git a/pflocal/mig-mutate.h b/pflocal/mig-mutate.h
new file mode 100644
index 00000000..b1494730
--- /dev/null
+++ b/pflocal/mig-mutate.h
@@ -0,0 +1,33 @@
+/* Automagic type transformation for our mig interfaces
+
+ Copyright (C) 1995 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 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. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define IO_SELECT_REPLY_PORT
+
+#define IO_INTRAN sock_user_t begin_using_sock_user_port (io_t)
+#define IO_DESTRUCTOR end_using_sock_user_port (sock_user_t)
+
+#define IO_IMPORTS import "mig-decls.h";
+
+#define SOCKET_INTRAN sock_user_t begin_using_sock_user_port (socket_t)
+#define SOCKET_DESTRUCTOR end_using_sock_user_port (sock_user_t)
+#define ADDRPORT_INTRAN addr_t begin_using_addr_port (addr_port_t)
+#define ADDRPORT_DESTRUCTOR end_using_addr_port (addr_t)
+
+#define SOCKET_IMPORTS import "mig-decls.h";
diff --git a/pflocal/pf.c b/pflocal/pf.c
new file mode 100644
index 00000000..65d5c5a9
--- /dev/null
+++ b/pflocal/pf.c
@@ -0,0 +1,123 @@
+/* Protocol family operations
+
+ Copyright (C) 1995, 1999 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 <stddef.h>
+#include <sys/socket.h>
+#include <hurd/pipe.h>
+
+#include "sock.h"
+
+#include "socket_S.h"
+
+/* Create a new socket. Sock type is, for example, SOCK_STREAM,
+ SOCK_DGRAM, or some such. */
+error_t
+S_socket_create (mach_port_t pf,
+ int sock_type, int protocol,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ error_t err;
+ struct sock *sock;
+ struct pipe_class *pipe_class;
+
+ if (protocol != 0)
+ return EPROTONOSUPPORT;
+
+ switch (sock_type)
+ {
+ case SOCK_STREAM:
+ pipe_class = stream_pipe_class; break;
+ case SOCK_DGRAM:
+ pipe_class = dgram_pipe_class; break;
+ case SOCK_SEQPACKET:
+ pipe_class = seqpack_pipe_class; break;
+ default:
+ return ESOCKTNOSUPPORT;
+ }
+
+ err = sock_create (pipe_class, &sock);
+ if (!err)
+ {
+ err = sock_create_port (sock, port);
+ if (err)
+ sock_free (sock);
+ else
+ *port_type = MACH_MSG_TYPE_MAKE_SEND;
+ }
+
+ return err;
+}
+
+error_t
+S_socket_create_address (mach_port_t pf, int sockaddr_type,
+ char *data, size_t data_len,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_socket_fabricate_address (mach_port_t pf,
+ int sockaddr_type,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ error_t err;
+ struct addr *addr;
+
+ if (sockaddr_type != AF_LOCAL)
+ return EAFNOSUPPORT;
+
+ err = addr_create (&addr);
+ if (err)
+ return err;
+
+ *addr_port = ports_get_right (addr);
+ *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ return 0;
+}
+
+/* Implement socket_whatis_address as described in <hurd/socket.defs>.
+ Since we cannot tell what our adress is, return an empty string as
+ the file name. This is primarily for the implementation of accept
+ and recvfrom. The functions getsockname and getpeername remain
+ unsupported for the local namespace. */
+error_t
+S_socket_whatis_address (struct addr *addr,
+ int *sockaddr_type,
+ char **sockaddr, size_t *sockaddr_len)
+{
+ socklen_t addr_len = (offsetof (struct sockaddr, sa_data) + 1);
+
+ if (! addr)
+ return EOPNOTSUPP;
+
+ *sockaddr_type = AF_LOCAL;
+ if (*sockaddr_len < addr_len)
+ *sockaddr = mmap (0, addr_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ ((struct sockaddr *) *sockaddr)->sa_len = addr_len;
+ ((struct sockaddr *) *sockaddr)->sa_family = *sockaddr_type;
+ ((struct sockaddr *) *sockaddr)->sa_data[0] = 0;
+ *sockaddr_len = addr_len;
+
+ return 0;
+}
diff --git a/pflocal/pflocal.c b/pflocal/pflocal.c
new file mode 100644
index 00000000..7a4e8d91
--- /dev/null
+++ b/pflocal/pflocal.c
@@ -0,0 +1,130 @@
+/* A server for local sockets, of type PF_LOCAL
+
+ Copyright (C) 1995, 1997, 1998 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 <getopt.h>
+#include <error.h>
+#include <sys/stat.h>
+
+#include <hurd/hurd_types.h>
+#include <hurd/trivfs.h>
+
+#include "sock.h"
+
+/* Where to put the file-system ports. */
+static struct port_bucket *pf_port_bucket;
+
+/* Trivfs hooks */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = 0;
+
+/* Trivfs noise. */
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+/* ---------------------------------------------------------------- */
+
+/* A demuxer to separate out pf-level operations on our node. */
+static int
+pf_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int socket_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ return socket_server (inp, outp) || trivfs_demuxer (inp, outp);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ if (argc > 1)
+ {
+ fprintf(stderr, "Usage: %s\n", program_invocation_name);
+ exit (1);
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(2, 0, "Must be started as a translator");
+
+ pf_port_bucket = ports_create_bucket ();
+
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+
+ /* Prepare to create sockets. */
+ err = sock_global_init ();
+ if (err)
+ error(3, err, "Initializing");
+
+ /* Reply to our parent */
+ err =
+ trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], pf_port_bucket,
+ trivfs_protid_portclasses[0], pf_port_bucket,
+ NULL);
+ if (err)
+ error(3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ ports_manage_port_operations_multithread (pf_port_bucket,
+ pf_demuxer,
+ 30*1000, 5*60*1000, 0);
+ while (sock_global_shutdown () != 0);
+
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_fstype = FSTYPE_MISC;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ error_t err = ports_inhibit_bucket_rpcs (pf_port_bucket); /* Stop all I/O. */
+
+ if (err == 0 || (err != EINTR && force))
+ {
+ /* Now see if there are any old sockets lying around. */
+ err = sock_global_shutdown ();
+
+ /* Exit if not, or if we must. */
+ if (err == 0 || force)
+ exit (0);
+
+ /* We won't go away, so start things going again... */
+ ports_resume_bucket_rpcs (pf_port_bucket);
+ }
+
+ return err;
+}
diff --git a/pflocal/sock.c b/pflocal/sock.c
new file mode 100644
index 00000000..350c7de8
--- /dev/null
+++ b/pflocal/sock.c
@@ -0,0 +1,504 @@
+/* Sock functions
+
+ 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 <string.h> /* For bzero() */
+
+#include <cthreads.h>
+
+#include <hurd/pipe.h>
+
+#include "sock.h"
+#include "sserver.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the pipe that SOCK is reading from in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. In the
+ case where the read should signal EOF, EPIPE is returned. SOCK mustn't be
+ locked. */
+error_t
+sock_acquire_read_pipe (struct sock *sock, struct pipe **pipe)
+{
+ error_t err = 0;
+
+ mutex_lock (&sock->lock);
+
+ *pipe = sock->read_pipe;
+ if (*pipe != NULL)
+ /* SOCK may have a read pipe even before it's connected, so make
+ sure it really is. */
+ if ( !(sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ && !(sock->flags & SOCK_CONNECTED))
+ err = ENOTCONN;
+ else
+ pipe_acquire_reader (*pipe);
+ else if (sock->flags & SOCK_SHUTDOWN_READ)
+ /* Reading on a socket with the read-half shutdown always acts as if the
+ pipe were at eof, even if the socket isn't connected yet [at least in
+ netbsd]. */
+ err = EPIPE;
+ else
+ err = ENOTCONN;
+
+ mutex_unlock (&sock->lock);
+
+ return err;
+}
+
+/* Returns the pipe that SOCK is writing to in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. SOCK
+ mustn't be locked. */
+error_t
+sock_acquire_write_pipe (struct sock *sock, struct pipe **pipe)
+{
+ error_t err = 0;
+
+ mutex_lock (&sock->lock);
+ *pipe = sock->write_pipe;
+ if (*pipe != NULL)
+ pipe_acquire_writer (*pipe); /* Do this before unlocking the sock! */
+ else if (sock->flags & SOCK_SHUTDOWN_WRITE)
+ /* Writing on a socket with the write-half shutdown always acts as if the
+ pipe were broken, even if the socket isn't connected yet [at least in
+ netbsd]. */
+ err = EPIPE;
+ else if (sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ /* Connectionless protocols give a different error when unconnected. */
+ err = EDESTADDRREQ;
+ else
+ err = ENOTCONN;
+
+ mutex_unlock (&sock->lock);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return a new socket with the given pipe class in SOCK. */
+error_t
+sock_create (struct pipe_class *pipe_class, struct sock **sock)
+{
+ error_t err;
+ struct sock *new = malloc (sizeof (struct sock));
+
+ if (new == NULL)
+ return ENOMEM;
+
+ /* A socket always has a read pipe (this is just to avoid some annoyance in
+ sock_connect), so create it here. */
+ err = pipe_create (pipe_class, &new->read_pipe);
+ if (err)
+ {
+ free (new);
+ return err;
+ }
+
+ pipe_add_reader (new->read_pipe);
+
+ new->refs = 0;
+ new->flags = 0;
+ new->write_pipe = NULL;
+ new->id = MACH_PORT_NULL;
+ new->listen_queue = NULL;
+ new->connect_queue = NULL;
+ new->pipe_class = pipe_class;
+ new->addr = NULL;
+ bzero (&new->change_time, sizeof (new->change_time));
+ mutex_init (&new->lock);
+
+ *sock = new;
+ return 0;
+}
+
+/* Free SOCK, assuming there are no more handle on it. */
+void
+sock_free (struct sock *sock)
+{
+ sock_shutdown (sock, SOCK_SHUTDOWN_READ | SOCK_SHUTDOWN_WRITE);
+ if (sock->id != MACH_PORT_NULL)
+ mach_port_destroy (mach_task_self (), sock->id);
+ free (sock);
+}
+
+/* Free a sock derefed too far. */
+void
+_sock_norefs (struct sock *sock)
+{
+ /* A sock should never have an address when it has 0 refs, as the
+ address should hold a reference to the sock! */
+ assert (sock->addr == NULL);
+ mutex_unlock (&sock->lock); /* Unlock so sock_free can do stuff. */
+ sock_free (sock);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return a new socket largely copied from TEMPLATE. */
+error_t
+sock_clone (struct sock *template, struct sock **sock)
+{
+ error_t err = sock_create (template->pipe_class, sock);
+
+ if (err)
+ return err;
+
+ /* Copy some properties from TEMPLATE. */
+ (*sock)->flags = template->flags & ~SOCK_CONNECTED;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+struct port_class *sock_user_port_class;
+
+/* Get rid of a user reference to a socket. */
+static void
+sock_user_clean (void *vuser)
+{
+ struct sock_user *user = vuser;
+ sock_deref (user->sock);
+}
+
+/* Return a new user port on SOCK in PORT. */
+error_t
+sock_create_port (struct sock *sock, mach_port_t *port)
+{
+ struct sock_user *user;
+ error_t err =
+ ports_create_port (sock_user_port_class, sock_port_bucket,
+ sizeof (struct sock_user), &user);
+
+ if (err)
+ return err;
+
+ ensure_sock_server ();
+
+ mutex_lock (&sock->lock);
+ sock->refs++;
+ mutex_unlock (&sock->lock);
+
+ user->sock = sock;
+
+ *port = ports_get_right (user);
+ ports_port_deref (user); /* We only want one ref, for the send right. */
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Address manipulation. */
+
+struct addr
+{
+ struct port_info pi;
+ struct sock *sock;
+ struct mutex lock;
+};
+
+struct port_class *addr_port_class;
+
+/* Get rid of ADDR's socket's reference to it, in preparation for ADDR going
+ away. */
+static void
+addr_unbind (void *vaddr)
+{
+ struct sock *sock;
+ struct addr *addr = vaddr;
+
+ mutex_lock (&addr->lock);
+ sock = addr->sock;
+ if (sock)
+ {
+ mutex_lock (&sock->lock);
+ sock->addr = NULL;
+ addr->sock = NULL;
+ ports_port_deref_weak (addr);
+ mutex_unlock (&sock->lock);
+ sock_deref (sock);
+ }
+ mutex_unlock (&addr->lock);
+}
+
+/* Cleanup after the address ADDR, which is going away... */
+static void
+addr_clean (void *vaddr)
+{
+ struct addr *addr = vaddr;
+ /* ADDR should never have a socket bound to it at this point, as it should
+ have been removed by addr_unbind dropping the socket's weak reference
+ it. */
+ assert (addr->sock == NULL);
+}
+
+/* Return a new address, not connected to any socket yet, ADDR. */
+inline error_t
+addr_create (struct addr **addr)
+{
+ error_t err =
+ ports_create_port (addr_port_class, sock_port_bucket,
+ sizeof (struct addr), addr);
+
+ if (! err)
+ {
+ ensure_sock_server ();
+ (*addr)->sock = NULL;
+ mutex_init (&(*addr)->lock);
+ }
+
+ return err;
+}
+
+/* Bind SOCK to ADDR. */
+error_t
+sock_bind (struct sock *sock, struct addr *addr)
+{
+ error_t err = 0;
+ struct addr *old_addr;
+
+ mutex_lock (&addr->lock);
+ mutex_lock (&sock->lock);
+
+ old_addr = sock->addr;
+ if (addr && old_addr)
+ err = EINVAL; /* SOCK already bound. */
+ else if (addr && addr->sock)
+ err = EADDRINUSE; /* Something else already bound ADDR. */
+ else if (addr)
+ addr->sock = sock; /* First binding for SOCK. */
+ else
+ old_addr->sock = NULL; /* Unbinding SOCK. */
+
+ if (! err)
+ {
+ sock->addr = addr;
+ if (addr)
+ sock->refs++;
+ if (old_addr)
+ {
+ /* Note that we don't have to worry about SOCK's ref count going to
+ zero because whoever's calling us should be holding a ref. */
+ sock->refs--;
+ assert (sock->refs > 0); /* But make sure... */
+ }
+ }
+
+ mutex_unlock (&sock->lock);
+ mutex_unlock (&addr->lock);
+
+ return err;
+}
+
+/* Returns SOCK's addr, with an additional reference, fabricating one if
+ necessary. SOCK should be locked. */
+static inline error_t
+ensure_addr (struct sock *sock, struct addr **addr)
+{
+ error_t err = 0;
+
+ if (! sock->addr)
+ {
+ err = addr_create (&sock->addr);
+ if (!err)
+ {
+ sock->addr->sock = sock;
+ sock->refs++;
+ ports_port_ref_weak (sock->addr);
+ }
+ }
+ else
+ ports_port_ref (sock->addr);
+
+ if (!err)
+ *addr = sock->addr;
+
+ return err;
+}
+
+/* Returns the socket bound to ADDR in SOCK, or EADDRNOTAVAIL. The returned
+ sock will have one reference added to it. */
+error_t
+addr_get_sock (struct addr *addr, struct sock **sock)
+{
+ mutex_lock (&addr->lock);
+ *sock = addr->sock;
+ if (*sock)
+ (*sock)->refs++;
+ mutex_unlock (&addr->lock);
+ return *sock ? 0 : EADDRNOTAVAIL;
+}
+
+/* Returns SOCK's address in ADDR, with an additional reference added. If
+ SOCK doesn't currently have an address, one is fabricated first. */
+error_t
+sock_get_addr (struct sock *sock, struct addr **addr)
+{
+ error_t err;
+
+ mutex_lock (&sock->lock);
+ err = ensure_addr (sock, addr);
+ mutex_unlock (&sock->lock);
+
+ return err; /* XXX */
+}
+
+/* ---------------------------------------------------------------- */
+
+/* We hold this lock before we lock two sockets at once, to prevent someone
+ else trying to lock the same two sockets in the reverse order, resulting
+ in a deadlock. */
+static struct mutex socket_pair_lock;
+
+/* Connect SOCK1 and SOCK2. */
+error_t
+sock_connect (struct sock *sock1, struct sock *sock2)
+{
+ error_t err = 0;
+ /* In the case of a connectionless protocol, an already-connected socket may
+ be reconnected, so save the old destination for later disposal. */
+ struct pipe *old_sock1_write_pipe = NULL;
+ struct addr *old_sock1_write_addr = NULL;
+
+ void connect (struct sock *wr, struct sock *rd)
+ {
+ if (!( (wr->flags & SOCK_SHUTDOWN_WRITE)
+ || (rd->flags & SOCK_SHUTDOWN_READ)))
+ {
+ struct pipe *pipe = rd->read_pipe;
+ assert (pipe); /* Since SOCK_SHUTDOWN_READ isn't set. */
+ pipe_add_writer (pipe);
+ wr->write_pipe = pipe;
+ }
+ }
+
+ if (sock1->pipe_class != sock2->pipe_class)
+ /* Incompatible socket types. */
+ return EOPNOTSUPP; /* XXX?? */
+
+ mutex_lock (&socket_pair_lock);
+ mutex_lock (&sock1->lock);
+ if (sock1 != sock2)
+ /* If SOCK1 == SOCK2, then we get a fifo! */
+ mutex_lock (&sock2->lock);
+
+ if ((sock1->flags & SOCK_CONNECTED) || (sock2->flags & SOCK_CONNECTED))
+ /* An already-connected socket. */
+ err = EISCONN;
+ else
+ {
+ old_sock1_write_pipe = sock1->write_pipe;
+ old_sock1_write_addr = sock1->write_addr;
+
+ /* Always make the forward connection. */
+ connect (sock1, sock2);
+
+ /* Only make the reverse for connection-oriented protocols. */
+ if (! (sock1->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS))
+ {
+ sock1->flags |= SOCK_CONNECTED;
+ if (sock1 != sock2)
+ {
+ connect (sock2, sock1);
+ sock2->flags |= SOCK_CONNECTED;
+ }
+ }
+ }
+
+ if (sock1 != sock2)
+ mutex_unlock (&sock2->lock);
+ mutex_unlock (&sock1->lock);
+ mutex_unlock (&socket_pair_lock);
+
+ if (old_sock1_write_pipe)
+ {
+ pipe_remove_writer (old_sock1_write_pipe);
+ ports_port_deref (old_sock1_write_addr);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Shutdown either the read or write halves of SOCK, depending on whether the
+ SOCK_SHUTDOWN_READ or SOCK_SHUTDOWN_WRITE flags are set in FLAGS. */
+void
+sock_shutdown (struct sock *sock, unsigned flags)
+{
+ unsigned old_flags;
+
+ mutex_lock (&sock->lock);
+
+ old_flags = sock->flags;
+ sock->flags |= flags;
+
+ if (flags & SOCK_SHUTDOWN_READ && !(old_flags & SOCK_SHUTDOWN_READ))
+ /* Shutdown the read half. */
+ {
+ struct pipe *pipe = sock->read_pipe;
+ if (pipe != NULL)
+ {
+ sock->read_pipe = NULL;
+ /* Unlock SOCK here, as we may subsequently wake up other threads. */
+ mutex_unlock (&sock->lock);
+ pipe_remove_reader (pipe);
+ }
+ else
+ mutex_unlock (&sock->lock);
+ }
+
+ if (flags & SOCK_SHUTDOWN_WRITE && !(old_flags & SOCK_SHUTDOWN_WRITE))
+ /* Shutdown the write half. */
+ {
+ struct pipe *pipe = sock->write_pipe;
+ if (pipe != NULL)
+ {
+ sock->write_pipe = NULL;
+ /* Unlock SOCK here, as we may subsequently wake up other threads. */
+ mutex_unlock (&sock->lock);
+ pipe_remove_writer (pipe);
+ }
+ else
+ mutex_unlock (&sock->lock);
+ }
+ else
+ mutex_unlock (&sock->lock);
+}
+
+/* ---------------------------------------------------------------- */
+
+error_t
+sock_global_init ()
+{
+ sock_port_bucket = ports_create_bucket ();
+ sock_user_port_class = ports_create_class (sock_user_clean, NULL);
+ addr_port_class = ports_create_class (addr_clean, addr_unbind);
+ return 0;
+}
+
+/* Try to shutdown any active sockets, returning EBUSY if we can't. */
+error_t
+sock_global_shutdown ()
+{
+ int num_ports = ports_count_bucket (sock_port_bucket);
+ ports_enable_bucket (sock_port_bucket);
+ return (num_ports == 0 ? 0 : EBUSY);
+}
diff --git a/pflocal/sock.h b/pflocal/sock.h
new file mode 100644
index 00000000..e2852da0
--- /dev/null
+++ b/pflocal/sock.h
@@ -0,0 +1,161 @@
+/* Internal sockets
+
+ Copyright (C) 1995, 1996, 1999 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. */
+
+#ifndef __SOCK_H__
+#define __SOCK_H__
+
+#include <assert.h>
+#include <cthreads.h> /* For mutexes */
+#include <sys/mman.h>
+
+#include <hurd/ports.h>
+
+struct pipe;
+struct pipe_class;
+
+/* A port on SOCK. Multiple sock_user's can point to the same socket. */
+struct sock_user
+{
+ struct port_info pi;
+ struct sock *sock;
+};
+
+/* An endpoint for a possible I/O stream. */
+struct sock
+{
+ int refs;
+ struct mutex lock;
+
+ /* What kind of socket this is. */
+ struct pipe_class *pipe_class;
+
+ /* Reads from this socket come from READ_PIPE, writes go to WRITE_PIPE.
+ A sock always has a read pipe, and a write pipe when it's connected to
+ another socket. */
+ struct pipe *read_pipe, *write_pipe;
+
+ /* FLAGS from SOCK_*, below. */
+ unsigned flags;
+
+ /* A receive right for this socket's id ports. */
+ mach_port_t id;
+
+ /* Last time the socket got frobbed. */
+ time_value_t change_time;
+
+ /* This socket's local address. Note that we don't hold any references on
+ ADDR, and depend on the addr zeroing our pointer if it goes away (which
+ is ok, as we can then just make up another address if necessary, and no
+ one could tell anyway). */
+ struct addr *addr;
+
+ /* If this sock has been connected to another sock, then WRITE_ADDR is the
+ addr of that sock. We *do* hold a reference to this addr. */
+ struct addr *write_addr;
+
+ /* A connection queue to listen for incoming connections on. Once a socket
+ has one of these, it always does, and can never again be used for
+ anything but accepting incoming connections. */
+ struct connq *listen_queue;
+ /* A connection queue we're attempting to connect through; a socket may
+ only be attempting one connection at a time. */
+ struct connq *connect_queue;
+};
+
+/* Socket flags */
+#define SOCK_CONNECTED 0x1 /* A connected connection-oriented sock. */
+#define SOCK_NONBLOCK 0x2 /* Don't block on I/O. */
+#define SOCK_SHUTDOWN_READ 0x4 /* The read-half has been shutdown. */
+#define SOCK_SHUTDOWN_WRITE 0x8 /* The write-half has been shutdown. */
+
+/* Returns the pipe that SOCK is reading from in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. NULL may
+ also be returned in PIPE with a 0 error, meaning that EOF should be
+ returned. SOCK mustn't be locked. */
+error_t sock_acquire_read_pipe (struct sock *sock, struct pipe **pipe);
+
+/* Returns the pipe that SOCK is writing to in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. SOCK
+ mustn't be locked. */
+error_t sock_acquire_write_pipe (struct sock *sock, struct pipe **pipe);
+
+/* Connect together the previously unconnected sockets SOCK1 and SOCK2. */
+error_t sock_connect (struct sock *sock1, struct sock *sock2);
+
+/* Return a new socket with the given pipe class in SOCK. */
+error_t sock_create (struct pipe_class *pipe_class, struct sock **sock);
+
+/* Free SOCK, assuming there are no more handle on it. */
+void sock_free (struct sock *sock);
+
+/* Free a sock derefed too far. */
+void _sock_norefs (struct sock *sock);
+
+/* Remove a reference from SOCK, possibly freeing it. */
+extern inline void
+sock_deref (struct sock *sock)
+{
+ mutex_lock (&sock->lock);
+ if (--sock->refs == 0)
+ _sock_norefs (sock);
+ else
+ mutex_unlock (&sock->lock);
+}
+
+/* Return a new socket just like TEMPLATE in SOCK. */
+error_t sock_clone (struct sock *template, struct sock **sock);
+
+/* Return a new user port on SOCK in PORT. */
+error_t sock_create_port (struct sock *sock, mach_port_t *port);
+
+/* Bind SOCK to ADDR. */
+error_t sock_bind (struct sock *sock, struct addr *addr);
+
+/* Returns SOCK's address in ADDR, with an additional reference added. If
+ SOCK doesn't currently have an address, one is fabricated first. */
+error_t sock_get_addr (struct sock *sock, struct addr **addr);
+
+/* If SOCK is a connected socket, returns a send right to SOCK's peer's
+ address in ADDR_PORT. */
+error_t sock_get_write_addr_port (struct sock *sock, mach_port_t *addr_port);
+
+/* Shutdown either the read or write halves of SOCK, depending on whether the
+ SOCK_SHUTDOWN_READ or SOCK_SHUTDOWN_WRITE flags are set in FLAGS. */
+void sock_shutdown (struct sock *sock, unsigned flags);
+
+/* Return a new address, not connected to any socket yet, ADDR. */
+error_t addr_create (struct addr **addr);
+
+/* Returns the socket bound to ADDR in SOCK, or EADDRNOTAVAIL. The returned
+ sock will have one reference added to it. */
+error_t addr_get_sock (struct addr *addr, struct sock **sock);
+
+/* Prepare for socket creation. */
+error_t sock_global_init ();
+
+/* Try to shutdown any active sockets, returning EBUSY if we can't. Assumes
+ non-socket RPCS's have been disabled. */
+error_t sock_global_shutdown ();
+
+/* Mostly here for use by mig-decls.h. */
+extern struct port_class *sock_user_port_class;
+extern struct port_class *addr_port_class;
+
+#endif /* __SOCK_H__ */
diff --git a/pflocal/socket.c b/pflocal/socket.c
new file mode 100644
index 00000000..0bc72066
--- /dev/null
+++ b/pflocal/socket.c
@@ -0,0 +1,425 @@
+/* Socket-specific operations
+
+ 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 <sys/socket.h>
+
+#include <hurd/pipe.h>
+
+#include "sock.h"
+#include "connq.h"
+
+#include "socket_S.h"
+
+/* Connect two sockets */
+error_t
+S_socket_connect2 (struct sock_user *user1, struct sock_user *user2)
+{
+ error_t err;
+
+ if (!user1 || !user2)
+ return EOPNOTSUPP;
+
+ err = sock_connect (user1->sock, user2->sock);
+
+ /* Since USER2 isn't in the receiver position in the rpc, we get a send
+ right for it (although we only use the receive right with the same
+ name); be sure it's deallocated! */
+ mach_port_deallocate (mach_task_self (), user2->pi.port_right);
+
+ return err;
+}
+
+/* Make sure we have a queue to listen on. */
+static error_t
+ensure_connq (struct sock *sock)
+{
+ error_t err = 0;
+ mutex_lock (&sock->lock);
+ if (!sock->listen_queue)
+ err = connq_create (&sock->listen_queue);
+ mutex_unlock (&sock->lock);
+ return err;
+}
+
+/* Prepare a socket of appropriate type for future accept operations. */
+error_t
+S_socket_listen (struct sock_user *user, int queue_limit)
+{
+ error_t err;
+ if (!user)
+ return EOPNOTSUPP;
+ if (queue_limit < 0)
+ return EINVAL;
+ err = ensure_connq (user->sock);
+ if (!err)
+ err = connq_set_length (user->sock->listen_queue, queue_limit);
+ return err;
+}
+
+error_t
+S_socket_connect (struct sock_user *user, struct addr *addr)
+{
+ error_t err;
+ struct sock *peer;
+
+ if (! addr)
+ return ECONNREFUSED;
+
+ /* Deallocate ADDR's send right, which we get as a side effect of the rpc. */
+ mach_port_deallocate (mach_task_self (),
+ ((struct port_info *)addr)->port_right);
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ err = addr_get_sock (addr, &peer);
+ if (!err)
+ {
+ struct sock *sock = user->sock;
+ struct connq *cq = peer->listen_queue;
+
+ if (sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ /* For connectionless protocols, connect() just sets where writes
+ will go, so the destination need not be doing an accept. */
+ err = sock_connect (sock, peer);
+ else if (cq)
+ /* For connection-oriented protocols, only connect with sockets that
+ are actually listening. */
+ {
+ mutex_lock (&sock->lock);
+ if (sock->connect_queue)
+ /* SOCK is already doing a connect. */
+ err = EALREADY;
+ else if (sock->flags & SOCK_CONNECTED)
+ /* SOCK_CONNECTED is only set for connection-oriented sockets,
+ which can only ever connect once. [If we didn't do this test
+ here, it would eventually fail when it the listening socket
+ tried to accept our connection request.] */
+ err = EISCONN;
+ else
+ {
+ /* Assert that we're trying to connect, so anyone else trying
+ to do so will fail with EALREADY. */
+ sock->connect_queue = cq;
+ mutex_unlock (&sock->lock); /* Unlock SOCK while waiting. */
+
+ /* Try to connect. */
+ err = connq_connect (cq, sock->flags & SOCK_NONBLOCK, sock);
+
+ /* We can safely set CONNECT_QUEUE to NULL, as no one else can
+ set it until we've done so. */
+ mutex_lock (&sock->lock);
+ sock->connect_queue = NULL;
+ }
+ mutex_unlock (&sock->lock);
+ }
+ else
+ err = ECONNREFUSED;
+
+ sock_deref (peer);
+ }
+
+ return err;
+}
+
+/* Return a new connection from a socket previously listened. */
+error_t
+S_socket_accept (struct sock_user *user,
+ mach_port_t *port, mach_msg_type_name_t *port_type,
+ mach_port_t *peer_addr_port,
+ mach_msg_type_name_t *peer_addr_port_type)
+{
+ error_t err;
+ struct sock *sock;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ err = ensure_connq (sock);
+ if (!err)
+ {
+ struct connq_request *req;
+ struct sock *peer_sock;
+
+ err =
+ connq_listen (sock->listen_queue, sock->flags & SOCK_NONBLOCK,
+ &req, &peer_sock);
+ if (!err)
+ {
+ struct sock *conn_sock;
+
+ err = sock_clone (sock, &conn_sock);
+ if (!err)
+ {
+ err = sock_connect (conn_sock, peer_sock);
+ if (!err)
+ {
+ struct addr *peer_addr;
+ *port_type = MACH_MSG_TYPE_MAKE_SEND;
+ err = sock_create_port (conn_sock, port);
+ if (!err)
+ err = sock_get_addr (peer_sock, &peer_addr);
+ if (!err)
+ {
+ *peer_addr_port = ports_get_right (peer_addr);
+ *peer_addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (peer_addr);
+ }
+ else
+ /* TEAR DOWN THE CONNECTION XXX */;
+ }
+ if (err)
+ sock_free (conn_sock);
+ }
+
+ /* Communicate any error (or success) to the connecting thread. */
+ connq_request_complete (req, err);
+ }
+ }
+
+ return err;
+}
+
+/* Bind a socket to an address. */
+error_t
+S_socket_bind (struct sock_user *user, struct addr *addr)
+{
+ if (! addr)
+ return EADDRNOTAVAIL;
+
+ /* Deallocate ADDR's send right, which we get as a side effect of the rpc. */
+ mach_port_deallocate (mach_task_self (),
+ ((struct port_info *)addr)->port_right);
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ return sock_bind (user->sock, addr);
+}
+
+/* Shutdown a socket for reading or writing. */
+error_t
+S_socket_shutdown (struct sock_user *user, int what)
+{
+ if (! user)
+ return EOPNOTSUPP;
+ sock_shutdown (user->sock,
+ (what != 1 ? SOCK_SHUTDOWN_READ : 0)
+ | (what != 0 ? SOCK_SHUTDOWN_WRITE : 0));
+ return 0;
+}
+
+/* Find out the name of a socket. */
+error_t
+S_socket_name (struct sock_user *user,
+ mach_port_t *addr_port, mach_msg_type_name_t *addr_port_type)
+{
+ error_t err;
+ struct addr *addr;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_get_addr (user->sock, &addr);
+ if (err)
+ return err;
+
+ *addr_port = ports_get_right (addr);
+ *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ return 0;
+}
+
+/* Find out the name of the socket's peer. */
+error_t
+S_socket_peername (struct sock_user *user,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ return EOPNOTSUPP; /* XXX */
+ if (!user)
+ return EOPNOTSUPP;
+ *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+}
+
+/* Send data over a socket, possibly including Mach ports. */
+error_t
+S_socket_send (struct sock_user *user, struct addr *dest_addr, int flags,
+ char *data, size_t data_len,
+ mach_port_t *ports, size_t num_ports,
+ char *control, size_t control_len,
+ size_t *amount)
+{
+ error_t err = 0;
+ struct pipe *pipe;
+ struct sock *sock, *dest_sock;
+ struct addr *source_addr;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ if (flags & MSG_OOB)
+ /* BSD local sockets don't support OOB data. */
+ return EOPNOTSUPP;
+
+ if (dest_addr)
+ {
+ err = addr_get_sock (dest_addr, &dest_sock);
+ if (err)
+ return err;
+ if (sock->pipe_class != dest_sock->pipe_class)
+ /* Sending to a different type of socket! */
+ err = EINVAL; /* ? XXX */
+ }
+ else
+ dest_sock = 0;
+
+ /* We could provide a source address for all writes, but we
+ only do so for connectionless sockets because that's the
+ only place it's required, and it's more efficient not to. */
+ if (!err && sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ err = sock_get_addr (sock, &source_addr);
+ else
+ source_addr = NULL;
+
+ if (!err)
+ {
+ if (dest_sock)
+ /* Grab the destination socket's read pipe directly, and stuff data
+ into it. This is not quite the usage sock_acquire_read_pipe was
+ intended for, but it will work, as the only inappropiate errors
+ occur on a broken pipe, which shouldn't be possible with the sort of
+ sockets with which we can use socket_send... XXXX */
+ err = sock_acquire_read_pipe (dest_sock, &pipe);
+ else
+ /* No address, must be a connected socket... */
+ err = sock_acquire_write_pipe (sock, &pipe);
+
+ if (!err)
+ {
+ err = pipe_send (pipe, sock->flags & SOCK_NONBLOCK,
+ source_addr, data, data_len,
+ control, control_len, ports, num_ports,
+ amount);
+ pipe_release_writer (pipe);
+ }
+
+ if (err)
+ /* The send failed, so free any resources it would have consumed
+ (mig gets rid of memory, but we have to do everything else). */
+ {
+ if (source_addr)
+ ports_port_deref (source_addr);
+ while (num_ports-- > 0)
+ mach_port_deallocate (mach_task_self (), *ports++);
+ }
+ }
+
+ if (dest_sock)
+ sock_deref (dest_sock);
+
+ return err;
+}
+
+/* Receive data from a socket, possibly including Mach ports. */
+error_t
+S_socket_recv (struct sock_user *user,
+ mach_port_t *addr, mach_msg_type_name_t *addr_type,
+ int in_flags,
+ char **data, size_t *data_len,
+ mach_port_t **ports, mach_msg_type_name_t *ports_type,
+ size_t *num_ports,
+ char **control, size_t *control_len,
+ int *out_flags, size_t amount)
+{
+ error_t err;
+ unsigned flags;
+ struct pipe *pipe;
+ void *source_addr = NULL;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ if (in_flags & MSG_OOB)
+ /* BSD local sockets don't support OOB data. */
+ return EINVAL; /* XXX */
+
+ /* Fill in the pipe FLAGS from any corresponding ones in IN_FLAGS. */
+ flags = 0;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ *data_len = 0;
+ if (num_ports)
+ *num_ports = 0;
+ if (control_len)
+ *control_len = 0;
+ }
+ else if (!err)
+ {
+ err =
+ pipe_recv (pipe, user->sock->flags & SOCK_NONBLOCK, &flags,
+ &source_addr, data, data_len, amount,
+ control, control_len, ports, num_ports);
+ pipe_release_reader (pipe);
+ }
+
+ if (!err)
+ /* Setup mach ports for return. */
+ {
+ *addr_type = MACH_MSG_TYPE_MAKE_SEND;
+ *ports_type = MACH_MSG_TYPE_MAKE_SEND;
+ if (source_addr)
+ {
+ *addr = ports_get_right (source_addr);
+ ports_port_deref (source_addr); /* since get_right has one too. */
+ }
+ else
+ *addr = MACH_PORT_NULL;
+ }
+
+ /* Fill in OUT_FLAGS from from any corresponding ones in FLAGS. */
+ out_flags = 0;
+
+ return err;
+}
+
+/* Stubs for currently unsupported rpcs. */
+
+error_t
+S_socket_getopt (struct sock_user *user,
+ int level, int opt,
+ char **value, size_t *value_len)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_socket_setopt (struct sock_user *user,
+ int level, int opt, char *value, size_t value_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/pflocal/sserver.c b/pflocal/sserver.c
new file mode 100644
index 00000000..7e00b323
--- /dev/null
+++ b/pflocal/sserver.c
@@ -0,0 +1,84 @@
+/* Server for socket ops
+
+ Copyright (C) 1995, 1997 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 <cthreads.h>
+
+#include <hurd/ports.h>
+
+/* A port bucket to handle SOCK_USERs and ADDRs. */
+struct port_bucket *sock_port_bucket;
+
+/* ---------------------------------------------------------------- */
+
+/* True if there are threads servicing sock requests. */
+static int sock_server_active = 0;
+static spin_lock_t sock_server_active_lock = SPIN_LOCK_INITIALIZER;
+
+/* A demuxer for socket operations. */
+static int
+sock_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int socket_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ extern int io_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ return
+ socket_server (inp, outp)
+ || io_server (inp, outp)
+ || ports_interrupt_server (inp, outp)
+ || ports_notify_server (inp, outp);
+}
+
+/* Handle socket requests while there are sockets around. */
+static void
+handle_sock_requests ()
+{
+ while (ports_count_bucket (sock_port_bucket) > 0)
+ {
+ ports_enable_bucket (sock_port_bucket);
+ ports_manage_port_operations_multithread (sock_port_bucket, sock_demuxer,
+ 30*1000, 2*60*1000, 0);
+ }
+
+ /* The last service thread is about to exist; make this known. */
+ spin_lock (&sock_server_active_lock);
+ sock_server_active = 0;
+ spin_unlock (&sock_server_active_lock);
+
+ /* Let the whole joke start once again. */
+ ports_enable_bucket (sock_port_bucket);
+}
+
+/* Makes sure there are some request threads for sock operations, and starts
+ a server if necessary. This routine should be called *after* creating the
+ port(s) which need server, as the server routine only operates while there
+ are any ports. */
+void
+ensure_sock_server ()
+{
+ spin_lock (&sock_server_active_lock);
+ if (sock_server_active)
+ spin_unlock (&sock_server_active_lock);
+ else
+ {
+ sock_server_active = 1;
+ spin_unlock (&sock_server_active_lock);
+ cthread_detach (cthread_fork ((cthread_fn_t)handle_sock_requests,
+ (any_t)0));
+ }
+}
diff --git a/pflocal/sserver.h b/pflocal/sserver.h
new file mode 100644
index 00000000..a61ad916
--- /dev/null
+++ b/pflocal/sserver.h
@@ -0,0 +1,33 @@
+/* Server for socket ops
+
+ 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. */
+
+#ifndef __SSERVER_H__
+#define __SSERVER_H__
+
+/* Makes sure there are some request threads for sock operations, and starts
+ a server if necessary. This routine should be called *after* creating the
+ port(s) which need server, as the server routine only operates while there
+ are any ports. */
+void ensure_sock_server ();
+
+/* A port bucket to handle SOCK_USERs and ADDRs. */
+struct port_bucket *sock_port_bucket;
+
+#endif /* __SSERVER_H__ */