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