Implement shutdown(2).
This commit is contained in:
parent
d217621649
commit
fcefd86432
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" */
|
||||
|
|
|
@ -25,6 +25,7 @@ test-pthread-once \
|
|||
test-pthread-self \
|
||||
test-pthread-tls \
|
||||
test-signal-raise \
|
||||
test-unix-socket-shutdown \
|
||||
|
||||
all: $(BINARIES) $(TESTS)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue