| // 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. | |
| #ifndef SRC_VIRTUALIZATION_LIB_GUEST_INTERACTION_CLIENT_CLIENT_IMPL_H_ | |
| #define SRC_VIRTUALIZATION_LIB_GUEST_INTERACTION_CLIENT_CLIENT_IMPL_H_ | |
| #include <lib/fdio/fd.h> | |
| #include <zircon/system/ulib/fit/include/lib/fit/function.h> | |
| #include <filesystem> | |
| #include "src/virtualization/lib/grpc/fdio_util.h" | |
| #include "src/virtualization/lib/guest_interaction/client/client_operation_state.h" | |
| #include "src/virtualization/lib/guest_interaction/platform_interface/platform_interface.h" | |
| #include "src/virtualization/lib/guest_interaction/proto/guest_interaction.grpc.pb.h" | |
| template <class T> | |
| class ClientImpl { | |
| public: | |
| explicit ClientImpl(uint32_t vsock_fd); | |
| void Get(const std::string& source, zx::channel channel, TransferCallback callback); | |
| void Put(zx::channel source_channel, const std::string& destination, TransferCallback callback); | |
| void Exec(const std::string& command, const std::map<std::string, std::string>& env_vars, | |
| zx::socket std_in, zx::socket std_out, zx::socket std_err, | |
| fidl::InterfaceRequest<fuchsia::netemul::guest::CommandListener> listener); | |
| void Run(); | |
| void Stop(); | |
| private: | |
| grpc::CompletionQueue cq_; | |
| std::unique_ptr<GuestInteractionService::Stub> stub_; | |
| bool should_run_; | |
| T platform_interface_; | |
| }; | |
| // The gRPC channel internals take responsibility for closing the supplied vsock_fd. | |
| template <class T> | |
| ClientImpl<T>::ClientImpl(uint32_t vsock_fd) : should_run_(true) { | |
| stub_ = GuestInteractionService::NewStub(grpc::CreateInsecureChannelFromFd("vsock", vsock_fd)); | |
| } | |
| template <class T> | |
| void ClientImpl<T>::Run() { | |
| void* tag; | |
| bool ok; | |
| gpr_timespec wait_time = {}; | |
| should_run_ = true; | |
| while (should_run_) { | |
| grpc::CompletionQueue::NextStatus status = cq_.AsyncNext(&tag, &ok, wait_time); | |
| if (status == grpc::CompletionQueue::GOT_EVENT) { | |
| static_cast<CallData*>(tag)->Proceed(ok); | |
| } | |
| } | |
| } | |
| template <class T> | |
| void ClientImpl<T>::Stop() { | |
| should_run_ = false; | |
| } | |
| template <class T> | |
| void ClientImpl<T>::Get(const std::string& source, zx::channel channel, TransferCallback callback) { | |
| int32_t fd; | |
| zx_status_t fd_create_status = fdio_fd_create(channel.release(), &fd); | |
| if (fd_create_status != ZX_OK) { | |
| callback(OperationStatus::CLIENT_CREATE_FILE_FAILURE); | |
| return; | |
| } | |
| GetRequest get_request; | |
| get_request.set_source(source); | |
| GetCallData<T>* call_data = new GetCallData<T>(fd, std::move(callback)); | |
| call_data->reader_ = stub_->PrepareAsyncGet(&(call_data->ctx_), get_request, &cq_); | |
| call_data->reader_->StartCall(call_data); | |
| } | |
| template <class T> | |
| void ClientImpl<T>::Put(zx::channel source_channel, const std::string& destination, | |
| TransferCallback callback) { | |
| int32_t fd; | |
| zx_status_t fd_create_status = fdio_fd_create(source_channel.release(), &fd); | |
| if (fd_create_status != ZX_OK) { | |
| callback(OperationStatus::CLIENT_FILE_READ_FAILURE); | |
| return; | |
| } | |
| PutCallData<T>* call_data = new PutCallData<T>(fd, destination, std::move(callback)); | |
| call_data->writer_ = stub_->PrepareAsyncPut(&(call_data->ctx_), &(call_data->response_), &cq_); | |
| call_data->writer_->StartCall(call_data); | |
| } | |
| template <class T> | |
| void ClientImpl<T>::Exec(const std::string& command, | |
| const std::map<std::string, std::string>& env_vars, zx::socket std_in, | |
| zx::socket std_out, zx::socket std_err, | |
| fidl::InterfaceRequest<fuchsia::netemul::guest::CommandListener> req) { | |
| // Convert the provided zx::sockets into FD's. | |
| int32_t stdin_fd = ConvertSocketToNonBlockingFd(std::move(std_in)); | |
| int32_t stdout_fd = ConvertSocketToNonBlockingFd(std::move(std_out)); | |
| int32_t stderr_fd = ConvertSocketToNonBlockingFd(std::move(std_err)); | |
| std::unique_ptr<ListenerInterface> listener = std::make_unique<ListenerInterface>(std::move(req)); | |
| ExecCallData<T>* call_data = | |
| new ExecCallData<T>(command, env_vars, stdin_fd, stdout_fd, stderr_fd, std::move(listener)); | |
| call_data->rw_ = stub_->PrepareAsyncExec(call_data->ctx_.get(), &cq_); | |
| call_data->rw_->StartCall(call_data); | |
| } | |
| #endif // SRC_VIRTUALIZATION_LIB_GUEST_INTERACTION_CLIENT_CLIENT_IMPL_H_ |