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