blob: 32a66f48bb735446d4b1f68be8c6e131a10c07bf [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fidl/fuchsia.posix.socket/cpp/wire_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/spawn.h>
#include <lib/sync/completion.h>
#include <lib/zx/process.h>
#include <zircon/processargs.h>
#include <zxtest/zxtest.h>
namespace {
class Server final : public fidl::testing::WireTestBase<fuchsia_posix_socket::StreamSocket> {
public:
Server(zx_handle_t channel, zx::socket peer) : channel_(channel), peer_(std::move(peer)) {}
void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override {
ADD_FAILURE("%s should not be called", name.c_str());
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Query(QueryCompleter::Sync& completer) final {
const std::string_view kProtocol = fuchsia_posix_socket::wire::kStreamSocketProtocolName;
// TODO(https://fxbug.dev/42052765): avoid the const cast.
uint8_t* data = reinterpret_cast<uint8_t*>(const_cast<char*>(kProtocol.data()));
completer.Reply(fidl::VectorView<uint8_t>::FromExternal(data, kProtocol.size()));
}
void Describe(DescribeCompleter::Sync& completer) override {
zx::socket peer;
if (const zx_status_t status =
peer_.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_WRITE, &peer);
status != ZX_OK) {
return completer.Close(status);
}
fidl::Arena alloc;
completer.Reply(fuchsia_posix_socket::wire::StreamSocketDescribeResponse::Builder(alloc)
.socket(std::move(peer))
.Build());
}
void Accept(AcceptRequestView request, AcceptCompleter::Sync& completer) override {
zx_status_t status = zx_object_signal_peer(channel_, 0, ZX_USER_SIGNAL_0);
if (status != ZX_OK) {
return completer.Close(status);
}
return completer.Close(sync_completion_wait(&accept_end_, ZX_TIME_INFINITE));
}
sync_completion_t& accept_end() { return accept_end_; }
private:
zx_handle_t channel_;
zx::socket peer_;
sync_completion_t accept_end_;
};
TEST(AtExit, ExitInAccept) {
auto endpoints = fidl::Endpoints<fuchsia_posix_socket::StreamSocket>::Create();
zx::socket client_socket, server_socket;
ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &client_socket, &server_socket));
// We're going to need the raw handle so we can signal on it and close it.
zx_handle_t server_handle = endpoints.server.channel().get();
Server server(server_handle, std::move(server_socket));
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
fidl::BindServer(loop.dispatcher(), std::move(endpoints.server), &server);
ASSERT_OK(loop.StartThread("fake-socket-server"));
const char* argv[] = {"/pkg/bin/accept-child", nullptr};
const fdio_spawn_action_t actions[] = {
{
.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
.h =
{
.id = PA_HND(PA_USER0, 0),
.handle = endpoints.client.channel().release(),
},
},
};
zx::process process;
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
ASSERT_OK(fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, argv[0], argv, nullptr,
sizeof(actions) / sizeof(fdio_spawn_action_t), actions,
process.reset_and_get_address(), err_msg),
"%s", err_msg);
// Wait until the child has let us know that it is exiting.
ASSERT_OK(zx_object_wait_one(server_handle, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr));
// Verify that the child didn't crash.
ASSERT_OK(process.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), nullptr));
sync_completion_signal(&server.accept_end());
zx_info_process_t proc_info;
ASSERT_OK(process.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr));
ASSERT_EQ(proc_info.return_code, 0);
}
} // namespace