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