/* * Copyright 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * Authors: * Charles Kerr */ #include #include #include #include #include /** * A Mock ADBD server. * * Binds to a local domain socket, sends public key requests across it, * and reads back the client's responses. */ class GAdbdServer { public: GAdbdServer(const std::string& socket_path, const std::vector& requests): m_requests{requests}, m_server_socket{create_server_socket(socket_path)}, m_cancellable{g_cancellable_new()}, m_worker_thread{&GAdbdServer::worker_func, this} { } ~GAdbdServer() { // tell the worker thread to stop whatever it's doing and exit. g_cancellable_cancel(m_cancellable); m_worker_thread.join(); g_clear_object(&m_cancellable); g_clear_object(&m_server_socket); } const std::vector m_requests; std::vector m_responses; private: void worker_func() // runs in worker thread { auto requests = m_requests; while (!g_cancellable_is_cancelled(m_cancellable) && !requests.empty()) { // wait for a client connection g_message("GAdbdServer::Impl::worker_func() calling g_socket_accept()"); GError* error {}; auto client_socket = g_socket_accept(m_server_socket, m_cancellable, &error); if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_message("GAdbdServer: Error accepting socket connection: %s", error->message); g_clear_error(&error); break; } // pop the next request off the stack auto request = requests.front(); // send the request g_message("GAdbdServer::Impl::worker_func() sending req [%s]", request.c_str()); g_socket_send(client_socket, request.c_str(), request.size(), m_cancellable, &error); if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_message("GAdbdServer: Error sending request: %s", error->message); g_clear_error(&error); g_clear_object(&client_socket); break; } // read the response g_message("GAdbdServer::Impl::worker_func() reading response"); char buf[4096]; const auto n_bytes = g_socket_receive(client_socket, buf, sizeof(buf), m_cancellable, &error); if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_message("GAdbdServer: Error reading response: %s", error->message); g_clear_error(&error); g_clear_object(&client_socket); continue; } const std::string response(buf, std::string::size_type(n_bytes)); g_message("server read %d bytes, got response: '%s'", int(n_bytes), response.c_str()); if (!response.empty()) { m_responses.push_back(response); requests.erase(requests.begin()); } // cleanup g_clear_object(&client_socket); } } // bind to a local domain socket static GSocket* create_server_socket(const std::string& socket_path) { GError* error {}; auto socket = g_socket_new(G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error(error); auto address = g_unix_socket_address_new (socket_path.c_str()); g_socket_bind (socket, address, false, &error); g_assert_no_error (error); g_clear_object (&address); g_socket_listen (socket, &error); g_assert_no_error (error); return socket; } GSocket* m_server_socket {}; GCancellable* m_cancellable {}; std::thread m_worker_thread; };