| // Copyright 2020 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 <arpa/inet.h> | |
| #include <fcntl.h> | |
| #include <string.h> | |
| #include <sys/socket.h> | |
| #include <unistd.h> | |
| #include <set> | |
| #include "src/developer/debug/shared/buffered_fd.h" | |
| #include "src/developer/debug/shared/platform_message_loop.h" | |
| #include "src/developer/shell/mirror/common.h" | |
| #include "src/developer/shell/mirror/wire_format.h" | |
| #ifndef SRC_DEVELOPER_SHELL_MIRROR_SERVER_H_ | |
| #define SRC_DEVELOPER_SHELL_MIRROR_SERVER_H_ | |
| namespace shell::mirror { | |
| class SocketServer; | |
| // Represents a instance of a connection "attempt" to a client. | |
| class SocketConnection { | |
| public: | |
| explicit SocketConnection(SocketServer* server) : server_(server), id_(global_id_++) {} | |
| ~SocketConnection(); | |
| SocketConnection& operator=(SocketConnection&) = delete; | |
| SocketConnection(SocketConnection&) = delete; | |
| // |main_thread_loop| is used for posting a task that creates the debug agent after accepting a | |
| // a connection. This is because the debug agent assumes it's running on the message loop's | |
| // thread. | |
| Err Accept(debug_ipc::MessageLoop* main_thread_loop, int server_fd); | |
| struct SocketConnectionComparator { | |
| bool operator()(const SocketConnection& one, const SocketConnection& two) { | |
| return one.id_ < two.id_; | |
| } | |
| }; | |
| // Unregisters this socket connection from a given server. This has the effect | |
| // of deleting the connection, so use with caution. | |
| void UnregisterAndDestroy(); | |
| private: | |
| static uint64_t global_id_; | |
| SocketServer* server_; | |
| debug_ipc::BufferedFD buffer_; | |
| bool connected_ = false; | |
| uint64_t id_; | |
| }; | |
| // Represents a server. | |
| class SocketServer : public debug_ipc::FDWatcher { | |
| public: | |
| SocketServer() = default; | |
| // A configuration object for the server. | |
| struct ConnectionConfig { | |
| debug_ipc::PlatformMessageLoop* message_loop = nullptr; | |
| int port = 0; | |
| std::optional<std::string> path; | |
| }; | |
| // Runs the server with the given configuration. | |
| void Run(ConnectionConfig config); | |
| // Sets up loops in a sensible way (one loop to accept a connection, and one loop to respond to | |
| // requests), and runs a server. Calls inited_fn when it is done initing. | |
| Err RunInLoop(ConnectionConfig config, debug_ipc::FileLineFunction from_here, | |
| fit::closure inited_fn); | |
| int GetPort() { return config_.port; } | |
| std::string GetPath() { return *config_.path; } | |
| void RemoveConnection(SocketConnection* connection) { | |
| for (auto it = connections_.begin(); it != connections_.end(); ++it) { | |
| if (it->get() == connection) { | |
| connections_.erase(it); | |
| return; | |
| } | |
| } | |
| } | |
| // IMPORTANT: All others can only be called on the main thread. | |
| // Initialize the server. | |
| // |port| is the port to use. If *port is 0, the function will try to assign one. | |
| Err Init(uint16_t* port); | |
| virtual void OnFDReady(int fd, bool read, bool write, bool err) override; | |
| private: | |
| fbl::unique_fd server_socket_; | |
| std::set<std::unique_ptr<SocketConnection>> connections_; | |
| debug_ipc::MessageLoop::WatchHandle connection_monitor_; | |
| ConnectionConfig config_; | |
| FXL_DISALLOW_COPY_AND_ASSIGN(SocketServer); | |
| }; | |
| // Manages sending data along a given StreamBuffer. | |
| class Update { | |
| public: | |
| Update(debug_ipc::StreamBuffer* stream, const std::string* path) | |
| : stream_(stream), files_(*path), path_(*path) {} | |
| // Sends the contents of |path| to |stream|. | |
| Err SendUpdates(); | |
| private: | |
| debug_ipc::StreamBuffer* stream_; | |
| Files files_; | |
| std::string path_; | |
| }; | |
| } // namespace shell::mirror | |
| #endif // SRC_DEVELOPER_SHELL_MIRROR_SERVER_H_ |