xref: /haiku/src/add-ons/kernel/network/protocols/unix/UnixEndpoint.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
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, "%05lx", 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 		RETURN_ERROR(EINVAL);
244 
245 	gSocketModule->set_max_backlog(socket, backlog);
246 
247 	fAcceptSemaphore = create_sem(0, "unix accept");
248 	if (fAcceptSemaphore < 0)
249 		RETURN_ERROR(ENOBUFS);
250 
251 	_UnsetReceiveFifo();
252 
253 	fCredentials.pid = getpid();
254 	fCredentials.uid = geteuid();
255 	fCredentials.gid = getegid();
256 
257 	fState = UNIX_ENDPOINT_LISTENING;
258 
259 	RETURN_ERROR(B_OK);
260 }
261 
262 
263 status_t
264 UnixEndpoint::Connect(const struct sockaddr *_address)
265 {
266 	if (_address->sa_family != AF_UNIX)
267 		RETURN_ERROR(EAFNOSUPPORT);
268 
269 	TRACE("[%ld] %p->UnixEndpoint::Connect(\"%s\")\n", find_thread(NULL), this,
270 		ConstSocketAddress(&gAddressModule, _address).AsString().Data());
271 
272 	const sockaddr_un* address = (const sockaddr_un*)_address;
273 
274 	UnixEndpointLocker endpointLocker(this);
275 
276 	if (fState == UNIX_ENDPOINT_CONNECTED)
277 		RETURN_ERROR(EISCONN);
278 
279 	if (fState != UNIX_ENDPOINT_NOT_CONNECTED)
280 		RETURN_ERROR(B_BAD_VALUE);
281 // TODO: If listening, we could set the backlog to 0 and connect.
282 
283 	// check the address first
284 	UnixAddress unixAddress;
285 
286 	if (address->sun_path[0] == '\0') {
287 		// internal address space (or empty address)
288 		int32 internalID;
289 		if (UnixAddress::IsEmptyAddress(*address))
290 			RETURN_ERROR(B_BAD_VALUE);
291 
292 		internalID = UnixAddress::InternalID(*address);
293 		if (internalID < 0)
294 			RETURN_ERROR(internalID);
295 
296 		unixAddress.SetTo(internalID);
297 	} else {
298 		// FS address space
299 		size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path));
300 		if (pathLen == 0 || pathLen == sizeof(address->sun_path))
301 			RETURN_ERROR(B_BAD_VALUE);
302 
303 		struct stat st;
304 		status_t error = vfs_read_stat(-1, address->sun_path, true, &st,
305 			!gStackModule->is_syscall());
306 		if (error != B_OK)
307 			RETURN_ERROR(error);
308 
309 		if (!S_ISSOCK(st.st_mode))
310 			RETURN_ERROR(B_BAD_VALUE);
311 
312 		unixAddress.SetTo(st.st_dev, st.st_ino, NULL);
313 	}
314 
315 	// get the peer endpoint
316 	UnixAddressManagerLocker addressLocker(gAddressManager);
317 	UnixEndpoint* listeningEndpoint = gAddressManager.Lookup(unixAddress);
318 	if (listeningEndpoint == NULL)
319 		RETURN_ERROR(ECONNREFUSED);
320 	BReference<UnixEndpoint> peerReference(listeningEndpoint);
321 	addressLocker.Unlock();
322 
323 	UnixEndpointLocker peerLocker(listeningEndpoint);
324 
325 	if (!listeningEndpoint->IsBound()
326 		|| listeningEndpoint->fState != UNIX_ENDPOINT_LISTENING
327 		|| listeningEndpoint->fAddress != unixAddress) {
328 		RETURN_ERROR(ECONNREFUSED);
329 	}
330 
331 	// Allocate FIFOs for us and the socket we're going to spawn. We do that
332 	// now, so that the mess we need to cleanup, if allocating them fails, is
333 	// harmless.
334 	UnixFifo* fifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT);
335 	UnixFifo* peerFifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT);
336 	ObjectDeleter<UnixFifo> fifoDeleter(fifo);
337 	ObjectDeleter<UnixFifo> peerFifoDeleter(peerFifo);
338 
339 	status_t error;
340 	if ((error = fifo->Init()) != B_OK || (error = peerFifo->Init()) != B_OK)
341 		return error;
342 
343 	// spawn new endpoint for accept()
344 	net_socket* newSocket;
345 	error = gSocketModule->spawn_pending_socket(listeningEndpoint->socket,
346 		&newSocket);
347 	if (error != B_OK)
348 		RETURN_ERROR(error);
349 
350 	// init connected peer endpoint
351 	UnixEndpoint* connectedEndpoint = (UnixEndpoint*)newSocket->first_protocol;
352 
353 	UnixEndpointLocker connectedLocker(connectedEndpoint);
354 
355 	connectedEndpoint->_Spawn(this, listeningEndpoint, peerFifo);
356 
357 	// update our attributes
358 	_UnsetReceiveFifo();
359 
360 	fPeerEndpoint = connectedEndpoint;
361 	PeerAddress().SetTo(&connectedEndpoint->socket->address);
362 	fPeerEndpoint->AcquireReference();
363 	fReceiveFifo = fifo;
364 
365 	fCredentials.pid = getpid();
366 	fCredentials.uid = geteuid();
367 	fCredentials.gid = getegid();
368 
369 	fifoDeleter.Detach();
370 	peerFifoDeleter.Detach();
371 
372 	fState = UNIX_ENDPOINT_CONNECTED;
373 
374 	gSocketModule->set_connected(newSocket);
375 
376 	release_sem(listeningEndpoint->fAcceptSemaphore);
377 
378 	connectedLocker.Unlock();
379 	peerLocker.Unlock();
380 	endpointLocker.Unlock();
381 
382 	RETURN_ERROR(B_OK);
383 }
384 
385 
386 status_t
387 UnixEndpoint::Accept(net_socket **_acceptedSocket)
388 {
389 	TRACE("[%ld] %p->UnixEndpoint::Accept()\n", find_thread(NULL), this);
390 
391 	bigtime_t timeout = absolute_timeout(socket->receive.timeout);
392 	if (gStackModule->is_restarted_syscall())
393 		timeout = gStackModule->restore_syscall_restart_timeout();
394 	else
395 		gStackModule->store_syscall_restart_timeout(timeout);
396 
397 	UnixEndpointLocker locker(this);
398 
399 	status_t error;
400 	do {
401 		locker.Unlock();
402 
403 		error = acquire_sem_etc(fAcceptSemaphore, 1,
404 			B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, timeout);
405 		if (error < B_OK)
406 			RETURN_ERROR(error);
407 
408 		locker.Lock();
409 		error = gSocketModule->dequeue_connected(socket, _acceptedSocket);
410 	} while (error != B_OK);
411 
412 	if (error == B_TIMED_OUT && timeout == 0) {
413 		// translate non-blocking timeouts to the correct error code
414 		error = B_WOULD_BLOCK;
415 	}
416 
417 	RETURN_ERROR(error);
418 }
419 
420 
421 ssize_t
422 UnixEndpoint::Send(const iovec *vecs, size_t vecCount,
423 	ancillary_data_container *ancillaryData)
424 {
425 	TRACE("[%ld] %p->UnixEndpoint::Send(%p, %ld, %p)\n", find_thread(NULL),
426 		this, vecs, vecCount, ancillaryData);
427 
428 	bigtime_t timeout = absolute_timeout(socket->send.timeout);
429 	if (gStackModule->is_restarted_syscall())
430 		timeout = gStackModule->restore_syscall_restart_timeout();
431 	else
432 		gStackModule->store_syscall_restart_timeout(timeout);
433 
434 	UnixEndpointLocker locker(this);
435 
436 	BReference<UnixEndpoint> peerReference;
437 	UnixEndpointLocker peerLocker;
438 
439 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
440 	if (error != B_OK)
441 		RETURN_ERROR(error);
442 
443 	UnixEndpoint* peerEndpoint = fPeerEndpoint;
444 	peerReference.SetTo(peerEndpoint);
445 
446 	// lock the peer's FIFO
447 	UnixFifo* peerFifo = peerEndpoint->fReceiveFifo;
448 	BReference<UnixFifo> _(peerFifo);
449 	UnixFifoLocker fifoLocker(peerFifo);
450 
451 	// unlock endpoints
452 	locker.Unlock();
453 	peerLocker.Unlock();
454 
455 	ssize_t result = peerFifo->Write(vecs, vecCount, ancillaryData, timeout);
456 
457 	// Notify select()ing readers, if we successfully wrote anything.
458 	size_t readable = peerFifo->Readable();
459 	bool notifyRead = (error == B_OK && readable > 0
460 		&& !peerFifo->IsReadShutdown());
461 
462 	// Notify select()ing writers, if we failed to write anything and there's
463 	// still room to write.
464 	size_t writable = peerFifo->Writable();
465 	bool notifyWrite = (error != B_OK && writable > 0
466 		&& !peerFifo->IsWriteShutdown());
467 
468 	// re-lock our endpoint (unlock FIFO to respect locking order)
469 	fifoLocker.Unlock();
470 	locker.Lock();
471 
472 	bool peerLocked = (fPeerEndpoint == peerEndpoint
473 		&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
474 
475 	// send notifications
476 	if (peerLocked && notifyRead)
477 		gSocketModule->notify(peerEndpoint->socket, B_SELECT_READ, readable);
478 	if (notifyWrite)
479 		gSocketModule->notify(socket, B_SELECT_WRITE, writable);
480 
481 	switch (result) {
482 		case UNIX_FIFO_SHUTDOWN:
483 			if (fPeerEndpoint == peerEndpoint
484 					&& fState == UNIX_ENDPOINT_CONNECTED) {
485 				// Orderly write shutdown on our side.
486 				// Note: Linux and Solaris also send a SIGPIPE, but according
487 				// the send() specification that shouldn't be done.
488 				result = EPIPE;
489 			} else {
490 				// The FD has been closed.
491 				result = EBADF;
492 			}
493 			break;
494 		case EPIPE:
495 			// The peer closed connection or shutdown its read side. Reward
496 			// the caller with a SIGPIPE.
497 			if (gStackModule->is_syscall())
498 				send_signal(find_thread(NULL), SIGPIPE);
499 			break;
500 		case B_TIMED_OUT:
501 			// Translate non-blocking timeouts to the correct error code.
502 			if (timeout == 0)
503 				result = B_WOULD_BLOCK;
504 			break;
505 	}
506 
507 	RETURN_ERROR(result);
508 }
509 
510 
511 ssize_t
512 UnixEndpoint::Receive(const iovec *vecs, size_t vecCount,
513 	ancillary_data_container **_ancillaryData, struct sockaddr *_address,
514 	socklen_t *_addressLength)
515 {
516 	TRACE("[%ld] %p->UnixEndpoint::Receive(%p, %ld)\n", find_thread(NULL),
517 		this, vecs, vecCount);
518 
519 	bigtime_t timeout = absolute_timeout(socket->receive.timeout);
520 	if (gStackModule->is_restarted_syscall())
521 		timeout = gStackModule->restore_syscall_restart_timeout();
522 	else
523 		gStackModule->store_syscall_restart_timeout(timeout);
524 
525 	UnixEndpointLocker locker(this);
526 
527 	// We can read as long as we have a FIFO. I.e. we are still connected, or
528 	// disconnected and not yet reconnected/listening/closed.
529 	if (fReceiveFifo == NULL)
530 		RETURN_ERROR(ENOTCONN);
531 
532 	UnixEndpoint* peerEndpoint = fPeerEndpoint;
533 	BReference<UnixEndpoint> peerReference(peerEndpoint);
534 
535 	// Copy the peer address upfront. This way, if we read something, we don't
536 	// get into a potential race with Close().
537 	if (_address != NULL) {
538 		socklen_t addrLen = min_c(*_addressLength, socket->peer.ss_len);
539 		memcpy(_address, &socket->peer, addrLen);
540 		*_addressLength = addrLen;
541 	}
542 
543 	// lock our FIFO
544 	UnixFifo* fifo = fReceiveFifo;
545 	BReference<UnixFifo> _(fifo);
546 	UnixFifoLocker fifoLocker(fifo);
547 
548 	// unlock endpoint
549 	locker.Unlock();
550 
551 	ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, timeout);
552 
553 	// Notify select()ing writers, if we successfully read anything.
554 	size_t writable = fifo->Writable();
555 	bool notifyWrite = (result >= 0 && writable > 0
556 		&& !fifo->IsWriteShutdown());
557 
558 	// Notify select()ing readers, if we failed to read anything and there's
559 	// still something left to read.
560 	size_t readable = fifo->Readable();
561 	bool notifyRead = (result < 0 && readable > 0
562 		&& !fifo->IsReadShutdown());
563 
564 	// re-lock our endpoint (unlock FIFO to respect locking order)
565 	fifoLocker.Unlock();
566 	locker.Lock();
567 
568 	UnixEndpointLocker peerLocker;
569 	bool peerLocked = (peerEndpoint != NULL && fPeerEndpoint == peerEndpoint
570 		&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
571 
572 	// send notifications
573 	if (notifyRead)
574 		gSocketModule->notify(socket, B_SELECT_READ, readable);
575 	if (peerLocked && notifyWrite)
576 		gSocketModule->notify(peerEndpoint->socket, B_SELECT_WRITE, writable);
577 
578 	switch (result) {
579 		case UNIX_FIFO_SHUTDOWN:
580 			// Either our socket was closed or read shutdown.
581 			if (fState == UNIX_ENDPOINT_CLOSED) {
582 				// The FD has been closed.
583 				result = EBADF;
584 			} else {
585 				// if (fReceiveFifo == fifo) {
586 				// 		Orderly shutdown or the peer closed the connection.
587 				// } else {
588 				//		Weird case: Peer closed connection and we are already
589 				// 		reconnected (or listening).
590 				// }
591 				result = 0;
592 			}
593 			break;
594 		case B_TIMED_OUT:
595 			// translate non-blocking timeouts to the correct error code
596 			if (timeout == 0)
597 				result = B_WOULD_BLOCK;
598 			break;
599 	}
600 
601 	RETURN_ERROR(result);
602 }
603 
604 
605 ssize_t
606 UnixEndpoint::Sendable()
607 {
608 	TRACE("[%ld] %p->UnixEndpoint::Sendable()\n", find_thread(NULL), this);
609 
610 	UnixEndpointLocker locker(this);
611 	UnixEndpointLocker peerLocker;
612 
613 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
614 	if (error != B_OK)
615 		RETURN_ERROR(error);
616 
617 	// lock the peer's FIFO
618 	UnixFifo* peerFifo = fPeerEndpoint->fReceiveFifo;
619 	UnixFifoLocker fifoLocker(peerFifo);
620 
621 	RETURN_ERROR(peerFifo->Writable());
622 }
623 
624 
625 ssize_t
626 UnixEndpoint::Receivable()
627 {
628 	TRACE("[%ld] %p->UnixEndpoint::Receivable()\n", find_thread(NULL), this);
629 
630 	UnixEndpointLocker locker(this);
631 
632 	if (fState == UNIX_ENDPOINT_LISTENING)
633 		return gSocketModule->count_connected(socket);
634 
635 	if (fState != UNIX_ENDPOINT_CONNECTED)
636 		RETURN_ERROR(ENOTCONN);
637 
638 	UnixFifoLocker fifoLocker(fReceiveFifo);
639 	ssize_t readable = fReceiveFifo->Readable();
640 	if (readable == 0 && fReceiveFifo->IsWriteShutdown())
641 		RETURN_ERROR(ENOTCONN);
642 	RETURN_ERROR(readable);
643 }
644 
645 
646 status_t
647 UnixEndpoint::SetReceiveBufferSize(size_t size)
648 {
649 	TRACE("[%ld] %p->UnixEndpoint::SetReceiveBufferSize(%lu)\n",
650 		find_thread(NULL), this, size);
651 
652 	UnixEndpointLocker locker(this);
653 
654 	if (fReceiveFifo == NULL)
655 		return B_BAD_VALUE;
656 
657 	UnixFifoLocker fifoLocker(fReceiveFifo);
658 	return fReceiveFifo->SetBufferCapacity(size);
659 }
660 
661 
662 status_t
663 UnixEndpoint::GetPeerCredentials(ucred* credentials)
664 {
665 	UnixEndpointLocker locker(this);
666 	UnixEndpointLocker peerLocker;
667 
668 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
669 	if (error != B_OK)
670 		RETURN_ERROR(error);
671 
672 	*credentials = fPeerEndpoint->fCredentials;
673 
674 	return B_OK;
675 }
676 
677 
678 status_t
679 UnixEndpoint::Shutdown(int direction)
680 {
681 	TRACE("[%ld] %p->UnixEndpoint::Shutdown(%d)\n",
682 		find_thread(NULL), this, direction);
683 
684 	uint32 shutdown;
685 	uint32 peerShutdown;
686 
687 	// translate the direction into shutdown flags for our and the peer fifo
688 	switch (direction) {
689 		case SHUT_RD:
690 			shutdown = UNIX_FIFO_SHUTDOWN_READ;
691 			peerShutdown = 0;
692 			break;
693 		case SHUT_WR:
694 			shutdown = 0;
695 			peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
696 			break;
697 		case SHUT_RDWR:
698 			shutdown = UNIX_FIFO_SHUTDOWN_READ;
699 			peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
700 			break;
701 		default:
702 			RETURN_ERROR(B_BAD_VALUE);
703 	}
704 
705 	// lock endpoints
706 	UnixEndpointLocker locker(this);
707 	UnixEndpointLocker peerLocker;
708 
709 	status_t error = _LockConnectedEndpoints(locker, peerLocker);
710 	if (error != B_OK)
711 		RETURN_ERROR(error);
712 
713 	// shutdown our FIFO
714 	fReceiveFifo->Lock();
715 	fReceiveFifo->Shutdown(shutdown);
716 	fReceiveFifo->Unlock();
717 
718 	// shutdown peer FIFO
719 	fPeerEndpoint->fReceiveFifo->Lock();
720 	fPeerEndpoint->fReceiveFifo->Shutdown(peerShutdown);
721 	fPeerEndpoint->fReceiveFifo->Unlock();
722 
723 	// send select notifications
724 	if (direction == SHUT_RD || direction == SHUT_RDWR) {
725 		gSocketModule->notify(socket, B_SELECT_READ, EPIPE);
726 		gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_WRITE, EPIPE);
727 	}
728 	if (direction == SHUT_WR || direction == SHUT_RDWR) {
729 		gSocketModule->notify(socket, B_SELECT_WRITE, EPIPE);
730 		gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_READ, EPIPE);
731 	}
732 
733 	RETURN_ERROR(B_OK);
734 }
735 
736 
737 void
738 UnixEndpoint::_Spawn(UnixEndpoint* connectingEndpoint,
739 	UnixEndpoint* listeningEndpoint, UnixFifo* fifo)
740 {
741 	ProtocolSocket::Open();
742 
743 	fIsChild = true;
744 	fPeerEndpoint = connectingEndpoint;
745 	fPeerEndpoint->AcquireReference();
746 
747 	fReceiveFifo = fifo;
748 
749 	PeerAddress().SetTo(&connectingEndpoint->socket->address);
750 
751 	fCredentials = listeningEndpoint->fCredentials;
752 
753 	fState = UNIX_ENDPOINT_CONNECTED;
754 }
755 
756 
757 void
758 UnixEndpoint::_Disconnect()
759 {
760 	// Both endpoints must be locked.
761 
762 	// Write shutdown the receive FIFO.
763 	fReceiveFifo->Lock();
764 	fReceiveFifo->Shutdown(UNIX_FIFO_SHUTDOWN_WRITE);
765 	fReceiveFifo->Unlock();
766 
767 	// select() notification.
768 	gSocketModule->notify(socket, B_SELECT_READ, ECONNRESET);
769 	gSocketModule->notify(socket, B_SELECT_WRITE, ECONNRESET);
770 
771 	// Unset the peer endpoint.
772 	fPeerEndpoint->ReleaseReference();
773 	fPeerEndpoint = NULL;
774 
775 	// We're officially disconnected.
776 // TODO: Deal with non accept()ed connections correctly!
777 	fIsChild = false;
778 	fState = UNIX_ENDPOINT_NOT_CONNECTED;
779 }
780 
781 
782 status_t
783 UnixEndpoint::_LockConnectedEndpoints(UnixEndpointLocker& locker,
784 	UnixEndpointLocker& peerLocker)
785 {
786 	if (fState != UNIX_ENDPOINT_CONNECTED)
787 		RETURN_ERROR(ENOTCONN);
788 
789 	// We need to lock the peer, too. Get a reference -- we might need to
790 	// unlock ourselves to get the locking order right.
791 	BReference<UnixEndpoint> peerReference(fPeerEndpoint);
792 	UnixEndpoint* peerEndpoint = fPeerEndpoint;
793 
794 	if (fIsChild) {
795 		// We're the child, but locking order is the other way around.
796 		locker.Unlock();
797 		peerLocker.SetTo(peerEndpoint, false);
798 
799 		locker.Lock();
800 
801 		// recheck our state, also whether the peer is still the same
802 		if (fState != UNIX_ENDPOINT_CONNECTED || peerEndpoint != fPeerEndpoint)
803 			RETURN_ERROR(ENOTCONN);
804 	} else
805 		peerLocker.SetTo(peerEndpoint, false);
806 
807 	RETURN_ERROR(B_OK);
808 }
809 
810 
811 status_t
812 UnixEndpoint::_Bind(struct vnode* vnode)
813 {
814 	struct stat st;
815 	status_t error = vfs_stat_vnode(vnode, &st);
816 	if (error != B_OK)
817 		RETURN_ERROR(error);
818 
819 	fAddress.SetTo(st.st_dev, st.st_ino, vnode);
820 	RETURN_ERROR(B_OK);
821 }
822 
823 
824 status_t
825 UnixEndpoint::_Bind(int32 internalID)
826 {
827 	fAddress.SetTo(internalID);
828 	RETURN_ERROR(B_OK);
829 }
830 
831 
832 status_t
833 UnixEndpoint::_Unbind()
834 {
835 	if (fState == UNIX_ENDPOINT_CONNECTED || fState == UNIX_ENDPOINT_LISTENING)
836 		RETURN_ERROR(B_BAD_VALUE);
837 
838 	if (IsBound()) {
839 		UnixAddressManagerLocker addressLocker(gAddressManager);
840 		gAddressManager.Remove(this);
841 		if (struct vnode* vnode = fAddress.Vnode())
842 			vfs_put_vnode(vnode);
843 
844 		fAddress.Unset();
845 	}
846 
847 	RETURN_ERROR(B_OK);
848 }
849 
850 
851 void
852 UnixEndpoint::_UnsetReceiveFifo()
853 {
854 	if (fReceiveFifo) {
855 		fReceiveFifo->ReleaseReference();
856 		fReceiveFifo = NULL;
857 	}
858 }
859 
860 
861 void
862 UnixEndpoint::_StopListening()
863 {
864 	if (fState == UNIX_ENDPOINT_LISTENING) {
865 		delete_sem(fAcceptSemaphore);
866 		fAcceptSemaphore = -1;
867 		fState = UNIX_ENDPOINT_NOT_CONNECTED;
868 	}
869 }
870