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