summaryrefslogtreecommitdiff
path: root/pfinet/socket.c
blob: 5ab292ea36e857be4b19a0bdbe0fdbf46bd9270f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
   Copyright (C) 1995,2000,02 Free Software Foundation, Inc.

   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., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */

#define _HACK_ERRNO_H
#include <assert-backtrace.h>
#include "pfinet.h"

#include <linux/socket.h>
#include <linux/net.h>

#ifndef NPROTO
#define NPROTO (PF_INET + 1)
#endif
struct net_proto_family *net_families[NPROTO];

/* Notice that a protocol family is live; this only works for inet here. */
int
sock_register (struct net_proto_family *fam)
{
  assert_backtrace (fam->family < NPROTO);
  net_families[fam->family] = fam;
  return 0;
}


struct socket *
sock_alloc (void)
{
  static ino_t nextino;		/* locked by global_lock */
  struct socket *sock;
  pthread_cond_t *c;

  sock = malloc (sizeof *sock + sizeof (pthread_cond_t));
  if (!sock)
    return 0;
  c = (void *) &sock[1];
  pthread_cond_init (c, NULL);
  memset (sock, 0, sizeof *sock);
  sock->state = SS_UNCONNECTED;
  sock->identity = MACH_PORT_NULL;
  sock->refcnt = 1;
  sock->wait = (void *) c;

  if (nextino == 0)
    nextino = 2;
  sock->st_ino = nextino++;

  return sock;
}

/* Create a sock_user structure, initialized from SOCK and ISROOT.
   If NOINSTALL is set, don't put it in the portset.
   We increment SOCK->refcnt iff CONSUME is zero.  */
struct sock_user *
make_sock_user (struct socket *sock, int isroot, int noinstall, int consume)
{
  error_t err;
  struct sock_user *user;

  assert_backtrace (sock->refcnt != 0);

  if (noinstall)
    err = ports_create_port_noinstall (socketport_class, pfinet_bucket,
				       sizeof (struct sock_user), &user);
  else
    err = ports_create_port (socketport_class, pfinet_bucket,
			     sizeof (struct sock_user), &user);
  if (err)
    {
      errno = err;
      return 0;
    }

  /* We maintain a reference count in `struct socket' (a member not
     in the original Linux structure), because there can be multiple
     ports (struct sock_user, aka protids) pointing to the same socket.
     The socket lives until all the ports die.  */
  if (! consume)
    ++sock->refcnt;
  user->isroot = isroot;
  user->sock = sock;
  return user;
}

/* This is called from the port cleanup function below, and on
   a newly allocated socket when something went wrong in its creation.  */
void
sock_release (struct socket *sock)
{
  if (--sock->refcnt != 0)
    return;

  if (sock->state != SS_UNCONNECTED)
    sock->state = SS_DISCONNECTING;

  if (sock->ops)
    sock->ops->release(sock, NULL);

  if (sock->identity != MACH_PORT_NULL)
    mach_port_destroy (mach_task_self (), sock->identity);

  free (sock);
}

/* Release the reference on the referenced socket. */
void
clean_socketport (void *arg)
{
  struct sock_user *const user = arg;

  pthread_mutex_lock (&global_lock);
  sock_release (user->sock);
  pthread_mutex_unlock (&global_lock);
}