1*27852ebeSDavid van Moolenbroek /* UNIX Domain Sockets - uds.c - socket management */
2cc5b1988SDavid van Moolenbroek
3cc5b1988SDavid van Moolenbroek #include "uds.h"
4cc5b1988SDavid van Moolenbroek
5*27852ebeSDavid van Moolenbroek static struct udssock uds_array[NR_UDSSOCK];
6*27852ebeSDavid van Moolenbroek static TAILQ_HEAD(uds_freelist, udssock) uds_freelist;
7*27852ebeSDavid van Moolenbroek static unsigned int uds_in_use;
8*27852ebeSDavid van Moolenbroek static int uds_running;
9cc5b1988SDavid van Moolenbroek
10*27852ebeSDavid van Moolenbroek static const struct sockevent_ops uds_ops;
11cc5b1988SDavid van Moolenbroek
SLIST_HEAD(udshash,udssock)12*27852ebeSDavid van Moolenbroek static SLIST_HEAD(udshash, udssock) udshash[UDSHASH_SLOTS];
13cc5b1988SDavid van Moolenbroek
14cc5b1988SDavid van Moolenbroek /*
15*27852ebeSDavid van Moolenbroek * Initialize file-to-socket hash table.
16cc5b1988SDavid van Moolenbroek */
17cc5b1988SDavid van Moolenbroek static void
18*27852ebeSDavid van Moolenbroek udshash_init(void)
19cc5b1988SDavid van Moolenbroek {
20*27852ebeSDavid van Moolenbroek unsigned int slot;
21cc5b1988SDavid van Moolenbroek
22*27852ebeSDavid van Moolenbroek for (slot = 0; slot < __arraycount(udshash); slot++)
23*27852ebeSDavid van Moolenbroek SLIST_INIT(&udshash[slot]);
24*27852ebeSDavid van Moolenbroek }
25cc5b1988SDavid van Moolenbroek
26*27852ebeSDavid van Moolenbroek /*
27*27852ebeSDavid van Moolenbroek * Return a hash table slot number for the given <dev,ino> pair.
28*27852ebeSDavid van Moolenbroek */
29*27852ebeSDavid van Moolenbroek static unsigned int
udshash_slot(dev_t dev,ino_t ino)30*27852ebeSDavid van Moolenbroek udshash_slot(dev_t dev, ino_t ino)
31*27852ebeSDavid van Moolenbroek {
32cc5b1988SDavid van Moolenbroek
33*27852ebeSDavid van Moolenbroek assert(dev != NO_DEV);
34*27852ebeSDavid van Moolenbroek assert(ino != 0);
35*27852ebeSDavid van Moolenbroek
36*27852ebeSDavid van Moolenbroek /*
37*27852ebeSDavid van Moolenbroek * Effectively combining two 64-bit numbers into a single 6-or-so-bit
38*27852ebeSDavid van Moolenbroek * hash is not too easy. This hash function is probably among the
39*27852ebeSDavid van Moolenbroek * worst options. Then again it is not all that critical as we are not
40*27852ebeSDavid van Moolenbroek * expecting that many bound UDS sockets in the system anyway.
41*27852ebeSDavid van Moolenbroek */
42*27852ebeSDavid van Moolenbroek return (unsigned int)(dev ^ ino) % UDSHASH_SLOTS;
43*27852ebeSDavid van Moolenbroek }
44*27852ebeSDavid van Moolenbroek
45*27852ebeSDavid van Moolenbroek /*
46*27852ebeSDavid van Moolenbroek * Look for a socket that is bound to the given <dev,ino> pair. Return a
47*27852ebeSDavid van Moolenbroek * pointer to the socket if found, or NULL otherwise.
48*27852ebeSDavid van Moolenbroek */
49*27852ebeSDavid van Moolenbroek static struct udssock *
udshash_get(dev_t dev,ino_t ino)50*27852ebeSDavid van Moolenbroek udshash_get(dev_t dev, ino_t ino)
51*27852ebeSDavid van Moolenbroek {
52*27852ebeSDavid van Moolenbroek struct udssock *uds;
53*27852ebeSDavid van Moolenbroek unsigned int slot;
54*27852ebeSDavid van Moolenbroek
55*27852ebeSDavid van Moolenbroek slot = udshash_slot(dev, ino);
56*27852ebeSDavid van Moolenbroek
57*27852ebeSDavid van Moolenbroek SLIST_FOREACH(uds, &udshash[slot], uds_hash) {
58*27852ebeSDavid van Moolenbroek if (uds->uds_dev == dev && uds->uds_ino == ino)
59*27852ebeSDavid van Moolenbroek return uds;
60*27852ebeSDavid van Moolenbroek }
61*27852ebeSDavid van Moolenbroek
62*27852ebeSDavid van Moolenbroek return NULL;
63*27852ebeSDavid van Moolenbroek }
64*27852ebeSDavid van Moolenbroek
65*27852ebeSDavid van Moolenbroek /*
66*27852ebeSDavid van Moolenbroek * Add a socket to the file-to-socket hash table. The socket must have its
67*27852ebeSDavid van Moolenbroek * device and inode fields set, and must not be in the hash table already.
68*27852ebeSDavid van Moolenbroek */
69*27852ebeSDavid van Moolenbroek static void
udshash_add(struct udssock * uds)70*27852ebeSDavid van Moolenbroek udshash_add(struct udssock * uds)
71*27852ebeSDavid van Moolenbroek {
72*27852ebeSDavid van Moolenbroek unsigned int slot;
73*27852ebeSDavid van Moolenbroek
74*27852ebeSDavid van Moolenbroek slot = udshash_slot(uds->uds_dev, uds->uds_ino);
75*27852ebeSDavid van Moolenbroek
76*27852ebeSDavid van Moolenbroek SLIST_INSERT_HEAD(&udshash[slot], uds, uds_hash);
77*27852ebeSDavid van Moolenbroek }
78*27852ebeSDavid van Moolenbroek
79*27852ebeSDavid van Moolenbroek /*
80*27852ebeSDavid van Moolenbroek * Remove a socket from the file-to-socket hash table. The socket must be in
81*27852ebeSDavid van Moolenbroek * the hash table.
82*27852ebeSDavid van Moolenbroek */
83*27852ebeSDavid van Moolenbroek static void
udshash_del(struct udssock * uds)84*27852ebeSDavid van Moolenbroek udshash_del(struct udssock * uds)
85*27852ebeSDavid van Moolenbroek {
86*27852ebeSDavid van Moolenbroek unsigned int slot;
87*27852ebeSDavid van Moolenbroek
88*27852ebeSDavid van Moolenbroek slot = udshash_slot(uds->uds_dev, uds->uds_ino);
89*27852ebeSDavid van Moolenbroek
90*27852ebeSDavid van Moolenbroek /* This macro is O(n). */
91*27852ebeSDavid van Moolenbroek SLIST_REMOVE(&udshash[slot], uds, udssock, uds_hash);
92*27852ebeSDavid van Moolenbroek }
93*27852ebeSDavid van Moolenbroek
94*27852ebeSDavid van Moolenbroek /*
95*27852ebeSDavid van Moolenbroek * Return the socket identifier for the given UDS socket object.
96*27852ebeSDavid van Moolenbroek */
97*27852ebeSDavid van Moolenbroek sockid_t
uds_get_id(struct udssock * uds)98*27852ebeSDavid van Moolenbroek uds_get_id(struct udssock * uds)
99*27852ebeSDavid van Moolenbroek {
100*27852ebeSDavid van Moolenbroek
101*27852ebeSDavid van Moolenbroek return (sockid_t)(uds - uds_array);
102*27852ebeSDavid van Moolenbroek }
103*27852ebeSDavid van Moolenbroek
104*27852ebeSDavid van Moolenbroek /*
105*27852ebeSDavid van Moolenbroek * Given either NULL or a previously returned socket, return the next in-use
106*27852ebeSDavid van Moolenbroek * UDS socket of the given socket type, or NULL if there are no more matches.
107*27852ebeSDavid van Moolenbroek * The sockets are returned in random order, but each matching socket is
108*27852ebeSDavid van Moolenbroek * returned exactly once (until any socket is allocated or freed).
109*27852ebeSDavid van Moolenbroek */
110*27852ebeSDavid van Moolenbroek struct udssock *
uds_enum(struct udssock * prev,int type)111*27852ebeSDavid van Moolenbroek uds_enum(struct udssock * prev, int type)
112*27852ebeSDavid van Moolenbroek {
113*27852ebeSDavid van Moolenbroek sockid_t id;
114*27852ebeSDavid van Moolenbroek
115*27852ebeSDavid van Moolenbroek if (prev != NULL)
116*27852ebeSDavid van Moolenbroek id = uds_get_id(prev) + 1;
117*27852ebeSDavid van Moolenbroek else
118*27852ebeSDavid van Moolenbroek id = 0;
119*27852ebeSDavid van Moolenbroek
120*27852ebeSDavid van Moolenbroek for (; id < NR_UDSSOCK; id++)
121*27852ebeSDavid van Moolenbroek if ((uds_array[id].uds_flags & UDSF_IN_USE) &&
122*27852ebeSDavid van Moolenbroek uds_get_type(&uds_array[id]) == type)
123*27852ebeSDavid van Moolenbroek return &uds_array[id];
124*27852ebeSDavid van Moolenbroek
125*27852ebeSDavid van Moolenbroek return NULL;
126*27852ebeSDavid van Moolenbroek }
127*27852ebeSDavid van Moolenbroek
128*27852ebeSDavid van Moolenbroek /*
129*27852ebeSDavid van Moolenbroek * Invalidate credentials on the socket.
130*27852ebeSDavid van Moolenbroek */
131*27852ebeSDavid van Moolenbroek static void
uds_clear_cred(struct udssock * uds)132*27852ebeSDavid van Moolenbroek uds_clear_cred(struct udssock * uds)
133*27852ebeSDavid van Moolenbroek {
134*27852ebeSDavid van Moolenbroek
135*27852ebeSDavid van Moolenbroek uds->uds_cred.unp_pid = -1;
136*27852ebeSDavid van Moolenbroek uds->uds_cred.unp_euid = -1;
137*27852ebeSDavid van Moolenbroek uds->uds_cred.unp_egid = -1;
138*27852ebeSDavid van Moolenbroek }
139*27852ebeSDavid van Moolenbroek
140*27852ebeSDavid van Moolenbroek /*
141*27852ebeSDavid van Moolenbroek * Obtain the credentials (process, user, and group ID) of the given user
142*27852ebeSDavid van Moolenbroek * endpoint and associate them with the socket for later retrieval. It is
143*27852ebeSDavid van Moolenbroek * important to note that this information is obtained once at connect time,
144*27852ebeSDavid van Moolenbroek * and never updated later. The party receiving the credentials must take this
145*27852ebeSDavid van Moolenbroek * into account.
146*27852ebeSDavid van Moolenbroek */
147*27852ebeSDavid van Moolenbroek static void
uds_get_cred(struct udssock * uds,endpoint_t user_endpt)148*27852ebeSDavid van Moolenbroek uds_get_cred(struct udssock * uds, endpoint_t user_endpt)
149*27852ebeSDavid van Moolenbroek {
150*27852ebeSDavid van Moolenbroek int r;
151*27852ebeSDavid van Moolenbroek
152*27852ebeSDavid van Moolenbroek if ((uds->uds_cred.unp_pid = r = getepinfo(user_endpt,
153*27852ebeSDavid van Moolenbroek &uds->uds_cred.unp_euid, &uds->uds_cred.unp_egid)) < 0) {
154*27852ebeSDavid van Moolenbroek printf("UDS: failed obtaining credentials of %d (%d)\n",
155*27852ebeSDavid van Moolenbroek user_endpt, r);
156*27852ebeSDavid van Moolenbroek
157*27852ebeSDavid van Moolenbroek uds_clear_cred(uds);
158cc5b1988SDavid van Moolenbroek }
159cc5b1988SDavid van Moolenbroek }
160cc5b1988SDavid van Moolenbroek
161*27852ebeSDavid van Moolenbroek /*
162*27852ebeSDavid van Moolenbroek * Allocate and initialize a UDS socket. On succes, return OK with a pointer
163*27852ebeSDavid van Moolenbroek * to the new socket in 'udsp'. On failure, return a negative error code.
164*27852ebeSDavid van Moolenbroek */
165cc5b1988SDavid van Moolenbroek static int
uds_alloc(struct udssock ** udsp)166*27852ebeSDavid van Moolenbroek uds_alloc(struct udssock ** udsp)
167cc5b1988SDavid van Moolenbroek {
168*27852ebeSDavid van Moolenbroek struct udssock *uds;
169*27852ebeSDavid van Moolenbroek int r;
170cc5b1988SDavid van Moolenbroek
171*27852ebeSDavid van Moolenbroek /* Allocate, initialize, and return a UNIX domain socket object. */
172*27852ebeSDavid van Moolenbroek if (TAILQ_EMPTY(&uds_freelist))
173*27852ebeSDavid van Moolenbroek return ENOBUFS;
174cc5b1988SDavid van Moolenbroek
175*27852ebeSDavid van Moolenbroek uds = TAILQ_FIRST(&uds_freelist);
176cc5b1988SDavid van Moolenbroek
177*27852ebeSDavid van Moolenbroek uds->uds_conn = NULL; /* not connected */
178*27852ebeSDavid van Moolenbroek uds->uds_link = NULL; /* not connecting or linked */
179*27852ebeSDavid van Moolenbroek uds->uds_queued = 0;
180*27852ebeSDavid van Moolenbroek uds->uds_flags = UDSF_IN_USE; /* may be found through enumeration */
181*27852ebeSDavid van Moolenbroek uds->uds_pathlen = 0; /* not bound: no path */
182*27852ebeSDavid van Moolenbroek uds->uds_dev = NO_DEV; /* not hashed: no socket file device */
183*27852ebeSDavid van Moolenbroek uds->uds_ino = 0; /* not hashed: no socket file inode */
184*27852ebeSDavid van Moolenbroek uds_clear_cred(uds); /* no bind/connect-time credentials */
185*27852ebeSDavid van Moolenbroek TAILQ_INIT(&uds->uds_queue); /* an empty queue */
186*27852ebeSDavid van Moolenbroek
187*27852ebeSDavid van Moolenbroek if ((r = uds_io_setup(uds)) != OK)
188*27852ebeSDavid van Moolenbroek return r;
189*27852ebeSDavid van Moolenbroek
190*27852ebeSDavid van Moolenbroek TAILQ_REMOVE(&uds_freelist, uds, uds_next);
191*27852ebeSDavid van Moolenbroek
192*27852ebeSDavid van Moolenbroek assert(uds_in_use < NR_UDSSOCK);
193*27852ebeSDavid van Moolenbroek uds_in_use++;
194*27852ebeSDavid van Moolenbroek
195*27852ebeSDavid van Moolenbroek *udsp = uds;
196*27852ebeSDavid van Moolenbroek return OK;
197*27852ebeSDavid van Moolenbroek }
198*27852ebeSDavid van Moolenbroek
199*27852ebeSDavid van Moolenbroek /*
200*27852ebeSDavid van Moolenbroek * Free a previously allocated socket.
201*27852ebeSDavid van Moolenbroek */
202*27852ebeSDavid van Moolenbroek static void
uds_free(struct sock * sock)203*27852ebeSDavid van Moolenbroek uds_free(struct sock * sock)
204*27852ebeSDavid van Moolenbroek {
205*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
206*27852ebeSDavid van Moolenbroek
207*27852ebeSDavid van Moolenbroek uds_io_cleanup(uds);
208*27852ebeSDavid van Moolenbroek
209*27852ebeSDavid van Moolenbroek uds->uds_flags = 0; /* no longer in use */
210*27852ebeSDavid van Moolenbroek
211*27852ebeSDavid van Moolenbroek TAILQ_INSERT_HEAD(&uds_freelist, uds, uds_next);
212*27852ebeSDavid van Moolenbroek
213*27852ebeSDavid van Moolenbroek assert(uds_in_use > 0);
214*27852ebeSDavid van Moolenbroek if (--uds_in_use == 0 && uds_running == FALSE)
215*27852ebeSDavid van Moolenbroek sef_cancel();
216*27852ebeSDavid van Moolenbroek }
217*27852ebeSDavid van Moolenbroek
218*27852ebeSDavid van Moolenbroek /*
219*27852ebeSDavid van Moolenbroek * Create a new socket.
220*27852ebeSDavid van Moolenbroek */
221*27852ebeSDavid van Moolenbroek static sockid_t
uds_socket(int domain,int type,int protocol,endpoint_t user_endpt __unused,struct sock ** sockp,const struct sockevent_ops ** ops)222*27852ebeSDavid van Moolenbroek uds_socket(int domain, int type, int protocol, endpoint_t user_endpt __unused,
223*27852ebeSDavid van Moolenbroek struct sock ** sockp, const struct sockevent_ops ** ops)
224*27852ebeSDavid van Moolenbroek {
225*27852ebeSDavid van Moolenbroek struct udssock *uds;
226*27852ebeSDavid van Moolenbroek int r;
227*27852ebeSDavid van Moolenbroek
228*27852ebeSDavid van Moolenbroek dprintf(("UDS: socket(%d,%d,%d)\n", domain, type, protocol));
229*27852ebeSDavid van Moolenbroek
230*27852ebeSDavid van Moolenbroek if (domain != PF_UNIX) {
231*27852ebeSDavid van Moolenbroek /* This means the service was configured incorrectly. */
232*27852ebeSDavid van Moolenbroek printf("UDS: got request for domain %d\n", domain);
233*27852ebeSDavid van Moolenbroek
234*27852ebeSDavid van Moolenbroek return EAFNOSUPPORT;
235*27852ebeSDavid van Moolenbroek }
236*27852ebeSDavid van Moolenbroek
237*27852ebeSDavid van Moolenbroek /* We support the following three socket types. */
238*27852ebeSDavid van Moolenbroek switch (type) {
239*27852ebeSDavid van Moolenbroek case SOCK_STREAM:
240*27852ebeSDavid van Moolenbroek case SOCK_SEQPACKET:
241*27852ebeSDavid van Moolenbroek case SOCK_DGRAM:
242*27852ebeSDavid van Moolenbroek break;
243*27852ebeSDavid van Moolenbroek default:
244*27852ebeSDavid van Moolenbroek return EPROTOTYPE;
245*27852ebeSDavid van Moolenbroek }
246*27852ebeSDavid van Moolenbroek
247*27852ebeSDavid van Moolenbroek /*
248*27852ebeSDavid van Moolenbroek * The PF_UNIX domain does not support particular protocols, so the
249*27852ebeSDavid van Moolenbroek * given protocol must be zero (= anything that matches).
250*27852ebeSDavid van Moolenbroek */
251*27852ebeSDavid van Moolenbroek if (protocol != UDSPROTO_UDS)
252*27852ebeSDavid van Moolenbroek return EPROTONOSUPPORT;
253*27852ebeSDavid van Moolenbroek
254*27852ebeSDavid van Moolenbroek if ((r = uds_alloc(&uds)) != OK)
255*27852ebeSDavid van Moolenbroek return r;
256*27852ebeSDavid van Moolenbroek
257*27852ebeSDavid van Moolenbroek dprintf(("UDS: socket returns %d\n", uds_get_id(uds)));
258*27852ebeSDavid van Moolenbroek
259*27852ebeSDavid van Moolenbroek *sockp = &uds->uds_sock;
260*27852ebeSDavid van Moolenbroek *ops = &uds_ops;
261*27852ebeSDavid van Moolenbroek return uds_get_id(uds);
262*27852ebeSDavid van Moolenbroek }
263*27852ebeSDavid van Moolenbroek
264*27852ebeSDavid van Moolenbroek /*
265*27852ebeSDavid van Moolenbroek * Connect a pair of sockets.
266*27852ebeSDavid van Moolenbroek */
267*27852ebeSDavid van Moolenbroek static int
uds_pair(struct sock * sock1,struct sock * sock2,endpoint_t user_endpt)268*27852ebeSDavid van Moolenbroek uds_pair(struct sock * sock1, struct sock * sock2, endpoint_t user_endpt)
269*27852ebeSDavid van Moolenbroek {
270*27852ebeSDavid van Moolenbroek struct udssock *uds1 = (struct udssock *)sock1;
271*27852ebeSDavid van Moolenbroek struct udssock *uds2 = (struct udssock *)sock2;
272*27852ebeSDavid van Moolenbroek
273*27852ebeSDavid van Moolenbroek dprintf(("UDS: pair(%d,%d)\n", uds_get_id(uds1), uds_get_id(uds2)));
274*27852ebeSDavid van Moolenbroek
275*27852ebeSDavid van Moolenbroek /* Only connection-oriented types are acceptable. */
276*27852ebeSDavid van Moolenbroek if (uds_get_type(uds1) == SOCK_DGRAM)
277*27852ebeSDavid van Moolenbroek return EOPNOTSUPP;
278*27852ebeSDavid van Moolenbroek
279*27852ebeSDavid van Moolenbroek /* Connect the sockets. */
280*27852ebeSDavid van Moolenbroek uds1->uds_conn = uds2;
281*27852ebeSDavid van Moolenbroek uds2->uds_conn = uds1;
282*27852ebeSDavid van Moolenbroek uds1->uds_flags |= UDSF_CONNECTED;
283*27852ebeSDavid van Moolenbroek uds2->uds_flags |= UDSF_CONNECTED;
284*27852ebeSDavid van Moolenbroek
285*27852ebeSDavid van Moolenbroek /* Obtain the (same) credentials for both sides of the connection. */
286*27852ebeSDavid van Moolenbroek uds_get_cred(uds1, user_endpt);
287*27852ebeSDavid van Moolenbroek memcpy(&uds2->uds_cred, &uds1->uds_cred, sizeof(uds2->uds_cred));
288*27852ebeSDavid van Moolenbroek
289*27852ebeSDavid van Moolenbroek return OK;
290*27852ebeSDavid van Moolenbroek }
291*27852ebeSDavid van Moolenbroek
292*27852ebeSDavid van Moolenbroek /*
293*27852ebeSDavid van Moolenbroek * Disconnect a UDS socket, notifying or freeing up the other end of the
294*27852ebeSDavid van Moolenbroek * connection depending on whether the socket was linked, that is, on the
295*27852ebeSDavid van Moolenbroek * accept queue of a listening socket.
296*27852ebeSDavid van Moolenbroek */
297*27852ebeSDavid van Moolenbroek static void
uds_disconnect(struct udssock * uds,int was_linked)298*27852ebeSDavid van Moolenbroek uds_disconnect(struct udssock * uds, int was_linked)
299*27852ebeSDavid van Moolenbroek {
300*27852ebeSDavid van Moolenbroek struct udssock *conn;
301*27852ebeSDavid van Moolenbroek
302*27852ebeSDavid van Moolenbroek assert(uds_is_connected(uds));
303*27852ebeSDavid van Moolenbroek assert(uds_has_conn(uds));
304*27852ebeSDavid van Moolenbroek
305*27852ebeSDavid van Moolenbroek conn = uds->uds_conn;
306*27852ebeSDavid van Moolenbroek
307*27852ebeSDavid van Moolenbroek assert(uds_is_connected(conn));
308*27852ebeSDavid van Moolenbroek assert(uds_has_conn(conn));
309*27852ebeSDavid van Moolenbroek assert(!uds_has_link(conn));
310*27852ebeSDavid van Moolenbroek assert(conn->uds_conn == uds);
311*27852ebeSDavid van Moolenbroek
312*27852ebeSDavid van Moolenbroek /* Disconnect the sockets. */
313*27852ebeSDavid van Moolenbroek uds->uds_conn = NULL;
314*27852ebeSDavid van Moolenbroek conn->uds_conn = NULL;
315*27852ebeSDavid van Moolenbroek
316*27852ebeSDavid van Moolenbroek /*
317*27852ebeSDavid van Moolenbroek * If the given socket is linked, then it is a connected socket for
318*27852ebeSDavid van Moolenbroek * which the other end has been created but not yet accepted. In that
319*27852ebeSDavid van Moolenbroek * case, the other end ('conn') will have to be freed up. Otherwise,
320*27852ebeSDavid van Moolenbroek * it is a regular user-created socket and we must properly transition
321*27852ebeSDavid van Moolenbroek * it into disconnected state.
322*27852ebeSDavid van Moolenbroek */
323*27852ebeSDavid van Moolenbroek if (!was_linked) {
324*27852ebeSDavid van Moolenbroek sockevent_raise(&conn->uds_sock, SEV_SEND | SEV_RECV);
325*27852ebeSDavid van Moolenbroek
326*27852ebeSDavid van Moolenbroek /*
327*27852ebeSDavid van Moolenbroek * Clear the peer credentials so that they will not be mistaken
328*27852ebeSDavid van Moolenbroek * for having been obtained at bind time.
329*27852ebeSDavid van Moolenbroek */
330*27852ebeSDavid van Moolenbroek uds_clear_cred(conn);
331*27852ebeSDavid van Moolenbroek } else
332*27852ebeSDavid van Moolenbroek sockevent_raise(&conn->uds_sock, SEV_CLOSE);
333*27852ebeSDavid van Moolenbroek }
334*27852ebeSDavid van Moolenbroek
335*27852ebeSDavid van Moolenbroek /*
336*27852ebeSDavid van Moolenbroek * Add the socket 'link' to the queue of the socket 'uds'. This also implies
337*27852ebeSDavid van Moolenbroek * that 'link's link socket is set to 'uds'.
338*27852ebeSDavid van Moolenbroek */
339*27852ebeSDavid van Moolenbroek static void
uds_add_queue(struct udssock * uds,struct udssock * link)340*27852ebeSDavid van Moolenbroek uds_add_queue(struct udssock * uds, struct udssock * link)
341*27852ebeSDavid van Moolenbroek {
342*27852ebeSDavid van Moolenbroek
343*27852ebeSDavid van Moolenbroek dprintf(("UDS: add_queue(%d,%d)\n",
344*27852ebeSDavid van Moolenbroek uds_get_id(uds), uds_get_id(link)));
345*27852ebeSDavid van Moolenbroek
346*27852ebeSDavid van Moolenbroek TAILQ_INSERT_TAIL(&uds->uds_queue, link, uds_next);
347*27852ebeSDavid van Moolenbroek
348*27852ebeSDavid van Moolenbroek uds->uds_queued++;
349*27852ebeSDavid van Moolenbroek assert(uds->uds_queued != 0);
350*27852ebeSDavid van Moolenbroek
351*27852ebeSDavid van Moolenbroek link->uds_link = uds;
352*27852ebeSDavid van Moolenbroek }
353*27852ebeSDavid van Moolenbroek
354*27852ebeSDavid van Moolenbroek /*
355*27852ebeSDavid van Moolenbroek * Remove the socket 'link' from the queue of the socket 'uds'. This also
356*27852ebeSDavid van Moolenbroek * reset 'link's link to NULL.
357*27852ebeSDavid van Moolenbroek */
358*27852ebeSDavid van Moolenbroek static void
uds_del_queue(struct udssock * uds,struct udssock * link)359*27852ebeSDavid van Moolenbroek uds_del_queue(struct udssock * uds, struct udssock * link)
360*27852ebeSDavid van Moolenbroek {
361*27852ebeSDavid van Moolenbroek
362*27852ebeSDavid van Moolenbroek dprintf(("UDS: del_queue(%d,%d)\n",
363*27852ebeSDavid van Moolenbroek uds_get_id(uds), uds_get_id(link)));
364*27852ebeSDavid van Moolenbroek
365*27852ebeSDavid van Moolenbroek assert(link->uds_link == uds);
366*27852ebeSDavid van Moolenbroek
367*27852ebeSDavid van Moolenbroek TAILQ_REMOVE(&uds->uds_queue, link, uds_next);
368*27852ebeSDavid van Moolenbroek
369*27852ebeSDavid van Moolenbroek assert(uds->uds_queued > 0);
370*27852ebeSDavid van Moolenbroek uds->uds_queued--;
371*27852ebeSDavid van Moolenbroek
372*27852ebeSDavid van Moolenbroek link->uds_link = NULL;
373*27852ebeSDavid van Moolenbroek }
374*27852ebeSDavid van Moolenbroek
375*27852ebeSDavid van Moolenbroek /*
376*27852ebeSDavid van Moolenbroek * Remove all sockets from the queue of the socket 'uds', with the exception of
377*27852ebeSDavid van Moolenbroek * 'except' if non-NULL. Raise an ECONNRESET error on all removed sockets that
378*27852ebeSDavid van Moolenbroek * are not equal to 'uds'.
379*27852ebeSDavid van Moolenbroek */
380*27852ebeSDavid van Moolenbroek static void
uds_clear_queue(struct udssock * uds,struct udssock * except)381*27852ebeSDavid van Moolenbroek uds_clear_queue(struct udssock * uds, struct udssock * except)
382*27852ebeSDavid van Moolenbroek {
383*27852ebeSDavid van Moolenbroek struct udssock *link, *tmp;
384*27852ebeSDavid van Moolenbroek int found;
385*27852ebeSDavid van Moolenbroek
386*27852ebeSDavid van Moolenbroek dprintf(("UDS: clear_queue(%d,%d)\n",
387*27852ebeSDavid van Moolenbroek uds_get_id(uds), (except != NULL) ? uds_get_id(except) : -1));
388*27852ebeSDavid van Moolenbroek
389*27852ebeSDavid van Moolenbroek found = 0;
390*27852ebeSDavid van Moolenbroek
391*27852ebeSDavid van Moolenbroek /*
392*27852ebeSDavid van Moolenbroek * Abort all connecting sockets queued on this socket, except for the
393*27852ebeSDavid van Moolenbroek * given exception, which may be NULL.
394*27852ebeSDavid van Moolenbroek */
395*27852ebeSDavid van Moolenbroek TAILQ_FOREACH_SAFE(link, &uds->uds_queue, uds_next, tmp) {
396*27852ebeSDavid van Moolenbroek if (link == except) {
397*27852ebeSDavid van Moolenbroek found++;
398*27852ebeSDavid van Moolenbroek
399*27852ebeSDavid van Moolenbroek continue;
400*27852ebeSDavid van Moolenbroek }
401*27852ebeSDavid van Moolenbroek
402*27852ebeSDavid van Moolenbroek dprintf(("UDS: clear_queue removes %d\n", uds_get_id(link)));
403*27852ebeSDavid van Moolenbroek
404*27852ebeSDavid van Moolenbroek assert(uds_get_type(link) == SOCK_DGRAM ||
405*27852ebeSDavid van Moolenbroek uds_is_connecting(link) || uds_is_connected(link));
406*27852ebeSDavid van Moolenbroek
407*27852ebeSDavid van Moolenbroek uds_del_queue(uds, link);
408*27852ebeSDavid van Moolenbroek
409*27852ebeSDavid van Moolenbroek /*
410*27852ebeSDavid van Moolenbroek * Generate an error only if the socket was not linked to
411*27852ebeSDavid van Moolenbroek * itself (only datagram sockets can be linked to themselves).
412*27852ebeSDavid van Moolenbroek * The error is not helpful for applications in that case.
413*27852ebeSDavid van Moolenbroek */
414*27852ebeSDavid van Moolenbroek if (uds != link)
415*27852ebeSDavid van Moolenbroek sockevent_set_error(&link->uds_sock, ECONNRESET);
416*27852ebeSDavid van Moolenbroek
417*27852ebeSDavid van Moolenbroek /*
418*27852ebeSDavid van Moolenbroek * If this is a listening socket, disconnect the connecting or
419*27852ebeSDavid van Moolenbroek * connected end. If a connected peer was already created for
420*27852ebeSDavid van Moolenbroek * the queued socket, dispose of that peer.
421*27852ebeSDavid van Moolenbroek *
422*27852ebeSDavid van Moolenbroek * Clear credentials obtained when starting to connect (in
423*27852ebeSDavid van Moolenbroek * which case the socket is always a connection-oriented
424*27852ebeSDavid van Moolenbroek * socket), so that they will not be mistaken for credentials
425*27852ebeSDavid van Moolenbroek * obtained at bind time.
426*27852ebeSDavid van Moolenbroek */
427*27852ebeSDavid van Moolenbroek if (uds_get_type(link) != SOCK_DGRAM) {
428*27852ebeSDavid van Moolenbroek if (uds_is_connected(link))
429*27852ebeSDavid van Moolenbroek uds_disconnect(link, TRUE /*was_linked*/);
430*27852ebeSDavid van Moolenbroek else
431*27852ebeSDavid van Moolenbroek uds_clear_cred(link);
432*27852ebeSDavid van Moolenbroek }
433*27852ebeSDavid van Moolenbroek }
434*27852ebeSDavid van Moolenbroek
435*27852ebeSDavid van Moolenbroek assert(uds->uds_queued == found);
436*27852ebeSDavid van Moolenbroek }
437*27852ebeSDavid van Moolenbroek
438*27852ebeSDavid van Moolenbroek /*
439*27852ebeSDavid van Moolenbroek * Check whether the socket address given in 'addr', with length 'addr_len', is
440*27852ebeSDavid van Moolenbroek * a valid UNIX domain socket address (including a path to a socket file). On
441*27852ebeSDavid van Moolenbroek * success, return the (non-zero) length of the socket file's path, minus the
442*27852ebeSDavid van Moolenbroek * null terminator which may in fact not be present. The caller is responsible
443*27852ebeSDavid van Moolenbroek * for copying and terminating the path as needed. A pointer to the path as
444*27852ebeSDavid van Moolenbroek * stored in 'addr' is returned in 'pathp'. On failure, return an error code.
445*27852ebeSDavid van Moolenbroek */
446*27852ebeSDavid van Moolenbroek static int
uds_check_addr(const struct sockaddr * addr,socklen_t addr_len,const char ** pathp)447*27852ebeSDavid van Moolenbroek uds_check_addr(const struct sockaddr * addr, socklen_t addr_len,
448*27852ebeSDavid van Moolenbroek const char ** pathp)
449*27852ebeSDavid van Moolenbroek {
450*27852ebeSDavid van Moolenbroek const char *p;
451*27852ebeSDavid van Moolenbroek size_t len;
452*27852ebeSDavid van Moolenbroek
453*27852ebeSDavid van Moolenbroek /*
454*27852ebeSDavid van Moolenbroek * We could cast to a sockaddr_un structure pointer first, but that
455*27852ebeSDavid van Moolenbroek * would not provide any benefits here. Instead, we use sa_data as the
456*27852ebeSDavid van Moolenbroek * generic equivalent of sun_path.
457*27852ebeSDavid van Moolenbroek */
458*27852ebeSDavid van Moolenbroek if (addr_len < offsetof(struct sockaddr, sa_data))
459cc5b1988SDavid van Moolenbroek return EINVAL;
460cc5b1988SDavid van Moolenbroek
461*27852ebeSDavid van Moolenbroek if (addr->sa_family != AF_UNIX)
462*27852ebeSDavid van Moolenbroek return EAFNOSUPPORT;
463cc5b1988SDavid van Moolenbroek
464*27852ebeSDavid van Moolenbroek len = (size_t)addr_len - offsetof(struct sockaddr, sa_data);
465*27852ebeSDavid van Moolenbroek if (len > 0 && (p = memchr(addr->sa_data, '\0', len)) != NULL)
466*27852ebeSDavid van Moolenbroek len = (size_t)(p - addr->sa_data);
467*27852ebeSDavid van Moolenbroek
468*27852ebeSDavid van Moolenbroek /* The given path name must not be an empty string. */
469*27852ebeSDavid van Moolenbroek if (len == 0)
470*27852ebeSDavid van Moolenbroek return ENOENT;
471*27852ebeSDavid van Moolenbroek
472*27852ebeSDavid van Moolenbroek /* This check should be redundant but better safe than sorry. */
473*27852ebeSDavid van Moolenbroek if (len >= UDS_PATH_MAX)
474*27852ebeSDavid van Moolenbroek return EINVAL;
475*27852ebeSDavid van Moolenbroek
476*27852ebeSDavid van Moolenbroek *pathp = (const char *)addr->sa_data;
477*27852ebeSDavid van Moolenbroek return len;
478cc5b1988SDavid van Moolenbroek }
479cc5b1988SDavid van Moolenbroek
480*27852ebeSDavid van Moolenbroek /*
481*27852ebeSDavid van Moolenbroek * Given the socket file path given as 'path' with length 'path_len' (not
482*27852ebeSDavid van Moolenbroek * necessarily null terminated), store a socket address with the path in
483*27852ebeSDavid van Moolenbroek * 'addr', and return the socket address length in 'addr_len'. The calling
484*27852ebeSDavid van Moolenbroek * libraries (libsockdriver, libsockevent) and the static assert in uds.h
485*27852ebeSDavid van Moolenbroek * guarantee that 'addr' is sufficiently large to store any address we generate
486*27852ebeSDavid van Moolenbroek * here. The libraries may subsequently copy out only a part of it to the user
487*27852ebeSDavid van Moolenbroek * process. This function always succeeds.
488*27852ebeSDavid van Moolenbroek */
489*27852ebeSDavid van Moolenbroek void
uds_make_addr(const char * path,size_t len,struct sockaddr * addr,socklen_t * addr_len)490*27852ebeSDavid van Moolenbroek uds_make_addr(const char * path, size_t len, struct sockaddr * addr,
491*27852ebeSDavid van Moolenbroek socklen_t * addr_len)
492*27852ebeSDavid van Moolenbroek {
493cc5b1988SDavid van Moolenbroek
494*27852ebeSDavid van Moolenbroek /*
495*27852ebeSDavid van Moolenbroek * Generate the address. The stored length (sa_len/sun_len) does not
496*27852ebeSDavid van Moolenbroek * include a null terminator. The entire structure does include a null
497*27852ebeSDavid van Moolenbroek * terminator, but only if the socket is bound.
498*27852ebeSDavid van Moolenbroek */
499*27852ebeSDavid van Moolenbroek addr->sa_len = offsetof(struct sockaddr, sa_data) + len;
500*27852ebeSDavid van Moolenbroek addr->sa_family = AF_UNIX;
501*27852ebeSDavid van Moolenbroek if (len > 0) {
502*27852ebeSDavid van Moolenbroek /* This call may (intentionally) overrun the sa_data size. */
503*27852ebeSDavid van Moolenbroek memcpy((char *)addr->sa_data, path, len);
504*27852ebeSDavid van Moolenbroek ((char *)addr->sa_data)[len] = '\0';
505cc5b1988SDavid van Moolenbroek
506*27852ebeSDavid van Moolenbroek /* The socket is bound, so include the null terminator. */
507*27852ebeSDavid van Moolenbroek len++;
508*27852ebeSDavid van Moolenbroek assert(len <= UDS_PATH_MAX);
509*27852ebeSDavid van Moolenbroek }
510cc5b1988SDavid van Moolenbroek
511*27852ebeSDavid van Moolenbroek /* Note that this length may be different from sa_len/sun_len now. */
512*27852ebeSDavid van Moolenbroek *addr_len = offsetof(struct sockaddr, sa_data) + len;
513*27852ebeSDavid van Moolenbroek }
514*27852ebeSDavid van Moolenbroek
515*27852ebeSDavid van Moolenbroek /*
516*27852ebeSDavid van Moolenbroek * Bind a socket to a local address.
517*27852ebeSDavid van Moolenbroek */
518*27852ebeSDavid van Moolenbroek static int
uds_bind(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt)519*27852ebeSDavid van Moolenbroek uds_bind(struct sock * sock, const struct sockaddr * addr, socklen_t addr_len,
520*27852ebeSDavid van Moolenbroek endpoint_t user_endpt)
521*27852ebeSDavid van Moolenbroek {
522*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
523*27852ebeSDavid van Moolenbroek struct udssock *uds2;
524*27852ebeSDavid van Moolenbroek const char *path;
525*27852ebeSDavid van Moolenbroek size_t len;
526*27852ebeSDavid van Moolenbroek dev_t dev;
527*27852ebeSDavid van Moolenbroek ino_t ino;
528*27852ebeSDavid van Moolenbroek int r;
529*27852ebeSDavid van Moolenbroek
530*27852ebeSDavid van Moolenbroek dprintf(("UDS: bind(%d)\n", uds_get_id(uds)));
531*27852ebeSDavid van Moolenbroek
532*27852ebeSDavid van Moolenbroek /* A socket may be bound at any time, but only once. */
533*27852ebeSDavid van Moolenbroek if (uds_is_bound(uds))
534*27852ebeSDavid van Moolenbroek return EINVAL;
535*27852ebeSDavid van Moolenbroek
536*27852ebeSDavid van Moolenbroek /* Verify that the user gave us an acceptable address. */
537*27852ebeSDavid van Moolenbroek if ((r = uds_check_addr(addr, addr_len, &path)) < 0)
538*27852ebeSDavid van Moolenbroek return r;
539*27852ebeSDavid van Moolenbroek len = (size_t)r;
540*27852ebeSDavid van Moolenbroek
541*27852ebeSDavid van Moolenbroek /* Attempt to create the socket file on the file system. */
542*27852ebeSDavid van Moolenbroek r = socketpath(user_endpt, path, len, SPATH_CREATE, &dev, &ino);
543*27852ebeSDavid van Moolenbroek if (r != OK)
544*27852ebeSDavid van Moolenbroek return r;
545*27852ebeSDavid van Moolenbroek assert(dev != NO_DEV && ino != 0);
546*27852ebeSDavid van Moolenbroek
547*27852ebeSDavid van Moolenbroek /*
548*27852ebeSDavid van Moolenbroek * It is possible that a socket file of a previously bound socket was
549*27852ebeSDavid van Moolenbroek * unlinked, and due to inode number reuse, a new socket file has now
550*27852ebeSDavid van Moolenbroek * been created with the same <dev,ino> pair. In that case, we must
551*27852ebeSDavid van Moolenbroek * unbind the old socket, because it must no longer be found. The old
552*27852ebeSDavid van Moolenbroek * socket will still have a path (and behave as though it is bound) but
553*27852ebeSDavid van Moolenbroek * no longer be found through hash lookups.
554*27852ebeSDavid van Moolenbroek */
555*27852ebeSDavid van Moolenbroek if ((uds2 = udshash_get(dev, ino)) != NULL) {
556*27852ebeSDavid van Moolenbroek udshash_del(uds2);
557*27852ebeSDavid van Moolenbroek
558*27852ebeSDavid van Moolenbroek uds2->uds_dev = NO_DEV;
559*27852ebeSDavid van Moolenbroek uds2->uds_ino = 0;
560*27852ebeSDavid van Moolenbroek }
561*27852ebeSDavid van Moolenbroek
562*27852ebeSDavid van Moolenbroek /*
563*27852ebeSDavid van Moolenbroek * Obtain credentials for the socket, unless the socket is already
564*27852ebeSDavid van Moolenbroek * connecting or connected, in which case we must not replace the
565*27852ebeSDavid van Moolenbroek * credentials we obtained already. We later clear those credentials
566*27852ebeSDavid van Moolenbroek * upon a connection failure or disconnect, so that if the socket is
567*27852ebeSDavid van Moolenbroek * then put in listening mode, we know there are no bind-time
568*27852ebeSDavid van Moolenbroek * credentials. Not ideal, but we really need two separate sets of
569*27852ebeSDavid van Moolenbroek * credentials if we want to get this right, which is a waste of memory
570*27852ebeSDavid van Moolenbroek * as no sane application writer would ever rely on credential passing
571*27852ebeSDavid van Moolenbroek * after recycling a socket..
572*27852ebeSDavid van Moolenbroek */
573*27852ebeSDavid van Moolenbroek if (uds_get_type(uds) != SOCK_DGRAM && !uds_is_connecting(uds) &&
574*27852ebeSDavid van Moolenbroek !uds_is_connected(uds))
575*27852ebeSDavid van Moolenbroek uds_get_cred(uds, user_endpt);
576*27852ebeSDavid van Moolenbroek
577*27852ebeSDavid van Moolenbroek /* Asssign the address to the socket. */
578*27852ebeSDavid van Moolenbroek uds->uds_pathlen = len;
579*27852ebeSDavid van Moolenbroek memcpy(&uds->uds_path, path, len);
580*27852ebeSDavid van Moolenbroek uds->uds_dev = dev;
581*27852ebeSDavid van Moolenbroek uds->uds_ino = ino;
582*27852ebeSDavid van Moolenbroek
583*27852ebeSDavid van Moolenbroek udshash_add(uds);
584*27852ebeSDavid van Moolenbroek
585*27852ebeSDavid van Moolenbroek return OK;
586*27852ebeSDavid van Moolenbroek }
587*27852ebeSDavid van Moolenbroek
588*27852ebeSDavid van Moolenbroek /*
589*27852ebeSDavid van Moolenbroek * Look up a UDS socket based on a user-given address. If a socket exists for
590*27852ebeSDavid van Moolenbroek * the address, check if it is type-compatible with the given UDS socket.
591*27852ebeSDavid van Moolenbroek * On succes, return OK, with 'peerp' set to the socket that was found. On
592*27852ebeSDavid van Moolenbroek * failure, return a negative error code.
593*27852ebeSDavid van Moolenbroek */
594*27852ebeSDavid van Moolenbroek int
uds_lookup(struct udssock * uds,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt,struct udssock ** peerp)595*27852ebeSDavid van Moolenbroek uds_lookup(struct udssock * uds, const struct sockaddr * addr,
596*27852ebeSDavid van Moolenbroek socklen_t addr_len, endpoint_t user_endpt, struct udssock ** peerp)
597*27852ebeSDavid van Moolenbroek {
598*27852ebeSDavid van Moolenbroek struct udssock *peer;
599*27852ebeSDavid van Moolenbroek const char *path;
600*27852ebeSDavid van Moolenbroek size_t len;
601*27852ebeSDavid van Moolenbroek dev_t dev;
602*27852ebeSDavid van Moolenbroek ino_t ino;
603*27852ebeSDavid van Moolenbroek int r;
604*27852ebeSDavid van Moolenbroek
605*27852ebeSDavid van Moolenbroek /* Verify that the user gave us an acceptable address. */
606*27852ebeSDavid van Moolenbroek if ((r = uds_check_addr(addr, addr_len, &path)) < 0)
607*27852ebeSDavid van Moolenbroek return r;
608*27852ebeSDavid van Moolenbroek len = (size_t)r;
609*27852ebeSDavid van Moolenbroek
610*27852ebeSDavid van Moolenbroek /* Attempt to look up the socket file on the file system. */
611*27852ebeSDavid van Moolenbroek r = socketpath(user_endpt, path, len, SPATH_CHECK, &dev, &ino);
612*27852ebeSDavid van Moolenbroek if (r != OK)
613*27852ebeSDavid van Moolenbroek return r;
614*27852ebeSDavid van Moolenbroek assert(dev != NO_DEV && ino != 0);
615*27852ebeSDavid van Moolenbroek
616*27852ebeSDavid van Moolenbroek if ((peer = udshash_get(dev, ino)) == NULL)
617*27852ebeSDavid van Moolenbroek return ECONNREFUSED;
618*27852ebeSDavid van Moolenbroek if (uds_get_type(peer) != uds_get_type(uds))
619*27852ebeSDavid van Moolenbroek return EPROTOTYPE;
620*27852ebeSDavid van Moolenbroek
621*27852ebeSDavid van Moolenbroek *peerp = peer;
622*27852ebeSDavid van Moolenbroek return OK;
623*27852ebeSDavid van Moolenbroek }
624*27852ebeSDavid van Moolenbroek
625*27852ebeSDavid van Moolenbroek /*
626*27852ebeSDavid van Moolenbroek * Given the listening socket 'uds', and the socket 'link' that is calling or
627*27852ebeSDavid van Moolenbroek * has called connect(2) and is or will be linked to the listening socket's
628*27852ebeSDavid van Moolenbroek * queue, create a new socket and connect it to 'link', putting both sockets in
629*27852ebeSDavid van Moolenbroek * the connected state. The given link socket may be in unconnected,
630*27852ebeSDavid van Moolenbroek * connecting, or disconnected state prior to the call. Return OK or an error
631*27852ebeSDavid van Moolenbroek * code. The link state of the link socket remains unchanged in any case.
632*27852ebeSDavid van Moolenbroek */
633*27852ebeSDavid van Moolenbroek static int
uds_attach(struct udssock * uds,struct udssock * link)634*27852ebeSDavid van Moolenbroek uds_attach(struct udssock * uds, struct udssock * link)
635*27852ebeSDavid van Moolenbroek {
636*27852ebeSDavid van Moolenbroek struct udssock *conn;
637*27852ebeSDavid van Moolenbroek int r;
638*27852ebeSDavid van Moolenbroek
639*27852ebeSDavid van Moolenbroek /*
640*27852ebeSDavid van Moolenbroek * Allocate a new socket to use as peer socket for the connection that
641*27852ebeSDavid van Moolenbroek * is about to be established. The new socket is not yet known by
642*27852ebeSDavid van Moolenbroek * libsockevent.
643*27852ebeSDavid van Moolenbroek */
644*27852ebeSDavid van Moolenbroek if ((r = uds_alloc(&conn)) != OK)
645*27852ebeSDavid van Moolenbroek return r;
646*27852ebeSDavid van Moolenbroek
647*27852ebeSDavid van Moolenbroek /*
648*27852ebeSDavid van Moolenbroek * Ask libsockevent to clone the sock object in the new UDS socket from
649*27852ebeSDavid van Moolenbroek * the listening socket. This adds the sock object to libsockevent's
650*27852ebeSDavid van Moolenbroek * data structures and ensures that we can safely use the socket
651*27852ebeSDavid van Moolenbroek * despite the fact that it has not yet been accepted (and thus
652*27852ebeSDavid van Moolenbroek * returned to libsockevent). From this moment on, we must either
653*27852ebeSDavid van Moolenbroek * return the socket's ID (but not a pointer to it!) from uds_accept()
654*27852ebeSDavid van Moolenbroek * or raise SEV_CLOSE on it.
655*27852ebeSDavid van Moolenbroek */
656*27852ebeSDavid van Moolenbroek sockevent_clone(&uds->uds_sock, &conn->uds_sock, uds_get_id(conn));
657*27852ebeSDavid van Moolenbroek
658*27852ebeSDavid van Moolenbroek /* Connect the link socket to the new socket. */
659*27852ebeSDavid van Moolenbroek link->uds_conn = conn;
660*27852ebeSDavid van Moolenbroek link->uds_flags |= UDSF_CONNECTED;
661*27852ebeSDavid van Moolenbroek
662*27852ebeSDavid van Moolenbroek /*
663*27852ebeSDavid van Moolenbroek * Connect the new socket to the link socket as well. The child
664*27852ebeSDavid van Moolenbroek * socket should also inherit pretty much all settings from the
665*27852ebeSDavid van Moolenbroek * listening socket, including the bind path and the listening socket's
666*27852ebeSDavid van Moolenbroek * bind-time credentials.
667*27852ebeSDavid van Moolenbroek */
668*27852ebeSDavid van Moolenbroek conn->uds_conn = link;
669*27852ebeSDavid van Moolenbroek conn->uds_flags = uds->uds_flags & (UDSF_PASSCRED | UDSF_CONNWAIT);
670*27852ebeSDavid van Moolenbroek conn->uds_flags |= UDSF_CONNECTED;
671*27852ebeSDavid van Moolenbroek conn->uds_pathlen = uds->uds_pathlen;
672*27852ebeSDavid van Moolenbroek memcpy(conn->uds_path, uds->uds_path, (size_t)uds->uds_pathlen);
673*27852ebeSDavid van Moolenbroek memcpy(&conn->uds_cred, &uds->uds_cred, sizeof(conn->uds_cred));
674*27852ebeSDavid van Moolenbroek
675*27852ebeSDavid van Moolenbroek return OK;
676*27852ebeSDavid van Moolenbroek }
677*27852ebeSDavid van Moolenbroek
678*27852ebeSDavid van Moolenbroek /*
679*27852ebeSDavid van Moolenbroek * Connect a socket to a remote address.
680*27852ebeSDavid van Moolenbroek */
681*27852ebeSDavid van Moolenbroek static int
uds_connect(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt)682*27852ebeSDavid van Moolenbroek uds_connect(struct sock * sock, const struct sockaddr * addr,
683*27852ebeSDavid van Moolenbroek socklen_t addr_len, endpoint_t user_endpt)
684*27852ebeSDavid van Moolenbroek {
685*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
686*27852ebeSDavid van Moolenbroek struct udssock *link;
687*27852ebeSDavid van Moolenbroek int r;
688*27852ebeSDavid van Moolenbroek
689*27852ebeSDavid van Moolenbroek dprintf(("UDS: connect(%d)\n", uds_get_id(uds)));
690*27852ebeSDavid van Moolenbroek
691*27852ebeSDavid van Moolenbroek /* For connection-oriented sockets, several state checks apply. */
692*27852ebeSDavid van Moolenbroek if (uds_get_type(uds) != SOCK_DGRAM) {
693*27852ebeSDavid van Moolenbroek if (uds_is_listening(uds))
694*27852ebeSDavid van Moolenbroek return EOPNOTSUPP;
695*27852ebeSDavid van Moolenbroek if (uds_is_connecting(uds))
696*27852ebeSDavid van Moolenbroek return EALREADY;
697*27852ebeSDavid van Moolenbroek if (uds_is_connected(uds))
698*27852ebeSDavid van Moolenbroek return EISCONN;
699*27852ebeSDavid van Moolenbroek /* Disconnected sockets may be reconnected, see below. */
700*27852ebeSDavid van Moolenbroek } else {
701*27852ebeSDavid van Moolenbroek /*
702*27852ebeSDavid van Moolenbroek * Connectionless sockets may be unconnected by providing an
703*27852ebeSDavid van Moolenbroek * address with family AF_UNSPEC. Handle this case first here.
704*27852ebeSDavid van Moolenbroek */
705*27852ebeSDavid van Moolenbroek if (addr_len >= offsetof(struct sockaddr, sa_data) &&
706*27852ebeSDavid van Moolenbroek addr->sa_family == AF_UNSPEC) {
707*27852ebeSDavid van Moolenbroek /*
708*27852ebeSDavid van Moolenbroek * Reset this socket's previous connection to another
709*27852ebeSDavid van Moolenbroek * socket, if any. Unconnecting has no effect on other
710*27852ebeSDavid van Moolenbroek * sockets connected to this socket, though.
711*27852ebeSDavid van Moolenbroek */
712*27852ebeSDavid van Moolenbroek if (uds_has_link(uds))
713*27852ebeSDavid van Moolenbroek uds_del_queue(uds->uds_link, uds);
714*27852ebeSDavid van Moolenbroek
715*27852ebeSDavid van Moolenbroek return OK;
716*27852ebeSDavid van Moolenbroek }
717*27852ebeSDavid van Moolenbroek }
718*27852ebeSDavid van Moolenbroek
719*27852ebeSDavid van Moolenbroek /*
720*27852ebeSDavid van Moolenbroek * Find the socket identified by the given address. If it exists at
721*27852ebeSDavid van Moolenbroek * all, see if it is a proper match.
722*27852ebeSDavid van Moolenbroek */
723*27852ebeSDavid van Moolenbroek if ((r = uds_lookup(uds, addr, addr_len, user_endpt, &link)) != OK)
724*27852ebeSDavid van Moolenbroek return r;
725*27852ebeSDavid van Moolenbroek
726*27852ebeSDavid van Moolenbroek /*
727*27852ebeSDavid van Moolenbroek * Handle connectionless sockets first, in which case a connect links
728*27852ebeSDavid van Moolenbroek * the socket to a send target and limits receipt to datagrams from
729*27852ebeSDavid van Moolenbroek * that target. We actually point the socket to the peer socket,
730*27852ebeSDavid van Moolenbroek * through uds_link. That also means that if the target socket
731*27852ebeSDavid van Moolenbroek * disappears, we have to reset any sockets connected to it, in which
732*27852ebeSDavid van Moolenbroek * case we return them to the unconnected state. In order to allow
733*27852ebeSDavid van Moolenbroek * finding all sockets connected to a particular socket, we put all
734*27852ebeSDavid van Moolenbroek * those sockets on their target's queue, hence why we use uds_link and
735*27852ebeSDavid van Moolenbroek * not uds_conn. As mentioned before, we allow reconnecting without
736*27852ebeSDavid van Moolenbroek * restrictions.
737*27852ebeSDavid van Moolenbroek * TODO: see if reconnecting should clear a pending ECONNRESET.
738*27852ebeSDavid van Moolenbroek *
739*27852ebeSDavid van Moolenbroek * An important note: 'uds' and 'link' may actually be the same socket,
740*27852ebeSDavid van Moolenbroek * if the caller chooses to connect a socket with itself!
741*27852ebeSDavid van Moolenbroek */
742*27852ebeSDavid van Moolenbroek if (uds_get_type(uds) == SOCK_DGRAM) {
743*27852ebeSDavid van Moolenbroek /* Reconnecting to the same socket has no effect. */
744*27852ebeSDavid van Moolenbroek if (uds_has_link(uds) && uds->uds_link == link)
745*27852ebeSDavid van Moolenbroek return OK;
746*27852ebeSDavid van Moolenbroek
747*27852ebeSDavid van Moolenbroek /*
748*27852ebeSDavid van Moolenbroek * If the intended target is linked to another socket, we
749*27852ebeSDavid van Moolenbroek * refuse linking to it. Sending or receiving would never work
750*27852ebeSDavid van Moolenbroek * anyway. Do allow a socket to link to itself after being
751*27852ebeSDavid van Moolenbroek * linked to another socket. The error code is the same as in
752*27852ebeSDavid van Moolenbroek * the sending code, borrowed from Linux.
753*27852ebeSDavid van Moolenbroek */
754*27852ebeSDavid van Moolenbroek if (uds != link && uds_has_link(link) && link->uds_link != uds)
755*27852ebeSDavid van Moolenbroek return EPERM;
756*27852ebeSDavid van Moolenbroek
757*27852ebeSDavid van Moolenbroek /*
758*27852ebeSDavid van Moolenbroek * Reset this socket's previous link to another socket, if any.
759*27852ebeSDavid van Moolenbroek */
760*27852ebeSDavid van Moolenbroek if (uds_has_link(uds))
761*27852ebeSDavid van Moolenbroek uds_del_queue(uds->uds_link, uds);
762*27852ebeSDavid van Moolenbroek
763*27852ebeSDavid van Moolenbroek /*
764*27852ebeSDavid van Moolenbroek * Reset any links to this socket, except for the one by
765*27852ebeSDavid van Moolenbroek * the intended target. Sending or receiving would no longer
766*27852ebeSDavid van Moolenbroek * work anyway. If the socket was linked to itself, clear its
767*27852ebeSDavid van Moolenbroek * self-link without generating an ECONNRESET. If the socket
768*27852ebeSDavid van Moolenbroek * is relinking to itself, reestablish the link after first
769*27852ebeSDavid van Moolenbroek * clearing it.
770*27852ebeSDavid van Moolenbroek */
771*27852ebeSDavid van Moolenbroek uds_clear_queue(uds, (uds != link) ? link : NULL);
772*27852ebeSDavid van Moolenbroek
773*27852ebeSDavid van Moolenbroek uds_add_queue(link, uds);
774*27852ebeSDavid van Moolenbroek
775*27852ebeSDavid van Moolenbroek return OK;
776*27852ebeSDavid van Moolenbroek }
777*27852ebeSDavid van Moolenbroek
778*27852ebeSDavid van Moolenbroek /*
779*27852ebeSDavid van Moolenbroek * For connection-oriented sockets there is more to do. First, make
780*27852ebeSDavid van Moolenbroek * sure that the peer is a listening socket, that it has not been shut
781*27852ebeSDavid van Moolenbroek * down, and that its backlog is not already at the configured maximum.
782*27852ebeSDavid van Moolenbroek */
783*27852ebeSDavid van Moolenbroek if (!uds_is_listening(link))
784*27852ebeSDavid van Moolenbroek return ECONNREFUSED;
785*27852ebeSDavid van Moolenbroek
786*27852ebeSDavid van Moolenbroek if (uds_is_shutdown(link, SFL_SHUT_RD | SFL_SHUT_WR))
787*27852ebeSDavid van Moolenbroek return ECONNREFUSED;
788*27852ebeSDavid van Moolenbroek
789*27852ebeSDavid van Moolenbroek if (link->uds_queued >= link->uds_backlog)
790*27852ebeSDavid van Moolenbroek return ECONNREFUSED;
791*27852ebeSDavid van Moolenbroek
792*27852ebeSDavid van Moolenbroek /*
793*27852ebeSDavid van Moolenbroek * The behavior of connect(2) now depends on whether LOCAL_CONNWAIT is
794*27852ebeSDavid van Moolenbroek * set on either the connecting or the listening socket. If it is not,
795*27852ebeSDavid van Moolenbroek * the socket will be connected to a new as-yet invisible socket, which
796*27852ebeSDavid van Moolenbroek * will be the one returned from accept(2) later. If it was, the
797*27852ebeSDavid van Moolenbroek * socket will be put in the connecting state.
798*27852ebeSDavid van Moolenbroek */
799*27852ebeSDavid van Moolenbroek if (!((uds->uds_flags | link->uds_flags) & UDSF_CONNWAIT)) {
800*27852ebeSDavid van Moolenbroek if ((r = uds_attach(link, uds)) != OK)
801*27852ebeSDavid van Moolenbroek return r;
802*27852ebeSDavid van Moolenbroek
803*27852ebeSDavid van Moolenbroek assert(uds_is_connected(uds));
804*27852ebeSDavid van Moolenbroek } else {
805*27852ebeSDavid van Moolenbroek /*
806*27852ebeSDavid van Moolenbroek * Disconnected sockets now stop being connected. Any pending
807*27852ebeSDavid van Moolenbroek * data can still be received, though.
808*27852ebeSDavid van Moolenbroek */
809*27852ebeSDavid van Moolenbroek uds->uds_flags &= ~UDSF_CONNECTED;
810*27852ebeSDavid van Moolenbroek
811*27852ebeSDavid van Moolenbroek r = SUSPEND;
812*27852ebeSDavid van Moolenbroek }
813*27852ebeSDavid van Moolenbroek
814*27852ebeSDavid van Moolenbroek /* Obtain credentials for the socket. */
815*27852ebeSDavid van Moolenbroek uds_get_cred(uds, user_endpt);
816*27852ebeSDavid van Moolenbroek
817*27852ebeSDavid van Moolenbroek /* Add the socket at the end of the listening socket's queue. */
818*27852ebeSDavid van Moolenbroek uds_add_queue(link, uds);
819*27852ebeSDavid van Moolenbroek
820*27852ebeSDavid van Moolenbroek assert(r != SUSPEND || uds_is_connecting(uds));
821*27852ebeSDavid van Moolenbroek
822*27852ebeSDavid van Moolenbroek /*
823*27852ebeSDavid van Moolenbroek * Let an accept call handle the rest, which will in turn resume this
824*27852ebeSDavid van Moolenbroek * connect call. The sockevent library ensures that this works even if
825*27852ebeSDavid van Moolenbroek * the call is non-blocking.
826*27852ebeSDavid van Moolenbroek */
827*27852ebeSDavid van Moolenbroek sockevent_raise(&link->uds_sock, SEV_ACCEPT);
828*27852ebeSDavid van Moolenbroek
829*27852ebeSDavid van Moolenbroek return r;
830*27852ebeSDavid van Moolenbroek }
831*27852ebeSDavid van Moolenbroek
832*27852ebeSDavid van Moolenbroek /*
833*27852ebeSDavid van Moolenbroek * Put a socket in listening mode.
834*27852ebeSDavid van Moolenbroek */
835*27852ebeSDavid van Moolenbroek static int
uds_listen(struct sock * sock,int backlog)836*27852ebeSDavid van Moolenbroek uds_listen(struct sock * sock, int backlog)
837*27852ebeSDavid van Moolenbroek {
838*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
839*27852ebeSDavid van Moolenbroek
840*27852ebeSDavid van Moolenbroek /* The maximum backlog value must not exceed its field size. */
841*27852ebeSDavid van Moolenbroek assert(SOMAXCONN <= USHRT_MAX);
842*27852ebeSDavid van Moolenbroek
843*27852ebeSDavid van Moolenbroek dprintf(("UDS: listen(%d)\n", uds_get_id(uds)));
844*27852ebeSDavid van Moolenbroek
845*27852ebeSDavid van Moolenbroek /* Only connection-oriented types may be put in listening mode. */
846*27852ebeSDavid van Moolenbroek if (uds_get_type(uds) == SOCK_DGRAM)
847*27852ebeSDavid van Moolenbroek return EOPNOTSUPP;
848*27852ebeSDavid van Moolenbroek
849*27852ebeSDavid van Moolenbroek /* A connecting or connected socket may not listen. */
850*27852ebeSDavid van Moolenbroek if (uds_is_connecting(uds) || uds_is_connected(uds))
851*27852ebeSDavid van Moolenbroek return EINVAL;
852*27852ebeSDavid van Moolenbroek
853*27852ebeSDavid van Moolenbroek /* POSIX says that this is now the appropriate error code here. */
854*27852ebeSDavid van Moolenbroek if (!uds_is_bound(uds))
855*27852ebeSDavid van Moolenbroek return EDESTADDRREQ;
856*27852ebeSDavid van Moolenbroek
857*27852ebeSDavid van Moolenbroek /*
858*27852ebeSDavid van Moolenbroek * The socket is now entering the listening state. If it was
859*27852ebeSDavid van Moolenbroek * previously disconnected, clear the connection flag.
860*27852ebeSDavid van Moolenbroek */
861*27852ebeSDavid van Moolenbroek uds->uds_flags &= ~UDSF_CONNECTED;
862*27852ebeSDavid van Moolenbroek
863*27852ebeSDavid van Moolenbroek /*
864*27852ebeSDavid van Moolenbroek * We do not remove sockets from the backlog if it is now being dropped
865*27852ebeSDavid van Moolenbroek * below the current number of queued sockets. We only refuse newly
866*27852ebeSDavid van Moolenbroek * connecting sockets beyond the backlog size.
867*27852ebeSDavid van Moolenbroek */
868*27852ebeSDavid van Moolenbroek uds->uds_backlog = backlog;
869*27852ebeSDavid van Moolenbroek
870*27852ebeSDavid van Moolenbroek return OK;
871*27852ebeSDavid van Moolenbroek }
872*27852ebeSDavid van Moolenbroek
873*27852ebeSDavid van Moolenbroek /*
874*27852ebeSDavid van Moolenbroek * Test whether an accept request would block. Return OK if a socket could be
875*27852ebeSDavid van Moolenbroek * accepted, an appropriate error code if an accept call would fail instantly,
876*27852ebeSDavid van Moolenbroek * or SUSPEND if the accept request would block waiting for a connection.
877*27852ebeSDavid van Moolenbroek */
878*27852ebeSDavid van Moolenbroek static int
uds_test_accept(struct sock * sock)879*27852ebeSDavid van Moolenbroek uds_test_accept(struct sock * sock)
880*27852ebeSDavid van Moolenbroek {
881*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
882*27852ebeSDavid van Moolenbroek
883*27852ebeSDavid van Moolenbroek /*
884*27852ebeSDavid van Moolenbroek * Ensure that the socket is in listening mode. If not, we must return
885*27852ebeSDavid van Moolenbroek * the error code that is appropriate for this socket type.
886*27852ebeSDavid van Moolenbroek */
887*27852ebeSDavid van Moolenbroek if (uds_get_type(uds) == SOCK_DGRAM)
888*27852ebeSDavid van Moolenbroek return EOPNOTSUPP;
889*27852ebeSDavid van Moolenbroek if (!uds_is_listening(uds))
890*27852ebeSDavid van Moolenbroek return EINVAL;
891*27852ebeSDavid van Moolenbroek
892*27852ebeSDavid van Moolenbroek /*
893*27852ebeSDavid van Moolenbroek * If the socket has been shut down, new connections are no longer
894*27852ebeSDavid van Moolenbroek * accepted and accept calls no longer block. This is not a POSIX
895*27852ebeSDavid van Moolenbroek * requirement, but rather an application convenience feature.
896*27852ebeSDavid van Moolenbroek */
897*27852ebeSDavid van Moolenbroek if (uds->uds_queued == 0) {
898*27852ebeSDavid van Moolenbroek if (uds_is_shutdown(uds, SFL_SHUT_RD | SFL_SHUT_WR))
899*27852ebeSDavid van Moolenbroek return ECONNABORTED;
900*27852ebeSDavid van Moolenbroek
901*27852ebeSDavid van Moolenbroek return SUSPEND;
902cc5b1988SDavid van Moolenbroek }
903cc5b1988SDavid van Moolenbroek
904cc5b1988SDavid van Moolenbroek return OK;
905cc5b1988SDavid van Moolenbroek }
906cc5b1988SDavid van Moolenbroek
907*27852ebeSDavid van Moolenbroek /*
908*27852ebeSDavid van Moolenbroek * Accept a connection on a listening socket, creating a new socket. On
909*27852ebeSDavid van Moolenbroek * success, return the new socket identifier, with the new socket stored in
910*27852ebeSDavid van Moolenbroek * 'newsockp'. Otherwise, return an error code.
911*27852ebeSDavid van Moolenbroek */
912*27852ebeSDavid van Moolenbroek static sockid_t
uds_accept(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len,endpoint_t user_endpt __unused,struct sock ** newsockp)913*27852ebeSDavid van Moolenbroek uds_accept(struct sock * sock, struct sockaddr * addr, socklen_t * addr_len,
914*27852ebeSDavid van Moolenbroek endpoint_t user_endpt __unused, struct sock ** newsockp)
915*27852ebeSDavid van Moolenbroek {
916*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
917*27852ebeSDavid van Moolenbroek struct udssock *link, *conn;
918*27852ebeSDavid van Moolenbroek sockid_t r;
919*27852ebeSDavid van Moolenbroek
920*27852ebeSDavid van Moolenbroek dprintf(("UDS: accept(%d)\n", uds_get_id(uds)));
921*27852ebeSDavid van Moolenbroek
922*27852ebeSDavid van Moolenbroek if ((r = uds_test_accept(sock)) != OK)
923*27852ebeSDavid van Moolenbroek return r;
924*27852ebeSDavid van Moolenbroek
925*27852ebeSDavid van Moolenbroek /*
926*27852ebeSDavid van Moolenbroek * Take the first connecting socket off the listening queue.
927*27852ebeSDavid van Moolenbroek */
928*27852ebeSDavid van Moolenbroek assert(!TAILQ_EMPTY(&uds->uds_queue));
929*27852ebeSDavid van Moolenbroek
930*27852ebeSDavid van Moolenbroek link = TAILQ_FIRST(&uds->uds_queue);
931*27852ebeSDavid van Moolenbroek
932*27852ebeSDavid van Moolenbroek /*
933*27852ebeSDavid van Moolenbroek * Depending on the LOCAL_CONNWAIT setting at the time of connect(2),
934*27852ebeSDavid van Moolenbroek * the socket may be connecting or connected. In the latter case, its
935*27852ebeSDavid van Moolenbroek * attached socket is the socket we will return now. Otherwise we have
936*27852ebeSDavid van Moolenbroek * to attach a socket first.
937*27852ebeSDavid van Moolenbroek */
938*27852ebeSDavid van Moolenbroek assert(uds_is_connecting(link) || uds_is_connected(link));
939*27852ebeSDavid van Moolenbroek
940*27852ebeSDavid van Moolenbroek if (uds_is_connecting(link)) {
941*27852ebeSDavid van Moolenbroek /*
942*27852ebeSDavid van Moolenbroek * Attach a new socket. If this fails, return the error but
943*27852ebeSDavid van Moolenbroek * leave the connecting socket on the listening queue.
944*27852ebeSDavid van Moolenbroek */
945*27852ebeSDavid van Moolenbroek if ((r = uds_attach(uds, link)) != OK)
946*27852ebeSDavid van Moolenbroek return r;
947*27852ebeSDavid van Moolenbroek
948*27852ebeSDavid van Moolenbroek assert(uds_is_connected(link));
949*27852ebeSDavid van Moolenbroek
950*27852ebeSDavid van Moolenbroek /*
951*27852ebeSDavid van Moolenbroek * Wake up blocked (connect, send, select) calls on the peer
952*27852ebeSDavid van Moolenbroek * socket.
953*27852ebeSDavid van Moolenbroek */
954*27852ebeSDavid van Moolenbroek sockevent_raise(&link->uds_sock, SEV_CONNECT);
955*27852ebeSDavid van Moolenbroek }
956*27852ebeSDavid van Moolenbroek
957*27852ebeSDavid van Moolenbroek uds_del_queue(uds, link);
958*27852ebeSDavid van Moolenbroek
959*27852ebeSDavid van Moolenbroek /* Return the peer socket's address to the caller. */
960*27852ebeSDavid van Moolenbroek uds_make_addr(link->uds_path, link->uds_pathlen, addr, addr_len);
961*27852ebeSDavid van Moolenbroek
962*27852ebeSDavid van Moolenbroek conn = link->uds_conn;
963*27852ebeSDavid van Moolenbroek
964*27852ebeSDavid van Moolenbroek dprintf(("UDS: accept returns %d\n", uds_get_id(conn)));
965*27852ebeSDavid van Moolenbroek
966*27852ebeSDavid van Moolenbroek /*
967*27852ebeSDavid van Moolenbroek * We already cloned the sock object, so return its ID but not a
968*27852ebeSDavid van Moolenbroek * pointer to it. That tells libsockevent not to reinitialize it.
969*27852ebeSDavid van Moolenbroek */
970*27852ebeSDavid van Moolenbroek *newsockp = NULL;
971*27852ebeSDavid van Moolenbroek return uds_get_id(conn);
972*27852ebeSDavid van Moolenbroek }
973*27852ebeSDavid van Moolenbroek
974*27852ebeSDavid van Moolenbroek /*
975*27852ebeSDavid van Moolenbroek * Set socket options.
976*27852ebeSDavid van Moolenbroek */
977cc5b1988SDavid van Moolenbroek static int
uds_setsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t len)978*27852ebeSDavid van Moolenbroek uds_setsockopt(struct sock * sock, int level, int name,
979*27852ebeSDavid van Moolenbroek const struct sockdriver_data * data, socklen_t len)
980cc5b1988SDavid van Moolenbroek {
981*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
982*27852ebeSDavid van Moolenbroek int r, val;
983cc5b1988SDavid van Moolenbroek
984*27852ebeSDavid van Moolenbroek dprintf(("UDS: setsockopt(%d,%d,%d)\n", uds_get_id(uds), level, name));
985cc5b1988SDavid van Moolenbroek
986*27852ebeSDavid van Moolenbroek switch (level) {
987*27852ebeSDavid van Moolenbroek case SOL_SOCKET:
988*27852ebeSDavid van Moolenbroek switch (name) {
989*27852ebeSDavid van Moolenbroek case SO_SNDBUF:
990*27852ebeSDavid van Moolenbroek case SO_RCVBUF:
991*27852ebeSDavid van Moolenbroek /*
992*27852ebeSDavid van Moolenbroek * The send buffer size may not be changed because the
993*27852ebeSDavid van Moolenbroek * buffer is the same as the other side's receive
994*27852ebeSDavid van Moolenbroek * buffer, and what the other side is may vary from
995*27852ebeSDavid van Moolenbroek * send call to send call. Changing the receive buffer
996*27852ebeSDavid van Moolenbroek * size would disallow us from even accurately guessing
997*27852ebeSDavid van Moolenbroek * the send buffer size in getsockopt calls. Therefore
998*27852ebeSDavid van Moolenbroek * both are hardcoded and cannot actually be changed.
999*27852ebeSDavid van Moolenbroek * In order to support applications that want at least
1000*27852ebeSDavid van Moolenbroek * a certain minimum, we do accept requests to shrink
1001*27852ebeSDavid van Moolenbroek * either buffer, but we ignore the given size.
1002*27852ebeSDavid van Moolenbroek */
1003*27852ebeSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1004*27852ebeSDavid van Moolenbroek len)) != OK)
1005*27852ebeSDavid van Moolenbroek return r;
1006cc5b1988SDavid van Moolenbroek
1007*27852ebeSDavid van Moolenbroek if (val <= 0 || (size_t)val > uds_io_buflen())
1008cc5b1988SDavid van Moolenbroek return EINVAL;
1009cc5b1988SDavid van Moolenbroek
1010*27852ebeSDavid van Moolenbroek return OK; /* ignore new value */
1011*27852ebeSDavid van Moolenbroek }
1012cc5b1988SDavid van Moolenbroek
1013cc5b1988SDavid van Moolenbroek break;
1014cc5b1988SDavid van Moolenbroek
1015*27852ebeSDavid van Moolenbroek case UDSPROTO_UDS:
1016*27852ebeSDavid van Moolenbroek switch (name) {
1017*27852ebeSDavid van Moolenbroek case LOCAL_CREDS:
1018*27852ebeSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1019*27852ebeSDavid van Moolenbroek len)) != OK)
1020cc5b1988SDavid van Moolenbroek return r;
1021cc5b1988SDavid van Moolenbroek
1022*27852ebeSDavid van Moolenbroek if (val)
1023*27852ebeSDavid van Moolenbroek uds->uds_flags |= UDSF_PASSCRED;
1024cc5b1988SDavid van Moolenbroek else
1025*27852ebeSDavid van Moolenbroek uds->uds_flags &= ~UDSF_PASSCRED;
1026cc5b1988SDavid van Moolenbroek
1027cc5b1988SDavid van Moolenbroek /*
1028*27852ebeSDavid van Moolenbroek * In incredibly rare cases, disabling this flag may
1029*27852ebeSDavid van Moolenbroek * allow blocked sends to be resumed, because suddenly
1030*27852ebeSDavid van Moolenbroek * no room for the credentials is needed in the receive
1031*27852ebeSDavid van Moolenbroek * buffer anymore.
1032cc5b1988SDavid van Moolenbroek */
1033*27852ebeSDavid van Moolenbroek if (!val)
1034*27852ebeSDavid van Moolenbroek sockevent_raise(&uds->uds_sock, SEV_SEND);
1035cc5b1988SDavid van Moolenbroek
1036*27852ebeSDavid van Moolenbroek return OK;
1037cc5b1988SDavid van Moolenbroek
1038*27852ebeSDavid van Moolenbroek case LOCAL_CONNWAIT:
1039*27852ebeSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1040*27852ebeSDavid van Moolenbroek len)) != OK)
1041*27852ebeSDavid van Moolenbroek return r;
1042cc5b1988SDavid van Moolenbroek
1043*27852ebeSDavid van Moolenbroek if (val)
1044*27852ebeSDavid van Moolenbroek uds->uds_flags |= UDSF_CONNWAIT;
1045*27852ebeSDavid van Moolenbroek else
1046*27852ebeSDavid van Moolenbroek uds->uds_flags &= ~UDSF_CONNWAIT;
1047cc5b1988SDavid van Moolenbroek
1048cc5b1988SDavid van Moolenbroek /*
1049*27852ebeSDavid van Moolenbroek * Changing the setting does not affect sockets that
1050*27852ebeSDavid van Moolenbroek * are currently pending to be accepted. Therefore,
1051*27852ebeSDavid van Moolenbroek * uds_accept() may have to deal with either case on a
1052*27852ebeSDavid van Moolenbroek * socket-by-socket basis.
1053cc5b1988SDavid van Moolenbroek */
1054*27852ebeSDavid van Moolenbroek return OK;
1055cc5b1988SDavid van Moolenbroek
1056*27852ebeSDavid van Moolenbroek case LOCAL_PEEREID:
1057*27852ebeSDavid van Moolenbroek /* This option may be retrieved but not set. */
1058*27852ebeSDavid van Moolenbroek return ENOPROTOOPT;
1059cc5b1988SDavid van Moolenbroek }
1060cc5b1988SDavid van Moolenbroek
1061*27852ebeSDavid van Moolenbroek break;
1062*27852ebeSDavid van Moolenbroek }
1063cc5b1988SDavid van Moolenbroek
1064*27852ebeSDavid van Moolenbroek return ENOPROTOOPT;
1065cc5b1988SDavid van Moolenbroek }
1066cc5b1988SDavid van Moolenbroek
1067cc5b1988SDavid van Moolenbroek /*
1068*27852ebeSDavid van Moolenbroek * Retrieve socket options.
1069cc5b1988SDavid van Moolenbroek */
1070cc5b1988SDavid van Moolenbroek static int
uds_getsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t * len)1071*27852ebeSDavid van Moolenbroek uds_getsockopt(struct sock * sock, int level, int name,
1072*27852ebeSDavid van Moolenbroek const struct sockdriver_data * data, socklen_t * len)
1073cc5b1988SDavid van Moolenbroek {
1074*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
1075*27852ebeSDavid van Moolenbroek int val;
1076cc5b1988SDavid van Moolenbroek
1077*27852ebeSDavid van Moolenbroek dprintf(("UDS: getsockopt(%d,%d,%d)\n", uds_get_id(uds), level, name));
1078cc5b1988SDavid van Moolenbroek
1079*27852ebeSDavid van Moolenbroek switch (level) {
1080*27852ebeSDavid van Moolenbroek case SOL_SOCKET:
1081*27852ebeSDavid van Moolenbroek switch (name) {
1082*27852ebeSDavid van Moolenbroek case SO_SNDBUF:
1083*27852ebeSDavid van Moolenbroek case SO_RCVBUF:
1084*27852ebeSDavid van Moolenbroek /* See uds_setsockopt() for why this is static. */
1085*27852ebeSDavid van Moolenbroek val = (int)uds_io_buflen();
108668afc771SLionel Sambuc
1087*27852ebeSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
1088*27852ebeSDavid van Moolenbroek len);
1089cc5b1988SDavid van Moolenbroek }
1090cc5b1988SDavid van Moolenbroek
1091*27852ebeSDavid van Moolenbroek break;
1092*27852ebeSDavid van Moolenbroek
1093*27852ebeSDavid van Moolenbroek case UDSPROTO_UDS:
1094*27852ebeSDavid van Moolenbroek switch (name) {
1095*27852ebeSDavid van Moolenbroek case LOCAL_CREDS:
1096*27852ebeSDavid van Moolenbroek val = !!(uds->uds_flags & UDSF_PASSCRED);
1097*27852ebeSDavid van Moolenbroek
1098*27852ebeSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
1099*27852ebeSDavid van Moolenbroek len);
1100*27852ebeSDavid van Moolenbroek
1101*27852ebeSDavid van Moolenbroek case LOCAL_CONNWAIT:
1102*27852ebeSDavid van Moolenbroek val = !!(uds->uds_flags & UDSF_CONNWAIT);
1103*27852ebeSDavid van Moolenbroek
1104*27852ebeSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
1105*27852ebeSDavid van Moolenbroek len);
1106*27852ebeSDavid van Moolenbroek
1107*27852ebeSDavid van Moolenbroek case LOCAL_PEEREID:
1108*27852ebeSDavid van Moolenbroek /* getpeereid(3) documents these error codes. */
1109*27852ebeSDavid van Moolenbroek if (uds_get_type(uds) == SOCK_DGRAM)
1110*27852ebeSDavid van Moolenbroek return EINVAL;
1111*27852ebeSDavid van Moolenbroek if (!uds_is_connected(uds))
1112*27852ebeSDavid van Moolenbroek return ENOTCONN;
1113*27852ebeSDavid van Moolenbroek
1114*27852ebeSDavid van Moolenbroek /*
1115*27852ebeSDavid van Moolenbroek * This is a custom MINIX3 error, indicating that there
1116*27852ebeSDavid van Moolenbroek * are no credentials to return. This could be due to
1117*27852ebeSDavid van Moolenbroek * a failure to obtain them (which *should* not happen)
1118*27852ebeSDavid van Moolenbroek * but also if the socket was bound while connected,
1119*27852ebeSDavid van Moolenbroek * disconnected, and then reused as listening socket.
1120*27852ebeSDavid van Moolenbroek */
1121*27852ebeSDavid van Moolenbroek if (uds->uds_conn->uds_cred.unp_pid == -1)
1122*27852ebeSDavid van Moolenbroek return EINVAL;
1123*27852ebeSDavid van Moolenbroek
1124*27852ebeSDavid van Moolenbroek return sockdriver_copyout_opt(data,
1125*27852ebeSDavid van Moolenbroek &uds->uds_conn->uds_cred,
1126*27852ebeSDavid van Moolenbroek sizeof(uds->uds_conn->uds_cred), len);
1127*27852ebeSDavid van Moolenbroek }
1128*27852ebeSDavid van Moolenbroek
1129*27852ebeSDavid van Moolenbroek break;
1130*27852ebeSDavid van Moolenbroek }
1131*27852ebeSDavid van Moolenbroek
1132*27852ebeSDavid van Moolenbroek return ENOPROTOOPT;
1133*27852ebeSDavid van Moolenbroek }
1134*27852ebeSDavid van Moolenbroek
1135*27852ebeSDavid van Moolenbroek /*
1136*27852ebeSDavid van Moolenbroek * Retrieve a socket's local address.
1137*27852ebeSDavid van Moolenbroek */
1138*27852ebeSDavid van Moolenbroek static int
uds_getsockname(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)1139*27852ebeSDavid van Moolenbroek uds_getsockname(struct sock * sock, struct sockaddr * addr,
1140*27852ebeSDavid van Moolenbroek socklen_t * addr_len)
1141*27852ebeSDavid van Moolenbroek {
1142*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
1143*27852ebeSDavid van Moolenbroek
1144*27852ebeSDavid van Moolenbroek dprintf(("UDS: getsockname(%d)\n", uds_get_id(uds)));
1145*27852ebeSDavid van Moolenbroek
1146*27852ebeSDavid van Moolenbroek uds_make_addr(uds->uds_path, uds->uds_pathlen, addr, addr_len);
1147*27852ebeSDavid van Moolenbroek
1148*27852ebeSDavid van Moolenbroek return OK;
1149*27852ebeSDavid van Moolenbroek }
1150*27852ebeSDavid van Moolenbroek
1151*27852ebeSDavid van Moolenbroek /*
1152*27852ebeSDavid van Moolenbroek * Retrieve a socket's remote address.
1153*27852ebeSDavid van Moolenbroek */
1154*27852ebeSDavid van Moolenbroek static int
uds_getpeername(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)1155*27852ebeSDavid van Moolenbroek uds_getpeername(struct sock * sock, struct sockaddr * addr,
1156*27852ebeSDavid van Moolenbroek socklen_t * addr_len)
1157*27852ebeSDavid van Moolenbroek {
1158*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
1159*27852ebeSDavid van Moolenbroek struct udssock *peer;
1160*27852ebeSDavid van Moolenbroek
1161*27852ebeSDavid van Moolenbroek dprintf(("UDS: getpeername(%d)\n", uds_get_id(uds)));
1162*27852ebeSDavid van Moolenbroek
1163*27852ebeSDavid van Moolenbroek /*
1164*27852ebeSDavid van Moolenbroek * For disconnected sockets, we no longer have a peer socket and thus
1165*27852ebeSDavid van Moolenbroek * also no peer address. Too bad, but NetBSD does the same.
1166*27852ebeSDavid van Moolenbroek *
1167*27852ebeSDavid van Moolenbroek * For connecting sockets we could in fact return a peer address, but
1168*27852ebeSDavid van Moolenbroek * POSIX says (and other platforms agree) that we should deny the call.
1169*27852ebeSDavid van Moolenbroek */
1170*27852ebeSDavid van Moolenbroek peer = uds_get_peer(uds);
1171*27852ebeSDavid van Moolenbroek
1172*27852ebeSDavid van Moolenbroek if (peer == NULL || uds_is_connecting(uds))
1173*27852ebeSDavid van Moolenbroek return ENOTCONN;
1174*27852ebeSDavid van Moolenbroek
1175*27852ebeSDavid van Moolenbroek uds_make_addr(peer->uds_path, peer->uds_pathlen, addr, addr_len);
1176*27852ebeSDavid van Moolenbroek
1177*27852ebeSDavid van Moolenbroek return OK;
1178*27852ebeSDavid van Moolenbroek }
1179*27852ebeSDavid van Moolenbroek
1180*27852ebeSDavid van Moolenbroek /*
1181*27852ebeSDavid van Moolenbroek * Shut down socket send and receive operations. Note that 'flags' is a
1182*27852ebeSDavid van Moolenbroek * bitwise mask with libsockevent's SFL_SHUT_{RD,WR} flags rather than the set
1183*27852ebeSDavid van Moolenbroek * of SHUT_{RD,WR,RDWR} values from userland.
1184*27852ebeSDavid van Moolenbroek */
1185*27852ebeSDavid van Moolenbroek static int
uds_shutdown(struct sock * sock,unsigned int flags)1186*27852ebeSDavid van Moolenbroek uds_shutdown(struct sock * sock, unsigned int flags)
1187*27852ebeSDavid van Moolenbroek {
1188*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
1189*27852ebeSDavid van Moolenbroek struct udssock *conn;
1190*27852ebeSDavid van Moolenbroek unsigned int mask;
1191*27852ebeSDavid van Moolenbroek
1192*27852ebeSDavid van Moolenbroek dprintf(("UDS: shutdown(%d,0x%x)\n", uds_get_id(uds), flags));
1193*27852ebeSDavid van Moolenbroek
1194*27852ebeSDavid van Moolenbroek /*
1195*27852ebeSDavid van Moolenbroek * If we are shutting down the socket for reading, we can already close
1196*27852ebeSDavid van Moolenbroek * any in-flight file descriptors associated with this socket.
1197*27852ebeSDavid van Moolenbroek */
1198*27852ebeSDavid van Moolenbroek if (flags & SFL_SHUT_RD)
1199*27852ebeSDavid van Moolenbroek uds_io_reset(uds);
1200*27852ebeSDavid van Moolenbroek
1201*27852ebeSDavid van Moolenbroek /*
1202*27852ebeSDavid van Moolenbroek * A shutdown on this side of a connection may have an effect on
1203*27852ebeSDavid van Moolenbroek * ongoing operations on the other side. Fire appropriate events.
1204*27852ebeSDavid van Moolenbroek */
1205*27852ebeSDavid van Moolenbroek if (uds_is_connected(uds)) {
1206*27852ebeSDavid van Moolenbroek assert(uds_get_type(uds) != SOCK_DGRAM);
1207*27852ebeSDavid van Moolenbroek
1208*27852ebeSDavid van Moolenbroek conn = uds->uds_conn;
1209*27852ebeSDavid van Moolenbroek
1210*27852ebeSDavid van Moolenbroek mask = 0;
1211*27852ebeSDavid van Moolenbroek if (flags & SFL_SHUT_RD)
1212*27852ebeSDavid van Moolenbroek mask |= SEV_SEND;
1213*27852ebeSDavid van Moolenbroek if (flags & SFL_SHUT_WR)
1214*27852ebeSDavid van Moolenbroek mask |= SEV_RECV;
1215*27852ebeSDavid van Moolenbroek
1216*27852ebeSDavid van Moolenbroek sockevent_raise(&conn->uds_sock, mask);
1217*27852ebeSDavid van Moolenbroek }
1218*27852ebeSDavid van Moolenbroek
1219*27852ebeSDavid van Moolenbroek return OK;
1220*27852ebeSDavid van Moolenbroek }
1221*27852ebeSDavid van Moolenbroek
1222*27852ebeSDavid van Moolenbroek /*
1223*27852ebeSDavid van Moolenbroek * Close a socket.
1224*27852ebeSDavid van Moolenbroek *
1225*27852ebeSDavid van Moolenbroek * The 'force' flag is unused because we need never wait for data to be sent,
1226*27852ebeSDavid van Moolenbroek * since we keep all in-flight data on the receiver side.
1227*27852ebeSDavid van Moolenbroek */
1228*27852ebeSDavid van Moolenbroek static int
uds_close(struct sock * sock,int force __unused)1229*27852ebeSDavid van Moolenbroek uds_close(struct sock * sock, int force __unused)
1230*27852ebeSDavid van Moolenbroek {
1231*27852ebeSDavid van Moolenbroek struct udssock *uds = (struct udssock *)sock;
1232*27852ebeSDavid van Moolenbroek
1233*27852ebeSDavid van Moolenbroek dprintf(("UDS: close(%d)\n", uds_get_id(uds)));
1234*27852ebeSDavid van Moolenbroek
1235*27852ebeSDavid van Moolenbroek if (uds_get_type(uds) == SOCK_DGRAM) {
1236*27852ebeSDavid van Moolenbroek /* If this socket is linked to a target, disconnect it. */
1237*27852ebeSDavid van Moolenbroek if (uds_has_link(uds))
1238*27852ebeSDavid van Moolenbroek uds_del_queue(uds->uds_link, uds);
1239*27852ebeSDavid van Moolenbroek
1240*27852ebeSDavid van Moolenbroek /* Reset all sockets linked to this socket as a target. */
1241*27852ebeSDavid van Moolenbroek uds_clear_queue(uds, NULL);
1242*27852ebeSDavid van Moolenbroek } else if (uds_is_listening(uds)) {
1243*27852ebeSDavid van Moolenbroek /*
1244*27852ebeSDavid van Moolenbroek * Abort all connecting sockets queued on this socket, and
1245*27852ebeSDavid van Moolenbroek * break all connections for connected sockets queued on this
1246*27852ebeSDavid van Moolenbroek * socket, freeing their peers.
1247*27852ebeSDavid van Moolenbroek */
1248*27852ebeSDavid van Moolenbroek uds_clear_queue(uds, NULL);
1249*27852ebeSDavid van Moolenbroek } else if (uds_has_link(uds)) {
1250*27852ebeSDavid van Moolenbroek /*
1251*27852ebeSDavid van Moolenbroek * This socket is connecting or connected while the other side
1252*27852ebeSDavid van Moolenbroek * has not been accepted yet. Remove the socket from the
1253*27852ebeSDavid van Moolenbroek * listening socket's queue, and if it was connected, get rid
1254*27852ebeSDavid van Moolenbroek * of its peer socket altogether.
1255*27852ebeSDavid van Moolenbroek */
1256*27852ebeSDavid van Moolenbroek assert(uds_is_listening(uds->uds_link));
1257*27852ebeSDavid van Moolenbroek
1258*27852ebeSDavid van Moolenbroek uds_del_queue(uds->uds_link, uds);
1259*27852ebeSDavid van Moolenbroek
1260*27852ebeSDavid van Moolenbroek if (uds_is_connected(uds))
1261*27852ebeSDavid van Moolenbroek uds_disconnect(uds, TRUE /*was_linked*/);
1262*27852ebeSDavid van Moolenbroek } else if (uds_is_connected(uds)) {
1263*27852ebeSDavid van Moolenbroek /*
1264*27852ebeSDavid van Moolenbroek * Decouple the peer socket from this socket, and possibly wake
1265*27852ebeSDavid van Moolenbroek * up any pending operations on it. The socket remains marked
1266*27852ebeSDavid van Moolenbroek * as connected, but will now be disconnected.
1267*27852ebeSDavid van Moolenbroek */
1268*27852ebeSDavid van Moolenbroek uds_disconnect(uds, FALSE /*was_linked*/);
1269*27852ebeSDavid van Moolenbroek }
1270*27852ebeSDavid van Moolenbroek
1271*27852ebeSDavid van Moolenbroek if (uds_is_hashed(uds))
1272*27852ebeSDavid van Moolenbroek udshash_del(uds);
1273*27852ebeSDavid van Moolenbroek
1274*27852ebeSDavid van Moolenbroek return OK;
1275*27852ebeSDavid van Moolenbroek }
1276*27852ebeSDavid van Moolenbroek
1277*27852ebeSDavid van Moolenbroek static const struct sockevent_ops uds_ops = {
1278*27852ebeSDavid van Moolenbroek .sop_pair = uds_pair,
1279*27852ebeSDavid van Moolenbroek .sop_bind = uds_bind,
1280*27852ebeSDavid van Moolenbroek .sop_connect = uds_connect,
1281*27852ebeSDavid van Moolenbroek .sop_listen = uds_listen,
1282*27852ebeSDavid van Moolenbroek .sop_accept = uds_accept,
1283*27852ebeSDavid van Moolenbroek .sop_test_accept = uds_test_accept,
1284*27852ebeSDavid van Moolenbroek .sop_pre_send = uds_pre_send,
1285*27852ebeSDavid van Moolenbroek .sop_send = uds_send,
1286*27852ebeSDavid van Moolenbroek .sop_test_send = uds_test_send,
1287*27852ebeSDavid van Moolenbroek .sop_pre_recv = uds_pre_recv,
1288*27852ebeSDavid van Moolenbroek .sop_recv = uds_recv,
1289*27852ebeSDavid van Moolenbroek .sop_test_recv = uds_test_recv,
1290*27852ebeSDavid van Moolenbroek .sop_setsockopt = uds_setsockopt,
1291*27852ebeSDavid van Moolenbroek .sop_getsockopt = uds_getsockopt,
1292*27852ebeSDavid van Moolenbroek .sop_getsockname = uds_getsockname,
1293*27852ebeSDavid van Moolenbroek .sop_getpeername = uds_getpeername,
1294*27852ebeSDavid van Moolenbroek .sop_shutdown = uds_shutdown,
1295*27852ebeSDavid van Moolenbroek .sop_close = uds_close,
1296*27852ebeSDavid van Moolenbroek .sop_free = uds_free
1297*27852ebeSDavid van Moolenbroek };
1298*27852ebeSDavid van Moolenbroek
1299*27852ebeSDavid van Moolenbroek /*
1300*27852ebeSDavid van Moolenbroek * Initialize the service.
1301*27852ebeSDavid van Moolenbroek */
1302*27852ebeSDavid van Moolenbroek static int
uds_init(int type __unused,sef_init_info_t * info __unused)1303*27852ebeSDavid van Moolenbroek uds_init(int type __unused, sef_init_info_t * info __unused)
1304*27852ebeSDavid van Moolenbroek {
1305*27852ebeSDavid van Moolenbroek unsigned int i;
1306*27852ebeSDavid van Moolenbroek
1307*27852ebeSDavid van Moolenbroek /* Initialize the list of free sockets. */
1308*27852ebeSDavid van Moolenbroek TAILQ_INIT(&uds_freelist);
1309*27852ebeSDavid van Moolenbroek
1310*27852ebeSDavid van Moolenbroek for (i = 0; i < __arraycount(uds_array); i++) {
1311*27852ebeSDavid van Moolenbroek uds_array[i].uds_flags = 0;
1312*27852ebeSDavid van Moolenbroek
1313*27852ebeSDavid van Moolenbroek TAILQ_INSERT_TAIL(&uds_freelist, &uds_array[i], uds_next);
1314*27852ebeSDavid van Moolenbroek }
1315*27852ebeSDavid van Moolenbroek
1316*27852ebeSDavid van Moolenbroek /* Initialize the file-to-socket hash table. */
1317*27852ebeSDavid van Moolenbroek udshash_init();
1318*27852ebeSDavid van Moolenbroek
1319*27852ebeSDavid van Moolenbroek /* Initialize the input/output module. */
1320*27852ebeSDavid van Moolenbroek uds_io_init();
1321*27852ebeSDavid van Moolenbroek
1322*27852ebeSDavid van Moolenbroek /* Initialize the status module. */
1323*27852ebeSDavid van Moolenbroek uds_stat_init();
1324*27852ebeSDavid van Moolenbroek
1325*27852ebeSDavid van Moolenbroek /* Initialize the sockevent library. */
1326*27852ebeSDavid van Moolenbroek sockevent_init(uds_socket);
1327*27852ebeSDavid van Moolenbroek
1328*27852ebeSDavid van Moolenbroek uds_in_use = 0;
1329*27852ebeSDavid van Moolenbroek uds_running = TRUE;
1330*27852ebeSDavid van Moolenbroek
1331*27852ebeSDavid van Moolenbroek return OK;
1332*27852ebeSDavid van Moolenbroek }
1333*27852ebeSDavid van Moolenbroek
1334*27852ebeSDavid van Moolenbroek /*
1335*27852ebeSDavid van Moolenbroek * Clean up before shutdown.
1336*27852ebeSDavid van Moolenbroek */
1337*27852ebeSDavid van Moolenbroek static void
uds_cleanup(void)1338*27852ebeSDavid van Moolenbroek uds_cleanup(void)
1339*27852ebeSDavid van Moolenbroek {
1340*27852ebeSDavid van Moolenbroek
1341*27852ebeSDavid van Moolenbroek /* Tell the status module to clean up. */
1342*27852ebeSDavid van Moolenbroek uds_stat_cleanup();
1343*27852ebeSDavid van Moolenbroek }
1344*27852ebeSDavid van Moolenbroek
1345*27852ebeSDavid van Moolenbroek /*
1346*27852ebeSDavid van Moolenbroek * The service has received a signal.
1347*27852ebeSDavid van Moolenbroek */
1348cc5b1988SDavid van Moolenbroek static void
uds_signal(int signo)1349cc5b1988SDavid van Moolenbroek uds_signal(int signo)
1350cc5b1988SDavid van Moolenbroek {
1351cc5b1988SDavid van Moolenbroek
1352*27852ebeSDavid van Moolenbroek /* Only check for the termination signal. Ignore anything else. */
1353*27852ebeSDavid van Moolenbroek if (signo != SIGTERM)
1354*27852ebeSDavid van Moolenbroek return;
1355cc5b1988SDavid van Moolenbroek
1356*27852ebeSDavid van Moolenbroek /* Exit only once all sockets have been closed. */
1357*27852ebeSDavid van Moolenbroek uds_running = FALSE;
1358cc5b1988SDavid van Moolenbroek
1359*27852ebeSDavid van Moolenbroek if (uds_in_use == 0)
1360*27852ebeSDavid van Moolenbroek sef_cancel();
1361cc5b1988SDavid van Moolenbroek }
1362cc5b1988SDavid van Moolenbroek
1363*27852ebeSDavid van Moolenbroek /*
1364*27852ebeSDavid van Moolenbroek * Perform initialization using the System Event Framework (SEF).
1365*27852ebeSDavid van Moolenbroek */
1366cc5b1988SDavid van Moolenbroek static void
uds_startup(void)1367cc5b1988SDavid van Moolenbroek uds_startup(void)
1368cc5b1988SDavid van Moolenbroek {
1369*27852ebeSDavid van Moolenbroek
1370*27852ebeSDavid van Moolenbroek /* Register initialization callbacks. */
1371cc5b1988SDavid van Moolenbroek sef_setcb_init_fresh(uds_init);
1372cc5b1988SDavid van Moolenbroek
1373*27852ebeSDavid van Moolenbroek /* Register signal callback. */
1374cc5b1988SDavid van Moolenbroek sef_setcb_signal_handler(uds_signal);
1375cc5b1988SDavid van Moolenbroek
1376cc5b1988SDavid van Moolenbroek /* Let SEF perform startup. */
1377cc5b1988SDavid van Moolenbroek sef_startup();
1378cc5b1988SDavid van Moolenbroek }
1379cc5b1988SDavid van Moolenbroek
1380cc5b1988SDavid van Moolenbroek /*
1381*27852ebeSDavid van Moolenbroek * The UNIX Domain Sockets driver.
1382cc5b1988SDavid van Moolenbroek */
1383cc5b1988SDavid van Moolenbroek int
main(void)1384cc5b1988SDavid van Moolenbroek main(void)
1385cc5b1988SDavid van Moolenbroek {
1386*27852ebeSDavid van Moolenbroek message m;
1387*27852ebeSDavid van Moolenbroek int r, ipc_status;
1388*27852ebeSDavid van Moolenbroek
1389*27852ebeSDavid van Moolenbroek /* Initialize the service. */
1390cc5b1988SDavid van Moolenbroek uds_startup();
1391cc5b1988SDavid van Moolenbroek
1392*27852ebeSDavid van Moolenbroek /* Loop receiving and processing messages until instructed to stop. */
1393*27852ebeSDavid van Moolenbroek while (uds_running || uds_in_use > 0) {
1394*27852ebeSDavid van Moolenbroek if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) {
1395*27852ebeSDavid van Moolenbroek if (r == EINTR)
1396*27852ebeSDavid van Moolenbroek continue; /* sef_cancel() was called */
1397cc5b1988SDavid van Moolenbroek
1398*27852ebeSDavid van Moolenbroek panic("UDS: sef_receive_status failed: %d", r);
1399*27852ebeSDavid van Moolenbroek }
1400*27852ebeSDavid van Moolenbroek
1401*27852ebeSDavid van Moolenbroek /*
1402*27852ebeSDavid van Moolenbroek * Messages from the MIB service are (ultimately) for the
1403*27852ebeSDavid van Moolenbroek * status module. Everything else is assumed to be a socket
1404*27852ebeSDavid van Moolenbroek * request and passed to libsockevent, which will ignore
1405*27852ebeSDavid van Moolenbroek * anything it does not recognize.
1406*27852ebeSDavid van Moolenbroek */
1407*27852ebeSDavid van Moolenbroek if (m.m_source == MIB_PROC_NR)
1408*27852ebeSDavid van Moolenbroek rmib_process(&m, ipc_status);
1409*27852ebeSDavid van Moolenbroek else
1410*27852ebeSDavid van Moolenbroek sockevent_process(&m, ipc_status);
1411*27852ebeSDavid van Moolenbroek }
1412*27852ebeSDavid van Moolenbroek
1413*27852ebeSDavid van Moolenbroek /* Clean up before graceful shutdown. */
1414*27852ebeSDavid van Moolenbroek uds_cleanup();
1415*27852ebeSDavid van Moolenbroek
1416*27852ebeSDavid van Moolenbroek return EXIT_SUCCESS;
1417cc5b1988SDavid van Moolenbroek }
1418