Implement shutdown(2).

This commit is contained in:
Jonas 'Sortie' Termansen 2016-08-06 15:44:37 +02:00
parent d217621649
commit fcefd86432
13 changed files with 206 additions and 8 deletions

View File

@ -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

View File

@ -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<Inode>* out_root,
Ref<Inode>* out_server,
const struct stat* rootst)

View File

@ -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<Descriptor> open_elem(ioctx_t* ctx, const char* filename, int flags,

View File

@ -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);
};

View File

@ -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> inode;

View File

@ -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

View File

@ -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<Descriptor> 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)

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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" */

View File

@ -25,6 +25,7 @@ test-pthread-once \
test-pthread-self \
test-pthread-tls \
test-signal-raise \
test-unix-socket-shutdown \
all: $(BINARIES) $(TESTS)

View File

@ -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 <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdbool.h>
#include <unistd.h>
#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;
}