xref: /haiku/src/add-ons/kernel/network/protocols/unix/UnixEndpoint.cpp (revision e221c09e508ffc3c62738140c9b6fc4fa211662a)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "UnixEndpoint.h"
7 
8 #include <stdio.h>
9 #include <sys/stat.h>
10 
11 #include <AutoDeleter.h>
12 
13 #include <vfs.h>
14 
15 #include "UnixAddressManager.h"
16 #include "UnixFifo.h"
17 
18 
19 #define UNIX_ENDPOINT_DEBUG_LEVEL	0
20 #define UNIX_DEBUG_LEVEL			UNIX_ENDPOINT_DEBUG_LEVEL
21 #include "UnixDebug.h"
22 
23 
24 // Note on locking order (outermost -> innermost):
25 // UnixEndpoint: connecting -> listening -> child
26 // -> UnixFifo (never lock more than one at a time)
27 // -> UnixAddressManager
28 
29 
30 static inline bigtime_t
31 absolute_timeout(bigtime_t timeout)
32 {
33 	if (timeout == 0 || timeout == B_INFINITE_TIMEOUT)
34 		return timeout;
35 
36 // TODO: Make overflow safe!
37 	return timeout + system_time();
38 }
39 
40 
41 UnixEndpoint::UnixEndpoint(net_socket* socket)
42 	:
43 	ProtocolSocket(socket),
44 	fAddress(),
45 	fAddressHashLink(),
46 	fPeerEndpoint(NULL),
47 	fReceiveFifo(NULL),
48 	fState(UNIX_ENDPOINT_CLOSED),
49 	fAcceptSemaphore(-1),
50 	fIsChild(false)
51 {
52 	TRACE("[%ld] %p->UnixEndpoint::UnixEndpoint()\n", find_thread(NULL), this);
53 
54 	mutex_init(&fLock, "unix endpoint");
55 }
56 
57 
58 UnixEndpoint::~UnixEndpoint()
59 {
60 	TRACE("[%ld] %p->UnixEndpoint::~UnixEndpoint()\n", find_thread(NULL), this);
61 
62 	mutex_destroy(&fLock);
63 }
64 
65 
66 status_t
67 UnixEndpoint::Init()
68 {
69 	TRACE("[%ld] %p->UnixEndpoint::Init()\n", find_thread(NULL), this);
70 
71 	RETURN_ERROR(B_OK);
72 }
73 
74 
75 void
76 UnixEndpoint::Uninit()
77 {
78 	TRACE("[%ld] %p->UnixEndpoint::Uninit()\n", find_thread(NULL), this);
79 
80 	// check whether we're closed
81 	UnixEndpointLocker locker(this);
82 	bool closed = (fState == UNIX_ENDPOINT_CLOSED);
83 	locker.Unlock();
84 
85 	if (!closed) {
86 		// That probably means, we're a child endpoint of a listener and
87 		// have been fully connected, but not yet accepted. Our Close()
88 		// hook isn't called in this case. Do it manually.
89 		Close();
90 	}
91 
92 	RemoveReference();
93 }
94 
95 
96 status_t
97 UnixEndpoint::Open()
98 {
99 	TRACE("[%ld] %p->UnixEndpoint::Open()\n", find_thread(NULL), this);
100 
101 	status_t error = ProtocolSocket::Open();
102 	if (error != B_OK)
103 		RETURN_ERROR(error);
104 
105 	fState = UNIX_ENDPOINT_NOT_CONNECTED;
106 
107 	RETURN_ERROR(B_OK);
108 }
109 
110 
111 status_t
112 UnixEndpoint::Close()
113 {
114 	TRACE("[%ld] %p->UnixEndpoint::Close()\n", find_thread(NULL), this);
115 
116 	UnixEndpointLocker locker(this);
117 
118 	if (fState == UNIX_ENDPOINT_CONNECTED) {
119 		UnixEndpointLocker peerLocker;
120 		if (_LockConnectedEndpoints(locker, peerLocker) == B_OK) {
121 			// We're still connected. Disconnect both endpoints!
122 			fPeerEndpoint->_Disconnect();
123 			_Disconnect();
124 		}
125 	}
126 
127 	if (fState == UNIX_ENDPOINT_LISTENING)
128 		_StopListening();
129 
130 	_Unbind();
131 
132 	fState = UNIX_ENDPOINT_CLOSED;
133 	RETURN_ERROR(B_OK);
134 }
135 
136 
137 status_t
138 UnixEndpoint::Free()
139 {
140 	TRACE("[%ld] %p->UnixEndpoint::Free()\n", find_thread(NULL), this);
141 
142 	UnixEndpointLocker locker(this);
143 
144 	_UnsetReceiveFifo();
145 
146 	RETURN_ERROR(B_OK);
147 }
148 
149 
150 status_t
151 UnixEndpoint::Bind(const struct sockaddr *_address)
152 {
153 	if (_address->sa_family != AF_UNIX)
154 		RETURN_ERROR(EAFNOSUPPORT);
155 
156 	TRACE("[%ld] %p->UnixEndpoint::Bind(\"%s\")\n", find_thread(NULL), this,
157 		ConstSocketAddress(&gAddressModule, _address).AsString().Data());
158 
159 	const sockaddr_un* address = (const sockaddr_un*)_address;
160 
161 	UnixEndpointLocker endpointLocker(this);
162 
163 	if (fState != UNIX_ENDPOINT_NOT_CONNECTED || IsBound())
164 		RETURN_ERROR(B_BAD_VALUE);
165 
166 	if (address->sun_path[0] == '\0') {
167 		UnixAddressManagerLocker addressLocker(gAddressManager);
168 
169 		// internal address space (or empty address)
170 		int32 internalID;
171 		if (UnixAddress::IsEmptyAddress(*address))
172 			internalID = gAddressManager.NextUnusedInternalID();
173 		else
174 			internalID = UnixAddress::InternalID(*address);
175 		if (internalID < 0)
176 			RETURN_ERROR(internalID);
177 
178 		status_t error = _Bind(internalID);
179 		if (error != B_OK)
180 			RETURN_ERROR(error);
181 
182 		sockaddr_un* outAddress = (sockaddr_un*)&socket->address;
183 		outAddress->sun_path[0] = '\0';
184 		sprintf(outAddress->sun_path + 1, "%05lx", internalID);
185 		outAddress->sun_len = INTERNAL_UNIX_ADDRESS_LEN;
186 			// null-byte + 5 hex digits
187 
188 		gAddressManager.Add(this);
189 	} else {
190 		// FS address space
191 		size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path));
192 		if (pathLen == 0 || pathLen == sizeof(address->sun_path))
193 			RETURN_ERROR(B_BAD_VALUE);
194 
195 		struct vnode* vnode;
196 		status_t error = vfs_create_special_node(address->sun_path,
197 			NULL, S_IFSOCK | 0644, 0, !gStackModule->is_syscall(), NULL,
198 			&vnode);
199 		if (error != B_OK)
200 			RETURN_ERROR(error == B_FILE_EXISTS ? EADDRINUSE : error);
201 
202 		error = _Bind(vnode);
203 		if (error != B_OK) {
204 			vfs_put_vnode(vnode);
205 			RETURN_ERROR(error);
206 		}
207 
208 		size_t addressLen = address->sun_path + pathLen + 1 - (char*)address;
209 		memcpy(&socket->address, address, addressLen);
210 		socket->address.ss_len = addressLen;
211 
212 		UnixAddressManagerLocker addressLocker(gAddressManager);
213 		gAddressManager.Add(this);
214 	}
215 
216 	RETURN_ERROR(B_OK);
217 }
218 
219 
220 status_t
221 UnixEndpoint::Unbind()
222 {
223 	TRACE("[%ld] %p->UnixEndpoint::Unbind()\n", find_thread(NULL), this);
224 
225 	UnixEndpointLocker endpointLocker(this);
226 
227 	RETURN_ERROR(_Unbind());
228 }
229 
230 
231 status_t
232 UnixEndpoint::Listen(int backlog)
233 {
234 	TRACE("[%ld] %p->UnixEndpoint::Listen(%d)\n", find_thread(NULL), this,
235 		backlog);
236 
237 	UnixEndpointLocker endpointLocker(this);
238 
239 	if (!IsBound())
240 		RETURN_ERROR(EDESTADDRREQ);
241 	if (fState != UNIX_ENDPOINT_NOT_CONNECTED)
242 		RETURN_ERROR(EINVAL);
243 
244 	gSocketModule->set_max_backlog(socket, backlog);
245 
246 	fAcceptSemaphore = create_sem(0, "unix accept");
247 	if (fAcceptSemaphore < 0)
248 		RETURN_ERROR(ENOBUFS);
249 
250 	_UnsetReceiveFifo();
251 
252 	fCredentials.pid = getpid();
253 	fCredentials.uid = geteuid();
254 	fCredentials.gid = getegid();
255 
256 	fState = UNIX_ENDPOINT_LISTENING;
257 
258 	RETURN_ERROR(B_OK);
259 }
260 
261 
262 status_t
263 UnixEndpoint::Connect(const struct sockaddr *_address)
264 {
265 	if (_address->sa_family != AF_UNIX)
266 		RETURN_ERROR(EAFNOSUPPORT);
267 
268 	TRACE("[%ld] %p->UnixEndpoint::Connect(\"%s\")\n", find_thread(NULL), this,
269 		ConstSocketAddress(&gAddressModule, _address).AsString().Data());
270 
271 	const sockaddr_un* address = (const sockaddr_un*)_address;
272 
273 	UnixEndpointLocker endpointLocker(this);
274 
275 	if (fState == UNIX_ENDPOINT_CONNECTED)
276 		RETURN_ERROR(EISCONN);
277 
278 	if (fState != UNIX_ENDPOINT_NOT_CONNECTED)
279 		RETURN_ERROR(B_BAD_VALUE);
280 // TODO: If listening, we could set the backlog to 0 and connect.
281 
282 	// check the address first
283 	UnixAddress unixAddress;
284 
285 	if (address->sun_path[0] == '\0') {
286 		// internal address space (or empty address)
287 		int32 internalID;
288 		if (UnixAddress::IsEmptyAddress(*address))
289 			RETURN_ERROR(B_BAD_VALUE);
290 
291 		internalID = UnixAddress::InternalID(*address);
292 		if (internalID < 0)
293 			RETURN_ERROR(internalID);
294 
295 		unixAddress.SetTo(internalID);
296 	} else {
297 		// FS address space
298 		size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path));
299 		if (pathLen == 0 || pathLen == sizeof(address->sun_path))
300 			RETURN_ERROR(B_BAD_VALUE);
301 
302 		struct stat st;
303 		status_t error = vfs_read_stat(-1, address->sun_path, true, &st,
304 			!gStackModule->is_syscall());
305 		if (error != B_OK)
306 			RETURN_ERROR(error);
307 
308 		if (!S_ISSOCK(st.st_mode))
309 			RETURN_ERROR(B_BAD_VALUE);
310 
311 		unixAddress.SetTo(st.st_dev, st.st_ino, NULL);
312 	}
313 
314 	// get the peer endpoint
315 	UnixAddressManagerLocker addressLocker(gAddressManager);
316 	UnixEndpoint* listeningEndpoint = gAddressManager.Lookup(unixAddress);
317 	if (listeningEndpoint == NULL)
318 		RETURN_ERROR(ECONNREFUSED);
319 	Reference<UnixEndpoint> peerReference(listeningEndpoint);
320 	addressLocker.Unlock();
321 
322 	UnixEndpointLocker peerLocker(listeningEndpoint);
323 
324 	if (!listeningEndpoint->IsBound()
325 		|| listeningEndpoint->fState != UNIX_ENDPOINT_LISTENING
326 		|| listeningEndpoint->fAddress != unixAddress) {
327 		RETURN_ERROR(ECONNREFUSED);
328 	}
329 
330 	// Allocate FIFOs for us and the socket we're going to spawn. We do that
331 	// now, so that the mess we need to cleanup, if allocating them fails, is
332 	// harmless.
333 	UnixFifo* fifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT);
334 	UnixFifo* peerFifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT);
335 	ObjectDeleter<UnixFifo> fifoDeleter(fifo);
336 	ObjectDeleter<UnixFifo> peerFifoDeleter(peerFifo);
337 
338 	status_t error;
339 	if ((error = fifo->Init()) != B_OK || (error = peerFifo->Init()) != B_OK)
340 		return error;
341 
342 	// spawn new endpoint for accept()
343 	net_socket* newSocket;
344 	error = gSocketModule->spawn_pending_socket(listeningEndpoint->socket,
345 		&newSocket);
346 	if (error != B_OK)
347 		RETURN_ERROR(error);
348 
349 	// init connected peer endpoint
350 	UnixEndpoint* connectedEndpoint = (UnixEndpoint*)newSocket->first_protocol;
351 
352 	UnixEndpointLocker connectedLocker(connectedEndpoint);
353 
354 	connectedEndpoint->_Spawn(this, listeningEndpoint, peerFifo);
355 
356 	// update our attributes
357 	_UnsetReceiveFifo();
358 
359 	fPeerEndpoint = connectedEndpoint;
360 	PeerAddress().SetTo(&connectedEndpoint->socket->address);
361 	fPeerEndpoint->AddReference();
362 	fReceiveFifo = fifo;
363 
364 	fCredentials.pid = getpid();
365 	fCredentials.uid = geteuid();
366 	fCredentials.gid = getegid();
367 
368 	fifoDeleter.Detach();
369 	peerFifoDeleter.Detach();
370 
371 	fState = UNIX_ENDPOINT_CONNECTED;
372 
373 	gSocketModule->set_connected(newSocket);
374 
375 	release_sem(listeningEndpoint->fAcceptSemaphore);
376 
377 	connectedLocker.Unlock();
378 	peerLocker.Unlock();
379 	endpointLocker.Unlock();
380 
381 	RETURN_ERROR(B_OK);
382 }
383 
384 
385 status_t
386 UnixEndpoint::Accept(net_socket **_acceptedSocket)
387 {
388 	TRACE("[%ld] %p->UnixEndpoint::Accept()\n", find_thread(NULL), this);
389 
390 	bigtime_t timeout = absolute_timeout(socket->receive.timeout);
391 	if (gStackModule->is_restarted_syscall())
392 		timeout = gStackModule->restore_syscall_restart_timeout();
393 	else
394 		gStackModule->store_syscall_restart_timeout(timeout);
395 
396 	UnixEndpointLocker locker(this);
397 
398 	status_t error;
399 	do {
400 		locker.Unlock();
401 
402 		error = acquire_sem_etc(fAcceptSemaphore, 1,
403 			B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, timeout);
404 		if (error < B_OK)
405 			RETURN_ERROR(error);
406 
407 		locker.Lock();
408 		error = gSocketModule->dequeue_connected(socket, _acceptedSocket);
409 	} while (error != B_OK);
410 
411 	if (error == B_TIMED_OUT && timeout == 0) {
412 		// translate non-blocking timeouts to the correct error code
413 		error = B_WOULD_BLOCK;
414 	}
415 
416 	RETURN_ERROR(error);
417 }
418 
419 
420 ssize_t
421 UnixEndpoint::Send(const iovec *vecs, size_t vecCount,
422 	ancillary_data_container *ancillaryData)
423 {
424 	TRACE("[%ld] %p->UnixEndpoint::Send(%p, %ld, %p)\n", find_thread(NULL),
425 		this, vecs, vecCount, ancillaryData);
426 
427 	bigtime_t timeout = absolute_timeout(socket->send.timeout);
428 	if (gStackModule->is_restarted_syscall())
429 		timeout = gStackModule->restore_syscall_restart_timeout();
430 	else
431 		gStackModule->store_syscall_restart_timeout(timeout);
432 
433 	UnixEndpointLocker locker(this);
434 	UnixEndpointLocker peerLocker;
435 
436 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
437 	if (error != B_OK)
438 		RETURN_ERROR(error);
439 
440 	UnixEndpoint* peerEndpoint = fPeerEndpoint;
441 	Reference<UnixEndpoint> peerReference(peerEndpoint);
442 
443 	// lock the peer's FIFO
444 	UnixFifo* peerFifo = peerEndpoint->fReceiveFifo;
445 	Reference<UnixFifo> _(peerFifo);
446 	UnixFifoLocker fifoLocker(peerFifo);
447 
448 	// unlock endpoints
449 	locker.Unlock();
450 	peerLocker.Unlock();
451 
452 	ssize_t result = peerFifo->Write(vecs, vecCount, ancillaryData, timeout);
453 
454 	// Notify select()ing readers, if we successfully wrote anything.
455 	size_t readable = peerFifo->Readable();
456 	bool notifyRead = (error == B_OK && readable > 0
457 		&& !peerFifo->IsReadShutdown());
458 
459 	// Notify select()ing writers, if we failed to write anything and there's
460 	// still room to write.
461 	size_t writable = peerFifo->Writable();
462 	bool notifyWrite = (error != B_OK && writable > 0
463 		&& !peerFifo->IsWriteShutdown());
464 
465 	// re-lock our endpoint (unlock FIFO to respect locking order)
466 	fifoLocker.Unlock();
467 	locker.Lock();
468 
469 	bool peerLocked = (fPeerEndpoint == peerEndpoint
470 		&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
471 
472 	// send notifications
473 	if (peerLocked && notifyRead)
474 		gSocketModule->notify(peerEndpoint->socket, B_SELECT_READ, readable);
475 	if (notifyWrite)
476 		gSocketModule->notify(socket, B_SELECT_WRITE, writable);
477 
478 	switch (result) {
479 		case UNIX_FIFO_SHUTDOWN:
480 			if (fPeerEndpoint == peerEndpoint
481 					&& fState == UNIX_ENDPOINT_CONNECTED) {
482 				// Orderly write shutdown on our side.
483 				// Note: Linux and Solaris also send a SIGPIPE, but according
484 				// the send() specification that shouldn't be done.
485 				result = EPIPE;
486 			} else {
487 				// The FD has been closed.
488 				result = EBADF;
489 			}
490 			break;
491 		case EPIPE:
492 			// The peer closed connection or shutdown its read side. Reward
493 			// the caller with a SIGPIPE.
494 			if (gStackModule->is_syscall())
495 				send_signal(find_thread(NULL), SIGPIPE);
496 			break;
497 		case B_TIMED_OUT:
498 			// Translate non-blocking timeouts to the correct error code.
499 			if (timeout == 0)
500 				result = B_WOULD_BLOCK;
501 			break;
502 	}
503 
504 	RETURN_ERROR(result);
505 }
506 
507 
508 ssize_t
509 UnixEndpoint::Receive(const iovec *vecs, size_t vecCount,
510 	ancillary_data_container **_ancillaryData, struct sockaddr *_address,
511 	socklen_t *_addressLength)
512 {
513 	TRACE("[%ld] %p->UnixEndpoint::Receive(%p, %ld)\n", find_thread(NULL),
514 		this, vecs, vecCount);
515 
516 	bigtime_t timeout = absolute_timeout(socket->receive.timeout);
517 	if (gStackModule->is_restarted_syscall())
518 		timeout = gStackModule->restore_syscall_restart_timeout();
519 	else
520 		gStackModule->store_syscall_restart_timeout(timeout);
521 
522 	UnixEndpointLocker locker(this);
523 
524 	// We can read as long as we have a FIFO. I.e. we are still connected, or
525 	// disconnected and not yet reconnected/listening/closed.
526 	if (fReceiveFifo == NULL)
527 		RETURN_ERROR(ENOTCONN);
528 
529 	UnixEndpoint* peerEndpoint = fPeerEndpoint;
530 	Reference<UnixEndpoint> peerReference(peerEndpoint);
531 
532 	// Copy the peer address upfront. This way, if we read something, we don't
533 	// get into a potential race with Close().
534 	if (_address != NULL) {
535 		socklen_t addrLen = min_c(*_addressLength, socket->peer.ss_len);
536 		memcpy(_address, &socket->peer, addrLen);
537 		*_addressLength = addrLen;
538 	}
539 
540 	// lock our FIFO
541 	UnixFifo* fifo = fReceiveFifo;
542 	Reference<UnixFifo> _(fifo);
543 	UnixFifoLocker fifoLocker(fifo);
544 
545 	// unlock endpoint
546 	locker.Unlock();
547 
548 	ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, timeout);
549 
550 	// Notify select()ing writers, if we successfully read anything.
551 	size_t writable = fifo->Writable();
552 	bool notifyWrite = (result >= 0 && writable > 0
553 		&& !fifo->IsWriteShutdown());
554 
555 	// Notify select()ing readers, if we failed to read anything and there's
556 	// still something left to read.
557 	size_t readable = fifo->Readable();
558 	bool notifyRead = (result < 0 && readable > 0
559 		&& !fifo->IsReadShutdown());
560 
561 	// re-lock our endpoint (unlock FIFO to respect locking order)
562 	fifoLocker.Unlock();
563 	locker.Lock();
564 
565 	UnixEndpointLocker peerLocker;
566 	bool peerLocked = (peerEndpoint != NULL && fPeerEndpoint == peerEndpoint
567 		&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
568 
569 	// send notifications
570 	if (notifyRead)
571 		gSocketModule->notify(socket, B_SELECT_READ, readable);
572 	if (peerLocked && notifyWrite)
573 		gSocketModule->notify(peerEndpoint->socket, B_SELECT_WRITE, writable);
574 
575 	switch (result) {
576 		case UNIX_FIFO_SHUTDOWN:
577 			// Either our socket was closed or read shutdown.
578 			if (fState == UNIX_ENDPOINT_CLOSED) {
579 				// The FD has been closed.
580 				result = EBADF;
581 			} else {
582 				// if (fReceiveFifo == fifo) {
583 				// 		Orderly shutdown or the peer closed the connection.
584 				// } else {
585 				//		Weird case: Peer closed connection and we are already
586 				// 		reconnected (or listening).
587 				// }
588 				result = 0;
589 			}
590 			break;
591 		case B_TIMED_OUT:
592 			// translate non-blocking timeouts to the correct error code
593 			if (timeout == 0)
594 				result = B_WOULD_BLOCK;
595 			break;
596 	}
597 
598 	RETURN_ERROR(result);
599 }
600 
601 
602 ssize_t
603 UnixEndpoint::Sendable()
604 {
605 	TRACE("[%ld] %p->UnixEndpoint::Sendable()\n", find_thread(NULL), this);
606 
607 	UnixEndpointLocker locker(this);
608 	UnixEndpointLocker peerLocker;
609 
610 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
611 	if (error != B_OK)
612 		RETURN_ERROR(error);
613 
614 	// lock the peer's FIFO
615 	UnixFifo* peerFifo = fPeerEndpoint->fReceiveFifo;
616 	UnixFifoLocker fifoLocker(peerFifo);
617 
618 	RETURN_ERROR(peerFifo->Writable());
619 }
620 
621 
622 ssize_t
623 UnixEndpoint::Receivable()
624 {
625 	TRACE("[%ld] %p->UnixEndpoint::Receivable()\n", find_thread(NULL), this);
626 
627 	UnixEndpointLocker locker(this);
628 
629 	if (fState == UNIX_ENDPOINT_LISTENING)
630 		return gSocketModule->count_connected(socket);
631 
632 	if (fState != UNIX_ENDPOINT_CONNECTED)
633 		RETURN_ERROR(ENOTCONN);
634 
635 	UnixFifoLocker fifoLocker(fReceiveFifo);
636 	RETURN_ERROR(fReceiveFifo->Readable());
637 }
638 
639 
640 status_t
641 UnixEndpoint::SetReceiveBufferSize(size_t size)
642 {
643 	TRACE("[%ld] %p->UnixEndpoint::SetReceiveBufferSize(%lu)\n",
644 		find_thread(NULL), this, size);
645 
646 	UnixEndpointLocker locker(this);
647 
648 	if (fReceiveFifo == NULL)
649 		return B_BAD_VALUE;
650 
651 	UnixFifoLocker fifoLocker(fReceiveFifo);
652 	return fReceiveFifo->SetBufferCapacity(size);
653 }
654 
655 
656 status_t
657 UnixEndpoint::GetPeerCredentials(ucred* credentials)
658 {
659 	UnixEndpointLocker locker(this);
660 	UnixEndpointLocker peerLocker;
661 
662 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
663 	if (error != B_OK)
664 		RETURN_ERROR(error);
665 
666 	*credentials = fPeerEndpoint->fCredentials;
667 
668 	return B_OK;
669 }
670 
671 
672 status_t
673 UnixEndpoint::Shutdown(int direction)
674 {
675 	TRACE("[%ld] %p->UnixEndpoint::Shutdown(%d)\n",
676 		find_thread(NULL), this, direction);
677 
678 	uint32 shutdown;
679 	uint32 peerShutdown;
680 
681 	// translate the direction into shutdown flags for our and the peer fifo
682 	switch (direction) {
683 		case SHUT_RD:
684 			shutdown = UNIX_FIFO_SHUTDOWN_READ;
685 			peerShutdown = 0;
686 			break;
687 		case SHUT_WR:
688 			shutdown = 0;
689 			peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
690 			break;
691 		case SHUT_RDWR:
692 			shutdown = UNIX_FIFO_SHUTDOWN_READ;
693 			peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
694 			break;
695 		default:
696 			RETURN_ERROR(B_BAD_VALUE);
697 	}
698 
699 	// lock endpoints
700 	UnixEndpointLocker locker(this);
701 	UnixEndpointLocker peerLocker;
702 
703 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
704 	if (error != B_OK)
705 		RETURN_ERROR(error);
706 
707 	// shutdown our FIFO
708 	fReceiveFifo->Lock();
709 	fReceiveFifo->Shutdown(shutdown);
710 	fReceiveFifo->Unlock();
711 
712 	// shutdown peer FIFO
713 	fPeerEndpoint->fReceiveFifo->Lock();
714 	fPeerEndpoint->fReceiveFifo->Shutdown(peerShutdown);
715 	fPeerEndpoint->fReceiveFifo->Unlock();
716 
717 	// send select notifications
718 	if (direction == SHUT_RD || direction == SHUT_RDWR) {
719 		gSocketModule->notify(socket, B_SELECT_READ, EPIPE);
720 		gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_WRITE, EPIPE);
721 	}
722 	if (direction == SHUT_WR || direction == SHUT_RDWR) {
723 		gSocketModule->notify(socket, B_SELECT_WRITE, EPIPE);
724 		gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_READ, EPIPE);
725 	}
726 
727 	RETURN_ERROR(B_OK);
728 }
729 
730 
731 void
732 UnixEndpoint::_Spawn(UnixEndpoint* connectingEndpoint,
733 	UnixEndpoint* listeningEndpoint, UnixFifo* fifo)
734 {
735 	ProtocolSocket::Open();
736 
737 	fIsChild = true;
738 	fPeerEndpoint = connectingEndpoint;
739 	fPeerEndpoint->AddReference();
740 
741 	fReceiveFifo = fifo;
742 
743 	PeerAddress().SetTo(&connectingEndpoint->socket->address);
744 
745 	fCredentials = listeningEndpoint->fCredentials;
746 
747 	fState = UNIX_ENDPOINT_CONNECTED;
748 }
749 
750 
751 void
752 UnixEndpoint::_Disconnect()
753 {
754 	// Both endpoints must be locked.
755 
756 	// Write shutdown the receive FIFO.
757 	fReceiveFifo->Lock();
758 	fReceiveFifo->Shutdown(UNIX_FIFO_SHUTDOWN_WRITE);
759 	fReceiveFifo->Unlock();
760 
761 	// select() notification.
762 	gSocketModule->notify(socket, B_SELECT_READ, ECONNRESET);
763 	gSocketModule->notify(socket, B_SELECT_WRITE, ECONNRESET);
764 
765 	// Unset the peer endpoint.
766 	fPeerEndpoint->RemoveReference();
767 	fPeerEndpoint = NULL;
768 
769 	// We're officially disconnected.
770 // TODO: Deal with non accept()ed connections correctly!
771 	fIsChild = false;
772 	fState = UNIX_ENDPOINT_NOT_CONNECTED;
773 }
774 
775 
776 status_t
777 UnixEndpoint::_LockConnectedEndpoints(UnixEndpointLocker& locker,
778 	UnixEndpointLocker& peerLocker)
779 {
780 	if (fState != UNIX_ENDPOINT_CONNECTED)
781 		RETURN_ERROR(ENOTCONN);
782 
783 	// We need to lock the peer, too. Get a reference -- we might need to
784 	// unlock ourselves to get the locking order right.
785 	Reference<UnixEndpoint> peerReference(fPeerEndpoint);
786 	UnixEndpoint* peerEndpoint = fPeerEndpoint;
787 
788 	if (fIsChild) {
789 		// We're the child, but locking order is the other way around.
790 		locker.Unlock();
791 		peerLocker.SetTo(peerEndpoint, false);
792 
793 		locker.Lock();
794 
795 		// recheck our state, also whether the peer is still the same
796 		if (fState != UNIX_ENDPOINT_CONNECTED || peerEndpoint != fPeerEndpoint)
797 			RETURN_ERROR(ENOTCONN);
798 	} else
799 		peerLocker.SetTo(peerEndpoint, false);
800 
801 	RETURN_ERROR(B_OK);
802 }
803 
804 
805 status_t
806 UnixEndpoint::_Bind(struct vnode* vnode)
807 {
808 	struct stat st;
809 	status_t error = vfs_stat_vnode(vnode, &st);
810 	if (error != B_OK)
811 		RETURN_ERROR(error);
812 
813 	fAddress.SetTo(st.st_dev, st.st_ino, vnode);
814 	RETURN_ERROR(B_OK);
815 }
816 
817 
818 status_t
819 UnixEndpoint::_Bind(int32 internalID)
820 {
821 	fAddress.SetTo(internalID);
822 	RETURN_ERROR(B_OK);
823 }
824 
825 
826 status_t
827 UnixEndpoint::_Unbind()
828 {
829 	if (fState == UNIX_ENDPOINT_CONNECTED || fState == UNIX_ENDPOINT_LISTENING)
830 		RETURN_ERROR(B_BAD_VALUE);
831 
832 	if (IsBound()) {
833 		UnixAddressManagerLocker addressLocker(gAddressManager);
834 		gAddressManager.Remove(this);
835 		if (struct vnode* vnode = fAddress.Vnode())
836 			vfs_put_vnode(vnode);
837 
838 		fAddress.Unset();
839 	}
840 
841 	RETURN_ERROR(B_OK);
842 }
843 
844 
845 void
846 UnixEndpoint::_UnsetReceiveFifo()
847 {
848 	if (fReceiveFifo) {
849 		fReceiveFifo->RemoveReference();
850 		fReceiveFifo = NULL;
851 	}
852 }
853 
854 
855 void
856 UnixEndpoint::_StopListening()
857 {
858 	if (fState == UNIX_ENDPOINT_LISTENING) {
859 		delete_sem(fAcceptSemaphore);
860 		fAcceptSemaphore = -1;
861 		fState = UNIX_ENDPOINT_NOT_CONNECTED;
862 	}
863 }
864