blob: 4185f38c443ffd38b497902c638b89dfbd898fae [file]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mpact/sim/util/renode/socket_cli.h"
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <istream>
#include <ostream>
#include <thread> // NOLINT
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/log/log.h"
#include "mpact/sim/generic/debug_command_shell_interface.h"
#include "mpact/sim/util/renode/socket_streambuf.h"
namespace mpact {
namespace sim {
namespace util {
namespace renode {
using ::mpact::sim::generic::DebugCommandShellInterface;
SocketCLI::SocketCLI(int port, DebugCommandShellInterface& dbg_shell,
absl::AnyInvocable<void(bool)> is_connected_cb)
: dbg_shell_(dbg_shell), is_connected_cb_(std::move(is_connected_cb)) {
// Set initial status as not connected.
is_connected_cb_(false);
good_ = true;
// If the port has not been specified, just return.
if (port == -1) {
good_ = false;
return;
}
// Create the socket on the given port.
server_socket_ =
socket(/*domain=*/AF_INET, /*type=*/SOCK_STREAM, /*protocol=*/0);
int one = 1;
int err =
setsockopt(server_socket_, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (err != 0) {
LOG(ERROR) << "Failed to set socket options: " << strerror(errno);
good_ = false;
return;
}
sockaddr_in server_address_int;
server_address_int.sin_family = AF_INET;
server_address_int.sin_addr.s_addr = INADDR_ANY;
server_address_int.sin_port = htons(port);
std::memset(&server_address_int.sin_zero, 0,
sizeof(server_address_int.sin_zero));
int res = bind(server_socket_,
reinterpret_cast<const sockaddr*>(&server_address_int),
sizeof(server_address_int));
if (res != 0) {
good_ = false;
LOG(ERROR) << "Failed to bind to port " << port << ": " << strerror(errno);
return;
}
res = listen(server_socket_, /*backlog=*/1);
if (res != 0) {
good_ = false;
LOG(ERROR) << "Failed to listen on port " << port << ": "
<< strerror(errno);
}
// Launch CLI thread.
cli_thread_ = std::thread([this, port]() {
// Accept the connection and set up streams.
cli_fd_ = accept(server_socket_, /*addr=*/nullptr, /*addrlen=*/nullptr);
if (cli_fd_ == -1) {
good_ = false;
LOG(ERROR) << "Failed to accept connection on port " << port << ": "
<< strerror(errno);
} else {
good_ = true;
// Create input and output streams attached to the socket from which the
// cli will read commands/write responses.
SocketStreambuf out_buf(cli_fd_);
SocketStreambuf in_buf(cli_fd_);
std::ostream os(&out_buf);
if (!os.good()) return;
std::istream is(&in_buf);
if (!is.good()) return;
// Notify that CLI is connected.
is_connected_cb_(true);
// Start the CLI.
os << "CLI connected:\n";
dbg_shell_.Run(is, os);
// Notify that CLI is disconnected.
is_connected_cb_(false);
}
good_ = false;
});
}
SocketCLI::~SocketCLI() {
// Shutdown the connection.
if (cli_fd_ != -1) {
(void)shutdown(cli_fd_, SHUT_RDWR);
int err = close(cli_fd_);
if (err != 0) {
LOG(ERROR) << "Failed to close client socket " << cli_fd_ << ": "
<< strerror(errno);
}
}
if (server_socket_ != -1) {
int res = shutdown(server_socket_, SHUT_RDWR);
if (res != 0) {
LOG(ERROR) << "Failed to shutdown server socket " << server_socket_
<< ": " << strerror(errno);
}
res = fcntl(server_socket_, F_GETFD);
if (res >= 0) {
int err = close(server_socket_);
if (err != 0) {
LOG(ERROR) << "Failed to close server socket " << server_socket_ << ": "
<< strerror(errno);
}
}
}
if (cli_thread_.joinable()) cli_thread_.join();
}
} // namespace renode
} // namespace util
} // namespace sim
} // namespace mpact