summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pflocal/sock.c127
1 files changed, 98 insertions, 29 deletions
diff --git a/pflocal/sock.c b/pflocal/sock.c
index 056f632a..667ab708 100644
--- a/pflocal/sock.c
+++ b/pflocal/sock.c
@@ -129,21 +129,8 @@ sock_free (struct sock *sock)
/* But we must do the read pipe ourselves. */
pipe_release (sock->read_pipe);
-
-
free (sock);
}
-
-/* Remove a reference from SOCK, possibly freeing it. */
-void
-sock_unref (struct sock *sock)
-{
- mutex_lock (&sock->lock);
- if (--sock->refs == 0)
- sock_free (sock);
- else
- mutex_unlock (&sock->lock);
-}
/* ---------------------------------------------------------------- */
@@ -154,7 +141,19 @@ static void
clean_sock_user (void *vuser)
{
struct sock_user *user = vuser;
- sock_unref (user->sock);
+ struct sock *sock = user->sock;
+
+ /* Remove a reference from SOCK, possibly freeing it. */
+ mutex_lock (&sock->lock);
+ if (--sock->refs == 0)
+ {
+ /* 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);
+ sock_free (sock);
+ }
+ else
+ mutex_unlock (&sock->lock);
}
/* Return a new user port on SOCK in PORT. */
@@ -167,8 +166,8 @@ sock_create_port (struct sock *sock, mach_port_t *port)
sock_user_port_class = ports_create_class (NULL, clean_sock_user);
user =
- port_allocate_port (pflocal_port_bucket,
- sizeof (struct sock_user), sock_user_port_class);
+ ports_allocate_port (pflocal_port_bucket,
+ sizeof (struct sock_user), sock_user_port_class);
if (!user)
return ENOMEM;
@@ -185,45 +184,94 @@ sock_create_port (struct sock *sock, mach_port_t *port)
}
/* ---------------------------------------------------------------- */
+/* Address manipulation. */
+
+struct port_class *addr_port_class = NULL;
+
+/* Cleanup after the address ADDR, which is going away... */
+static void
+clean_addr (void *vaddr)
+{
+ struct addr *addr = vaddr;
+
+ struct sock *sock = addr->sock;
+ /* ... */
+}
+
+inline error_t
+addr_create (struct addr **addr)
+{
+ if (addr_port_class == NULL)
+ addr_port_class = ports_create_class (NULL, clean_addr);
+
+ *addr =
+ ports_allocate_port (pflocal_port_bucket,
+ sizeof (struct addr), addr_port_class);
+ if (! *addr)
+ return ENOMEM;
+
+ (*addr)->sock = NULL;
+ return 0;
+}
+
/* Bind SOCK to ADDR. */
error_t
sock_bind (struct sock *sock, struct addr *addr)
{
error_t err = 0;
+ struct addr *old_addr;
- mutex_lock (&sock->lock);
mutex_lock (&addr->lock);
+ mutex_lock (&sock->lock);
- if (addr && sock->addr)
+ 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
- sock->addr->sock = NULL; /* Unbinding SOCK. */
+ old_addr->sock = NULL; /* Unbinding SOCK. */
if (!err)
sock->addr = addr;
- mutex_unlock (&addr->lock);
+ 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 somehow. */
+ sock->refs--;
+ assert (sock->refs > 0); /* But make sure... */
+ }
+
mutex_unlock (&sock->lock);
+ mutex_unlock (&addr->lock);
return err;
}
/* Returns SOCK's addr, fabricating one if necessary. SOCK should be
locked. */
-static struct addr *
-ensure_addr (struct sock *sock)
+static inline error_t
+ensure_addr (struct sock *sock, struct addr **addr)
{
- if (! sock->addr)
+ error_t err = 0;
+
+ if (! sock->addr || sock->addr->refcnt == 0)
{
- addr_create (&sock->addr);
- sock->addr->sock = sock;
+ err = addr_create (&sock->addr);
+ if (!err)
+ {
+ sock->addr->sock = sock;
+ sock->refs++;
+ }
}
- return sock->addr;
+ *addr = sock->addr;
+ return err;
}
/* Returns a send right to SOCK's address in ADDR_PORT. If SOCK doesn't
@@ -231,10 +279,32 @@ ensure_addr (struct sock *sock)
error_t
sock_get_addr_port (struct sock *sock, mach_port_t *addr_port)
{
+ error_t err;
+ struct addr *addr;
+
mutex_lock (&sock->lock);
- *addr_port = ports_get_right (ensure_addr (sock));
+ err = ensure_addr (sock, addr);
+ if (!err)
+ *addr_port = ports_get_right (addr);
+ mutex_unlock (&sock->lock);
+
+ return err;
+}
+
+/* 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);
+ if (!err)
+ ports_port_ref (*addr);
mutex_unlock (&sock->lock);
- return 0; /* XXX */
+
+ return err; /* XXX */
}
/* If SOCK is a connected socket, returns a send right to SOCK's peer's
@@ -363,4 +433,3 @@ sock_shutdown (struct sock *sock, unsigned flags)
else
mutex_unlock (&sock->lock);
}
-