admin管理员组

文章数量:1391925

I have code client on c++, client works on ubuntu. When i create session ny bash closed. Why?

In function SendCommand i see the info about shellPid but my shall pid is already closed May be problem in destructor ShellHandler? if u have idea write please

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <poll.h>
#include <csignal>
#include <atomic>
#include <unordered_map>
#include <mutex>
#include <thread>

std::atomic<bool> running(true);

void signalHandler(int signal) {
    if (signal == SIGINT) {
        running = false;
    }
}

class ShellHandler {
private:
    int pipeIn[2];  // Pipe for sending input to the shell
    int pipeOut[2]; // Pipe for receiving output from the shell
    pid_t shellPid;

public:
    ShellHandler() : pipeIn{-1, -1}, pipeOut{-1, -1}, shellPid(-1) {
        // Create pipes for communication with the shell
        if (pipe(pipeIn) == -1 || pipe(pipeOut) == -1) {
            throw std::runtime_error("Failed to create pipes.");
        }

        // Fork a child process to run the shell
        shellPid = fork();
        if (shellPid == -1) {
            throw std::runtime_error("Failed to fork.");
        }

        if (shellPid == 0) {
            // Child process: Run the shell
            close(pipeIn[1]);  // Close the write end of the input pipe (не используется в дочернем процессе)
            close(pipeOut[0]); // Close the read end of the output pipe (не используется в дочернем процессе)

            // Redirect stdin and stdout to the pipes
            if (dup2(pipeIn[0], STDIN_FILENO) == -1) {
                std::cerr << "Error: Failed to redirect stdin. Errno: " << errno << std::endl;
                _exit(1);
            }
            if (dup2(pipeOut[1], STDOUT_FILENO) == -1) {
                std::cerr << "Error: Failed to redirect stdout. Errno: " << errno << std::endl;
                _exit(1);
            }
            if (dup2(pipeOut[1], STDERR_FILENO) == -1) {
                std::cerr << "Error: Failed to redirect stderr. Errno: " << errno << std::endl;
                _exit(1);
            }

            // Execute the shell
            execl("/bin/bash", "bash", "-i", nullptr); // Запуск в интерактивном режиме

            // If execl fails
            std::cerr << "Error: Failed to execute shell. Errno: " << errno << std::endl;
            _exit(1);
        } else {
            // Parent process: Close unused pipe ends
            close(pipeIn[0]);  // Close the read end of the input pipe (не используется в родительском процессе)
            close(pipeOut[1]); // Close the write end of the output pipe (не используется в родительском процессе)
            std::cout << "Shell started with PID: " << shellPid << std::endl;
        }
    }

    ~ShellHandler() {
        // Clean up
        if (pipeIn[1] != -1) close(pipeIn[1]);
        if (pipeOut[0] != -1) close(pipeOut[0]);

        // Если shell все еще работает, отправляем команду на завершение
        if (shellPid > 0) {
            std::cout << "Checking if shell with PID " << shellPid << " is still running..." << std::endl;
            if (kill(shellPid, 0) == 0) {
                std::cout << "Sending exit command to shell with PID: " << shellPid << std::endl;
                std::string cmd = "exit\n";
                if (write(pipeIn[1], cmd.c_str(), cmd.size()) == -1) {
                    std::cerr << "Error: Failed to send exit command to shell. Errno: " << errno << std::endl;
                }

                // Ждем завершения shell
                waitpid(shellPid, nullptr, 0);
            } else {
                std::cerr << "Error: Shell process is not running. Errno: " << errno << std::endl;
            }
        }
    }

    void SendCommand(const std::string& command) {
        // Логирование информации о сессии
        std::cout << "Session Info:" << std::endl;
        std::cout << "  shellPid: " << shellPid << std::endl;
        std::cout << "  pipeIn[0]: " << pipeIn[0] << ", pipeIn[1]: " << pipeIn[1] << std::endl;
        std::cout << "  pipeOut[0]: " << pipeOut[0] << ", pipeOut[1]: " << pipeOut[1] << std::endl;

        if (pipeIn[1] == -1) {
            std::cerr << "Error: Pipe is closed." << std::endl;
            throw std::runtime_error("Error: Pipe is closed.");
        }

        // Проверка, что shell все еще работает
        if (kill(shellPid, 0) == -1) {
            std::cerr << "Error: Shell process is not running." << std::endl;
            throw std::runtime_error("Error: Shell process is not running.");
        }

        // Отправка команды
        std::string cmd = command + "\n";
        std::cout << "Sending command to shell: " << cmd << std::endl;
        if (write(pipeIn[1], cmd.c_str(), cmd.size()) == -1) {
            std::cerr << "Error: Failed to write to pipe. Errno: " << errno << std::endl;
            throw std::runtime_error("Error: Failed to write to pipe.");
        }
    }

    std::string ReceiveOutput() {
        // Set the output pipe to non-blocking mode
        SetNonBlocking(pipeOut[0]);

        // Use poll to check if there is data available in the output pipe
        struct pollfd fds[1];
        fds[0].fd = pipeOut[0];
        fds[0].events = POLLIN;

        std::string output;
        bool dataAvailable = true;
        while (dataAvailable) {
            int ret = poll(fds, 1, 100); // Wait for 100ms
            if (ret == -1) {
                std::cerr << "Error: poll failed." << std::endl;
                throw std::runtime_error("Error: poll failed.");
            } else if (ret == 0) {
                std::cout << "Poll timeout, no data available." << std::endl;
                dataAvailable = false;
            } else if (fds[0].revents & POLLIN) {
                char buffer[4096] = {0};
                int bytesRead = read(pipeOut[0], buffer, sizeof(buffer));
                if (bytesRead <= 0) {
                    std::cerr << "No data read from pipe." << std::endl;
                    dataAvailable = false;
                } else {
                    output.append(buffer, bytesRead);
                    std::cout << "Read " << bytesRead << " bytes from pipe: " << output << std::endl;
                }
            }
        }

        return output;
    }

private:
    void SetNonBlocking(int fd) {
        int flags = fcntl(fd, F_GETFL, 0);
        if (flags == -1) {
            std::cerr << "Error: fcntl(F_GETFL) failed." << std::endl;
            throw std::runtime_error("Error: fcntl(F_GETFL) failed.");
        }
        if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
            std::cerr << "Error: fcntl(F_SETFL) failed." << std::endl;
            throw std::runtime_error("Error: fcntl(F_SETFL) failed.");
        }
        std::cout << "Pipe set to non-blocking mode." << std::endl;
    }
};

class NetworkHandler {
private:
    int clientSocket;

public:
    NetworkHandler(const std::string& serverIP, int port) {
        // Create a socket
        clientSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (clientSocket == -1) {
            throw std::runtime_error("Failed to create socket.");
        }

        // Define server address
        sockaddr_in serverAddr;
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(port);
        inet_pton(AF_INET, serverIP.c_str(), &serverAddr.sin_addr);

        // Try to connect to the server in a loop
        while (running) {
            if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == 0) {
                std::cout << "Connected to the server." << std::endl;
                break; // Exit the loop on successful connection
            } else {
                std::cerr << "Failed to connect to the server. Retrying in 5 seconds..." << std::endl;
                sleep(5); // Wait for 5 seconds before retrying
            }
        }
    }

    ~NetworkHandler() {
        close(clientSocket);
    }

    std::string ReceiveCommand() {
        char buffer[4096] = {0};
        int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
        if (bytesReceived <= 0) {
            throw std::runtime_error("Connection closed by the server.");
        }
        return std::string(buffer, bytesReceived);
    }

    void SendResult(const std::string& result) {
        // Отправляем длину сообщения
        uint32_t messageLength = htonl(result.size());
        send(clientSocket, &messageLength, sizeof(messageLength), 0);

        // Отправляем само сообщение
        send(clientSocket, result.c_str(), result.size(), 0);
    }
};

class Client {
private:
    NetworkHandler networkHandler;
    std::unordered_map<int, ShellHandler> sessions; // Сессии (sessionID -> ShellHandler)
    int activeSessionID = -1; // Активная сессия
    std::mutex sessionsMutex; // Мьютекс для синхронизации доступа к сессиям
    int nextSessionID = 1;    // Счетчик для генерации уникальных sessionID

public:
    Client(const std::string& serverIP, int port)
        : networkHandler(serverIP, port) {}

    void Run() {
        while (running) {
            try {
                // Receive a command from the server
                std::string command = networkHandler.ReceiveCommand();
                std::cout << "Received command: " << command << std::endl;

                // Обработка команд управления сессиями
                if (command == "list_sessions") {
                    std::lock_guard<std::mutex> lock(sessionsMutex);
                    std::string result = "Active sessions:\n";
                    for (const auto& session : sessions) {
                        result += "SessionID: " + std::to_string(session.first) + "\n";
                    }
                    networkHandler.SendResult(result);
                } else if (command == "create_new_session") {
                    std::lock_guard<std::mutex> lock(sessionsMutex);
                    int sessionID = nextSessionID++;
                    sessions.emplace(sessionID, ShellHandler());
                    activeSessionID = sessionID;
                    networkHandler.SendResult("New session created and selected. SessionID: " + std::to_string(sessionID));
                } else {
                    if (activeSessionID == -1) {
                        networkHandler.SendResult("No active session. Use 'create_new_session' to create one.");
                        continue;
                    }

                    std::lock_guard<std::mutex> lock(sessionsMutex);
                    auto it = sessions.find(activeSessionID);
                    if (it != sessions.end()) {
                        std::cout << "Sending command to session " << activeSessionID << ": " << command << std::endl;
                        try {
                            it->second.SendCommand(command);
                            std::string output = it->second.ReceiveOutput();
                            networkHandler.SendResult(output);
                            std::cout << "Output from session " << activeSessionID << ":\n" << output << std::endl;
                        } catch (const std::exception& e) {
                            std::cerr << "Error in session " << activeSessionID << ": " << e.what() << std::endl;
                            networkHandler.SendResult("Error: " + std::string(e.what()));
                        }
                    } else {
                        networkHandler.SendResult("Active session not found.");
                    }
                }
            } catch (const std::exception& e) {
                std::cerr << "Error: " << e.what() << std::endl;
                // Не завершаем работу клиента, а продолжаем цикл
                continue;
            }
        }
    }
};

int main() {
    // Set up signal handler for graceful shutdown
    std::signal(SIGINT, signalHandler);

    try {
        Client client("192.168.12.34", 12345); // Replace with server's IP address
        client.Run();
    } catch (const std::exception& e) {
        std::cerr << "Fatal error: " << e.what() << std::endl;
        return 1;
    }

    std::cout << "Connection closed." << std::endl;
    return 0;
}

i added more debugs in code to the localisate the problem

Server code

#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <thread>
#include <atomic>
#include <mutex>
#include <unordered_map>
#pragma comment(lib, "ws2_32.lib")

std::mutex clientsMutex; // Мьютекс для синхронизации доступа к списку клиентов

class ClientHandler {
public:
    SOCKET socket;
    int clientID;
    std::atomic<bool> running;

    ClientHandler(SOCKET sock, int id) : socket(sock), clientID(id), running(true) {}

    // Удаляем конструктор копирования и оператор присваивания
    ClientHandler(const ClientHandler&) = delete;
    ClientHandler& operator=(const ClientHandler&) = delete;

    void SendCommand(const std::string& command) {
        if (send(socket, command.c_str(), static_cast<int>(command.size()), 0) == SOCKET_ERROR) {
            std::cerr << "Error: Failed to send command to ClientID: " << clientID << std::endl;
            running = false;
        }
    }

    std::string ReceiveResult() {
        // Сначала получаем длину сообщения
        uint32_t messageLength;
        int bytesReceived = recv(socket, reinterpret_cast<char*>(&messageLength), sizeof(messageLength), 0);
        if (bytesReceived <= 0) {
            running = false;
            return "";
        }

        // Преобразуем длину сообщения из сетевого порядка байт в хостовый
        messageLength = ntohl(messageLength);

        // Получаем само сообщение
        std::string result;
        result.resize(messageLength);
        char* buffer = &result[0];
        int totalReceived = 0;

        while (totalReceived < static_cast<int>(messageLength)) {
            bytesReceived = recv(socket, buffer + totalReceived, static_cast<int>(messageLength - totalReceived), 0);
            if (bytesReceived <= 0) {
                running = false;
                return "";
            }
            totalReceived += bytesReceived;
        }

        return result;
    }
};

class Server {
private:
    std::list<ClientHandler> clients; // Список клиентов
    std::vector<std::thread> clientThreads; // Потоки для обработки клиентов
    std::unordered_map<int, int> activeSessions; // Активные сессии (clientID -> sessionID)
    std::mutex sessionsMutex; // Мьютекс для синхронизации доступа к активным сессиям
    std::atomic<bool> running;
    int activeClientID = -1; // ID активного клиента

    void HandleClient(ClientHandler& client) {
        while (client.running) {
            std::string result = client.ReceiveResult();
            if (!result.empty()) {
                std::cout << "Result from ClientID " << client.clientID << ":\n" << result << std::endl;
            }
            else {
                std::cerr << "ClientID " << client.clientID << " disconnected." << std::endl;
                break;
            }
        }

        // Удаляем клиента из списка
        std::lock_guard<std::mutex> lock(clientsMutex);
        clients.remove_if([&client](const ClientHandler& c) { return c.clientID == client.clientID; });
    }

public:
    Server() : running(true) {}

    void Run() {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
            std::cerr << "WSAStartup failed." << std::endl;
            return;
        }

        SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (serverSocket == INVALID_SOCKET) {
            std::cerr << "Socket creation failed." << std::endl;
            WSACleanup();
            return;
        }

        sockaddr_in serverAddr;
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_addr.s_addr = INADDR_ANY;
        serverAddr.sin_port = htons(12345);

        if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
            std::cerr << "Bind failed." << std::endl;
            closesocket(serverSocket);
            WSACleanup();
            return;
        }

        if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
            std::cerr << "Listen failed." << std::endl;
            closesocket(serverSocket);
            WSACleanup();
            return;
        }

        std::cout << "Server is running. Waiting for clients..." << std::endl;

        while (running) {
            SOCKET clientSocket = accept(serverSocket, NULL, NULL);
            if (clientSocket == INVALID_SOCKET) {
                std::cerr << "Accept failed." << std::endl;
                continue;
            }

            std::lock_guard<std::mutex> lock(clientsMutex);
            int clientID = clients.size() + 1;
            clients.emplace_back(clientSocket, clientID);
            clientThreads.emplace_back(&Server::HandleClient, this, std::ref(clients.back()));
            std::cout << "Client connected. ClientID: " << clientID << std::endl;
        }

        closesocket(serverSocket);
        WSACleanup();
    }

    void HandleCommands() {
        std::string command;
        while (true) {
            std::cout << "Enter command: ";
            std::getline(std::cin, command);

            if (command.substr(0, 6) == "change") {
                // Выбор активного клиента
                int id = std::stoi(command.substr(7));
                std::lock_guard<std::mutex> lock(clientsMutex);
                auto it = std::find_if(clients.begin(), clients.end(), [id](const ClientHandler& client) {
                    return client.clientID == id;
                    });

                if (it != clients.end()) {
                    activeClientID = id;
                    std::cout << "Selected ClientID: " << id << std::endl;
                }
                else {
                    std::cout << "ClientID not found." << std::endl;
                }
            }
            else if (command == "list") {
                // Список подключенных клиентов
                std::lock_guard<std::mutex> lock(clientsMutex);
                std::cout << "Connected clients:" << std::endl;
                for (const auto& client : clients) {
                    std::cout << "ClientID: " << client.clientID << std::endl;
                }
            }
            else if (command == "exit") {
                running = false;
                break;
            }
            else {
                // Отправка команды активному клиенту
                if (activeClientID == -1) {
                    std::cout << "No client selected. Use 'change <client_id>' to select a client." << std::endl;
                    continue;
                }

                std::lock_guard<std::mutex> lock(clientsMutex);
                auto it = std::find_if(clients.begin(), clients.end(), [this](const ClientHandler& client) {
                    return client.clientID == activeClientID;
                    });

                if (it != clients.end()) {
                    it->SendCommand(command);
                }
                else {
                    std::cout << "Active client not found." << std::endl;
                }
            }
        }
    }
};

int main() {
    Server server;
    std::thread serverThread([&server]() { server.Run(); });
    server.HandleCommands();

    serverThread.join();
    return 0;
}

本文标签: