diff options
Diffstat (limited to 'pflocal')
-rw-r--r-- | pflocal/ChangeLog | 305 | ||||
-rw-r--r-- | pflocal/Makefile | 33 | ||||
-rw-r--r-- | pflocal/connq.c | 275 | ||||
-rw-r--r-- | pflocal/connq.h | 58 | ||||
-rw-r--r-- | pflocal/io.c | 622 | ||||
-rw-r--r-- | pflocal/mig-decls.h | 59 | ||||
-rw-r--r-- | pflocal/mig-mutate.h | 33 | ||||
-rw-r--r-- | pflocal/pf.c | 123 | ||||
-rw-r--r-- | pflocal/pflocal.c | 130 | ||||
-rw-r--r-- | pflocal/sock.c | 504 | ||||
-rw-r--r-- | pflocal/sock.h | 161 | ||||
-rw-r--r-- | pflocal/socket.c | 425 | ||||
-rw-r--r-- | pflocal/sserver.c | 84 | ||||
-rw-r--r-- | pflocal/sserver.h | 33 |
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__ */ |