From fcefd86432925144e385c611c55c68c0e2a8fb09 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 6 Aug 2016 15:44:37 +0200 Subject: [PATCH] Implement shutdown(2). --- kernel/descriptor.cpp | 7 ++ kernel/fs/user.cpp | 17 ++++ kernel/include/sortix/kernel/descriptor.h | 1 + kernel/include/sortix/kernel/inode.h | 2 + kernel/include/sortix/kernel/vnode.h | 1 + kernel/inode.cpp | 5 + kernel/io.cpp | 8 +- kernel/net/fs.cpp | 12 ++- kernel/pipe.cpp | 37 +++++++- kernel/vnode.cpp | 5 + libc/include/fsmarshall-msg.h | 11 ++- regress/Makefile | 1 + regress/test-unix-socket-shutdown.c | 107 ++++++++++++++++++++++ 13 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 regress/test-unix-socket-shutdown.c diff --git a/kernel/descriptor.cpp b/kernel/descriptor.cpp index fe7c9268..ebcbc2da 100644 --- a/kernel/descriptor.cpp +++ b/kernel/descriptor.cpp @@ -1046,4 +1046,11 @@ int Descriptor::tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) return vnode->tcsetattr(ctx, actions, tio); } +int Descriptor::shutdown(ioctx_t* ctx, int how) +{ + if ( how & ~(SHUT_RD | SHUT_WR) ) + return errno = EINVAL, -1; + return vnode->shutdown(ctx, how); +} + } // namespace Sortix diff --git a/kernel/fs/user.cpp b/kernel/fs/user.cpp index 2921d279..d28b9d0b 100644 --- a/kernel/fs/user.cpp +++ b/kernel/fs/user.cpp @@ -266,6 +266,7 @@ public: virtual pid_t tcgetsid(ioctx_t* ctx); virtual int tcsendbreak(ioctx_t* ctx, int duration); virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + virtual int shutdown(ioctx_t* ctx, int how); private: bool SendMessage(Channel* channel, size_t type, void* ptr, size_t size, @@ -1726,6 +1727,22 @@ int Unode::tcsetattr(ioctx_t* ctx, int actions, const struct termios* user_tio) return ret; } +int Unode::shutdown(ioctx_t* ctx, int how) +{ + struct fsm_req_shutdown msg; + Channel* channel = server->Connect(ctx); + if ( !channel ) + return -1; + int ret = -1; + msg.ino = ino; + msg.how = how; + if ( SendMessage(channel, FSM_REQ_SHUTDOWN, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + bool Bootstrap(Ref* out_root, Ref* out_server, const struct stat* rootst) diff --git a/kernel/include/sortix/kernel/descriptor.h b/kernel/include/sortix/kernel/descriptor.h index 7cfa4bf5..91baaaf4 100644 --- a/kernel/include/sortix/kernel/descriptor.h +++ b/kernel/include/sortix/kernel/descriptor.h @@ -120,6 +120,7 @@ public: pid_t tcgetsid(ioctx_t* ctx); int tcsendbreak(ioctx_t* ctx, int duration); int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + int shutdown(ioctx_t* ctx, int how); private: Ref open_elem(ioctx_t* ctx, const char* filename, int flags, diff --git a/kernel/include/sortix/kernel/inode.h b/kernel/include/sortix/kernel/inode.h index 6b53c032..48a900f7 100644 --- a/kernel/include/sortix/kernel/inode.h +++ b/kernel/include/sortix/kernel/inode.h @@ -129,6 +129,7 @@ public: virtual pid_t tcgetsid(ioctx_t* ctx) = 0; virtual int tcsendbreak(ioctx_t* ctx, int duration) = 0; virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) = 0; + virtual int shutdown(ioctx_t* ctx, int how) = 0; }; @@ -233,6 +234,7 @@ public: virtual pid_t tcgetsid(ioctx_t* ctx); virtual int tcsendbreak(ioctx_t* ctx, int duration); virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + virtual int shutdown(ioctx_t* ctx, int how); }; diff --git a/kernel/include/sortix/kernel/vnode.h b/kernel/include/sortix/kernel/vnode.h index d7874e12..996f0305 100644 --- a/kernel/include/sortix/kernel/vnode.h +++ b/kernel/include/sortix/kernel/vnode.h @@ -117,6 +117,7 @@ public: pid_t tcgetsid(ioctx_t* ctx); int tcsendbreak(ioctx_t* ctx, int duration); int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + int shutdown(ioctx_t* ctx, int how); public /*TODO: private*/: Ref inode; diff --git a/kernel/inode.cpp b/kernel/inode.cpp index abc5190c..b078a773 100644 --- a/kernel/inode.cpp +++ b/kernel/inode.cpp @@ -621,4 +621,9 @@ int AbstractInode::tcsetattr(ioctx_t* /*ctx*/, int /*actions*/, const struct ter return errno = ENOTTY, -1; } +int AbstractInode::shutdown(ioctx_t* /*ctx*/, int /*how*/) +{ + return errno = ENOTSOCK, -1; +} + } // namespace Sortix diff --git a/kernel/io.cpp b/kernel/io.cpp index 273b6e2a..bef94f11 100644 --- a/kernel/io.cpp +++ b/kernel/io.cpp @@ -944,9 +944,11 @@ int sys_getsockname(int fd, struct sockaddr* addr, socklen_t* addrsize) int sys_shutdown(int fd, int how) { - (void) fd; - (void) how; - return errno = ENOSYS, -1; + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->shutdown(&ctx, how); } int sys_unmountat(int dirfd, const char* path, int flags) diff --git a/kernel/net/fs.cpp b/kernel/net/fs.cpp index 6e160186..44b22a60 100644 --- a/kernel/net/fs.cpp +++ b/kernel/net/fs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 2016, 2017 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -101,6 +101,7 @@ public: void* option_value, size_t* option_size_ptr); virtual int setsockopt(ioctx_t* ctx, int level, int option_name, const void* option_value, size_t option_size); + virtual int shutdown(ioctx_t* ctx, int how); private: int do_bind(ioctx_t* ctx, const uint8_t* addr, size_t addrsize); @@ -383,6 +384,15 @@ int StreamSocket::setsockopt(ioctx_t* ctx, int level, int option_name, return 0; } +int StreamSocket::shutdown(ioctx_t* /*ctx*/, int how) +{ + if ( how & SHUT_RD ) + incoming.Disconnect(); + if ( how & SHUT_WR ) + outgoing.Disconnect(); + return 0; +} + Manager::Manager(uid_t owner, gid_t group, mode_t mode) { inode_type = INODE_TYPE_UNKNOWN; diff --git a/kernel/pipe.cpp b/kernel/pipe.cpp index 5e4d3f50..2f3dcb8c 100644 --- a/kernel/pipe.cpp +++ b/kernel/pipe.cpp @@ -543,19 +543,21 @@ bool PipeEndpoint::Connect(PipeEndpoint* destination) void PipeEndpoint::Disconnect() { - assert(channel); + if ( !channel ) + return; if ( reading ) channel->CloseReading(); else channel->CloseWriting(); channel = NULL; - reading = false; } ssize_t PipeEndpoint::recv(ioctx_t* ctx, uint8_t* buf, size_t count, int flags) { if ( !reading ) return errno = EBADF, -1; + if ( !channel ) + return 0; ssize_t result = channel->recv(ctx, buf, count, flags); CurrentThread()->yield_to_tid = 0; Scheduler::ScheduleTrueThread(); @@ -566,6 +568,8 @@ ssize_t PipeEndpoint::recvmsg(ioctx_t* ctx, struct msghdr* msg, int flags) { if ( !reading ) return errno = EBADF, -1; + if ( !channel ) + return 0; ssize_t result = channel->recvmsg(ctx, msg, flags); CurrentThread()->yield_to_tid = 0; Scheduler::ScheduleTrueThread(); @@ -577,6 +581,12 @@ ssize_t PipeEndpoint::send(ioctx_t* ctx, const uint8_t* buf, size_t count, { if ( reading ) return errno = EBADF, -1; + if ( !channel ) + { + if ( !(flags & MSG_NOSIGNAL) ) + CurrentThread()->DeliverSignal(SIGPIPE); + return errno = EPIPE, -1; + } ssize_t result = channel->send(ctx, buf, count, flags); CurrentThread()->yield_to_tid = 0; Scheduler::ScheduleTrueThread(); @@ -587,6 +597,12 @@ ssize_t PipeEndpoint::sendmsg(ioctx_t* ctx, const struct msghdr* msg, int flags) { if ( reading ) return errno = EBADF, -1; + if ( !channel ) + { + if ( !(flags & MSG_NOSIGNAL) ) + CurrentThread()->DeliverSignal(SIGPIPE); + return errno = EPIPE, -1; + } ssize_t result = channel->sendmsg(ctx, msg, flags); CurrentThread()->yield_to_tid = 0; Scheduler::ScheduleTrueThread(); @@ -598,6 +614,8 @@ ssize_t PipeEndpoint::readv(ioctx_t* ctx, const struct iovec* iov, int iovcnt) if ( !reading ) return errno = EBADF, -1; ssize_t result = channel->readv(ctx, iov, iovcnt); + if ( !channel ) + return 0; CurrentThread()->yield_to_tid = 0; Scheduler::ScheduleTrueThread(); return result; @@ -607,6 +625,11 @@ ssize_t PipeEndpoint::writev(ioctx_t* ctx, const struct iovec* iov, int iovcnt) { if ( reading ) return errno = EBADF, -1; + if ( !channel ) + { + CurrentThread()->DeliverSignal(SIGPIPE); + return errno = EPIPE, -1; + } ssize_t result = channel->writev(ctx, iov, iovcnt); CurrentThread()->yield_to_tid = 0; Scheduler::ScheduleTrueThread(); @@ -615,17 +638,23 @@ ssize_t PipeEndpoint::writev(ioctx_t* ctx, const struct iovec* iov, int iovcnt) int PipeEndpoint::poll(ioctx_t* ctx, PollNode* node) { + if ( !channel ) + return 0; return reading ? channel->read_poll(ctx, node) : channel->write_poll(ctx, node); } bool PipeEndpoint::GetSIGPIPEDelivery() { + if ( !channel ) + return errno = EINVAL, true; return !reading ? channel->GetSIGPIPEDelivery() : false; } bool PipeEndpoint::SetSIGPIPEDelivery(bool deliver_sigpipe) { + if ( !channel ) + return errno = EINVAL, false; if ( !reading ) channel->SetSIGPIPEDelivery(deliver_sigpipe); else if ( reading && deliver_sigpipe != false ) @@ -635,12 +664,16 @@ bool PipeEndpoint::SetSIGPIPEDelivery(bool deliver_sigpipe) size_t PipeEndpoint::Size() { + if ( !channel ) + return errno = EINVAL, 0; return reading ? channel->ReadSize() : channel->WriteSize(); } bool PipeEndpoint::Resize(size_t new_size) { + if ( !channel ) + return errno = EINVAL, false; return reading ? channel->ReadResize(new_size) : channel->WriteResize(new_size); } diff --git a/kernel/vnode.cpp b/kernel/vnode.cpp index acc7a677..f1f45ff3 100644 --- a/kernel/vnode.cpp +++ b/kernel/vnode.cpp @@ -491,4 +491,9 @@ int Vnode::tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) return inode->tcsetattr(ctx, actions, tio); } +int Vnode::shutdown(ioctx_t* ctx, int how) +{ + return inode->shutdown(ctx, how); +} + } // namespace Sortix diff --git a/libc/include/fsmarshall-msg.h b/libc/include/fsmarshall-msg.h index 60b46591..afe60554 100644 --- a/libc/include/fsmarshall-msg.h +++ b/libc/include/fsmarshall-msg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -489,7 +489,14 @@ struct fsm_req_tcsetattr struct termios tio; }; -#define FSM_MSG_NUM 63 +#define FSM_REQ_SHUTDOWN 63 +struct fsm_req_shutdown +{ + ino_t ino; + int how; +}; + +#define FSM_MSG_NUM 64 #ifdef __cplusplus } /* extern "C" */ diff --git a/regress/Makefile b/regress/Makefile index 1873c296..31a42806 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -25,6 +25,7 @@ test-pthread-once \ test-pthread-self \ test-pthread-tls \ test-signal-raise \ +test-unix-socket-shutdown \ all: $(BINARIES) $(TESTS) diff --git a/regress/test-unix-socket-shutdown.c b/regress/test-unix-socket-shutdown.c new file mode 100644 index 00000000..2686b5db --- /dev/null +++ b/regress/test-unix-socket-shutdown.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * test-unix-socket-shutdown.c + * Tests whether unix sockets shut down correctly. + */ + +#include +#include + +#include +#include +#include + +#include "test.h" + +static void test(bool test_read, + bool parent_shutdown, + bool child_shutdown, + bool sigpipe) +{ + int fds[2]; + test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); + pid_t child_pid; + test_assert(0 <= (child_pid = fork())); + if ( child_pid == 0 ) + { + if ( !test_read && !sigpipe ) + signal(SIGPIPE, SIG_IGN); + if ( child_shutdown ) + { + if ( test_read ) + test_assert(shutdown(fds[0], SHUT_RD) == 0); + else + test_assert(shutdown(fds[0], SHUT_WR) == 0); + } + close(fds[1]); + char c; + if ( test_read ) + { + ssize_t amount = read(fds[0], &c, 1); + if ( amount < 0 ) + test_error(errno, "read"); + test_assert(amount == 0); + _exit(0); + } + else + { + while ( true ) + { + c = 'X'; + ssize_t amount = write(fds[0], &c, 1); + if ( !sigpipe && amount == -1 && errno == EPIPE ) + _exit(0); + if ( amount != 1 ) + test_error(errno, "write"); + } + } + } + close(fds[0]); + if ( parent_shutdown ) + { + if ( test_read ) + test_assert(shutdown(fds[1], SHUT_WR) == 0); + else + test_assert(shutdown(fds[1], SHUT_RD) == 0); + } + else if ( !parent_shutdown && !child_shutdown ) + close(fds[1]); + int status; + test_assert(waitpid(child_pid, &status, 0) == child_pid); + if ( test_read || !sigpipe ) + test_assert(WIFEXITED(status) && WEXITSTATUS(status) == 0); + else + test_assert(WIFSIGNALED(status) && WTERMSIG(status) == SIGPIPE); + if ( parent_shutdown || child_shutdown ) + close(fds[1]); +} + +int main(void) +{ + test(false, false, false, false); + test(true, false, false, false); + test(true, false, false, true); + test(false, true, false, false); + test(true, true, false, false); + test(true, true, false, true); + test(false, false, true, false); + test(true, false, true, false); + test(true, false, true, true); + test(false, true, true, false); + test(true, true, true, false); + test(true, true, true, true); + return 0; +}