| // 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 "src/virtualization/lib/grpc/grpc_vsock_server.h" | |
| #include <lib/fit/bridge.h> | |
| #include <lib/syslog/cpp/macros.h> | |
| #include "src/virtualization/lib/grpc/fdio_util.h" | |
| void GrpcVsockServerBuilder::RegisterService(grpc::Service* service) { | |
| builder_->RegisterService(service); | |
| } | |
| void GrpcVsockServerBuilder::AddListenPort(uint32_t vsock_port) { | |
| fit::bridge<void, zx_status_t> bridge; | |
| (*socket_endpoint_) | |
| ->Listen(vsock_port, server_->NewBinding(), | |
| // The unused capture of |socket_endpoint_| here is important. | |
| // This is a shared_ptr we need to keep alive longer than this | |
| // closure so ensure the FIDL reply will be delivered. If we | |
| // don't do this, the underlying channel may be closed if the | |
| // builder is free'd after a call to Build(). | |
| [socket_endpoint = socket_endpoint_, | |
| completer = std::move(bridge.completer)](zx_status_t status) mutable { | |
| if (status != ZX_OK) { | |
| completer.complete_error(status); | |
| } else { | |
| completer.complete_ok(); | |
| } | |
| }); | |
| service_promises_.push_back(bridge.consumer.promise()); | |
| } | |
| fit::promise<std::unique_ptr<GrpcVsockServer>, zx_status_t> GrpcVsockServerBuilder::Build() { | |
| return fit::join_promise_vector(std::move(service_promises_)) | |
| .then([builder = std::move(builder_)]( | |
| const fit::result<std::vector<fit::result<void, zx_status_t>>>& result) mutable | |
| -> fit::result<std::unique_ptr<grpc::Server>, zx_status_t> { | |
| // join_promise_vector should never fail, but instead return a vector | |
| // of results. | |
| FX_CHECK(result.is_ok()) << "fit::join_promise_vector returns fit::error"; | |
| for (const auto& result : result.value()) { | |
| if (result.is_error()) { | |
| FX_CHECK(false) << "Failed to listen on vsock port: " << result.error(); | |
| return fit::error(result.error()); | |
| } | |
| } | |
| // All the vsock listeners have been initialized. Now start the gRPC | |
| // server. | |
| return fit::ok(builder->BuildAndStart()); | |
| }) | |
| .and_then([server = std::move(server_)](std::unique_ptr<grpc::Server>& server_impl) mutable { | |
| server->SetServerImpl(std::move(server_impl)); | |
| return fit::ok(std::move(server)); | |
| }); | |
| } | |
| // This method is registered as a FIDL callback for all of our vsock port | |
| // listeners. In response we need to allocate a new zx::socket to use for the | |
| // connection and register one end with gRPC. | |
| void GrpcVsockServer::Accept(uint32_t src_cid, uint32_t src_port, uint32_t port, | |
| AcceptCallback callback) { | |
| FX_CHECK(server_); | |
| zx::socket h1, h2; | |
| zx_status_t status = zx::socket::create(ZX_SOCKET_STREAM, &h1, &h2); | |
| if (status != ZX_OK) { | |
| FX_LOGS(ERROR) << "Failed to create socket " << status; | |
| callback(ZX_ERR_CONNECTION_REFUSED, zx::handle()); | |
| return; | |
| } | |
| // gRPC is not compatible with Zircon primitives, so we need to provide it | |
| // with a compatible file descriptor instead. | |
| int fd = ConvertSocketToNonBlockingFd(std::move(h1)); | |
| if (fd < 0) { | |
| FX_LOGS(ERROR) << "Failed get file descriptor for socket"; | |
| callback(ZX_ERR_INTERNAL, zx::socket()); | |
| return; | |
| } | |
| grpc::AddInsecureChannelFromFd(server_.get(), fd); | |
| callback(ZX_OK, std::move(h2)); | |
| } |