fix: 修复了部分问题
This commit is contained in:
parent
21027a79be
commit
9c5207c59d
13
Makefile
13
Makefile
|
|
@ -25,6 +25,11 @@ TEST_CLIENT = $(BUILD_DIR)/test_client
|
||||||
TEST_CLIENT_SRC = $(TESTS_DIR)/test_client.c
|
TEST_CLIENT_SRC = $(TESTS_DIR)/test_client.c
|
||||||
TEST_CLIENT_DEPS = $(BUILD_DIR)/client.o $(BUILD_DIR)/debug.o
|
TEST_CLIENT_DEPS = $(BUILD_DIR)/client.o $(BUILD_DIR)/debug.o
|
||||||
|
|
||||||
|
# 并发测试客户端
|
||||||
|
TEST_CONCURRENT_CLIENT = $(BUILD_DIR)/test_concurrent_client
|
||||||
|
TEST_CONCURRENT_CLIENT_SRC = $(TESTS_DIR)/test_concurrent_client.c
|
||||||
|
TEST_CONCURRENT_CLIENT_DEPS = $(BUILD_DIR)/client.o $(BUILD_DIR)/debug.o
|
||||||
|
|
||||||
# 如果需要开启 debug,只需执行 make DEBUG=1
|
# 如果需要开启 debug,只需执行 make DEBUG=1
|
||||||
ifeq ($(DEBUG),1)
|
ifeq ($(DEBUG),1)
|
||||||
CFLAGS += -DDEBUG -g
|
CFLAGS += -DDEBUG -g
|
||||||
|
|
@ -40,12 +45,14 @@ ifeq ($(NO_CONFIG_CHECK),1)
|
||||||
CFLAGS += -DNO_CONFIG_CHECK
|
CFLAGS += -DNO_CONFIG_CHECK
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all clean debug hook rebuild pre_build test_client
|
.PHONY: all clean debug hook rebuild pre_build test_client test_concurrent_client
|
||||||
|
|
||||||
all: pre_build $(TARGET) $(HOOK_TARGET)
|
all: pre_build $(TARGET) $(HOOK_TARGET)
|
||||||
|
|
||||||
test_client: pre_build $(TEST_CLIENT)
|
test_client: pre_build $(TEST_CLIENT)
|
||||||
|
|
||||||
|
test_concurrent_client: pre_build $(TEST_CONCURRENT_CLIENT)
|
||||||
|
|
||||||
pre_build:
|
pre_build:
|
||||||
ifeq ($(DEBUG),1)
|
ifeq ($(DEBUG),1)
|
||||||
@echo "Building with debug flags..."
|
@echo "Building with debug flags..."
|
||||||
|
|
@ -77,6 +84,10 @@ $(TEST_CLIENT): $(TEST_CLIENT_SRC) $(TEST_CLIENT_DEPS)
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
$(CC) -Wall -Wextra -o $@ $(TEST_CLIENT_SRC) $(TEST_CLIENT_DEPS)
|
$(CC) -Wall -Wextra -o $@ $(TEST_CLIENT_SRC) $(TEST_CLIENT_DEPS)
|
||||||
|
|
||||||
|
$(TEST_CONCURRENT_CLIENT): $(TEST_CONCURRENT_CLIENT_SRC) $(TEST_CONCURRENT_CLIENT_DEPS)
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
$(CC) -Wall -Wextra -pthread -o $@ $(TEST_CONCURRENT_CLIENT_SRC) $(TEST_CONCURRENT_CLIENT_DEPS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
|
|
||||||
21
src/client.c
21
src/client.c
|
|
@ -1,5 +1,4 @@
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
@ -9,10 +8,12 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
#define BUFFER_SIZE 4096
|
#define BUFFER_SIZE 4096
|
||||||
|
|
||||||
// 读取完整消息
|
// 读取完整消息
|
||||||
ssize_t readMessage(int sock, char *buffer, size_t maxSize) {
|
ssize_t readMessage(int sock, char* buffer, size_t maxSize) {
|
||||||
uint32_t messageLen;
|
uint32_t messageLen;
|
||||||
// 先读取消息长度
|
// 先读取消息长度
|
||||||
if (read(sock, &messageLen, sizeof(messageLen)) != sizeof(messageLen)) {
|
if (read(sock, &messageLen, sizeof(messageLen)) != sizeof(messageLen)) {
|
||||||
|
|
@ -35,8 +36,8 @@ ssize_t readMessage(int sock, char *buffer, size_t maxSize) {
|
||||||
return messageLen;
|
return messageLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
int send_exec_params(const char *filename, char *const argv[],
|
int seeking_solutions(const char* filename, char* const argv[],
|
||||||
char *const envp[], const char *logPath) {
|
char* const envp[], const char* logPath, int* output_fd) {
|
||||||
char abs_path[PATH_MAX];
|
char abs_path[PATH_MAX];
|
||||||
char pwd[PATH_MAX];
|
char pwd[PATH_MAX];
|
||||||
|
|
||||||
|
|
@ -90,7 +91,7 @@ int send_exec_params(const char *filename, char *const argv[],
|
||||||
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
|
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
|
||||||
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
|
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
|
||||||
|
|
||||||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||||
DEBUG_LOG("connect error: %s\n", strerror(errno));
|
DEBUG_LOG("connect error: %s\n", strerror(errno));
|
||||||
close(sock);
|
close(sock);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -146,8 +147,14 @@ int send_exec_params(const char *filename, char *const argv[],
|
||||||
|
|
||||||
strncpy(display_buffer, buffer, BUFFER_SIZE - 1);
|
strncpy(display_buffer, buffer, BUFFER_SIZE - 1);
|
||||||
display_buffer[BUFFER_SIZE - 1] = '\0';
|
display_buffer[BUFFER_SIZE - 1] = '\0';
|
||||||
printf("%s", display_buffer);
|
|
||||||
fflush(stdout);
|
// 使用指定的输出描述符,如果为NULL则使用stdout
|
||||||
|
write(*output_fd, display_buffer, strlen(display_buffer));
|
||||||
|
|
||||||
|
// 如果是标准输出,flush
|
||||||
|
if (*output_fd == STDOUT_FILENO) {
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(sock);
|
close(sock);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,6 @@
|
||||||
#define MAX_BUF_SIZE 4096
|
#define MAX_BUF_SIZE 4096
|
||||||
|
|
||||||
// 函数声明
|
// 函数声明
|
||||||
int send_exec_params(const char *filename, char *const argv[], char *const envp[], const char *logPath);
|
int seeking_solutions(const char *filename, char *const argv[], char *const envp[], const char *logPath, int *output_fd);
|
||||||
|
|
||||||
#endif // EXEC_SOCKET_H
|
#endif // EXEC_SOCKET_H
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,17 @@
|
||||||
|
|
||||||
#define COMMAND_NOT_FOUND "/usr/lib/command-not-found"
|
#define COMMAND_NOT_FOUND "/usr/lib/command-not-found"
|
||||||
|
|
||||||
#ifdef DEBUG // 如果是debug模式,在本地目录生成log,方便debug
|
// #ifdef DEBUG // 如果是debug模式,在本地目录生成log,方便debug
|
||||||
|
|
||||||
#define LOG_FILE "./logs/execve.log"
|
// #define LOG_FILE "./logs/execve.log"
|
||||||
#define LOG_OUT_FILE "./logs/execve_out.log"
|
// #define LOG_OUT_FILE "./logs/execve_out.log"
|
||||||
|
|
||||||
#else
|
// #else
|
||||||
|
|
||||||
#define CONFIG_FILE ""
|
#define LOG_FILE "/var/log/bash-smart/execve.log"
|
||||||
#define LOG_FILE "/etc/exec_hook/logs/execve.log"
|
#define LOG_OUT_FILE "/var/log/bash-smart/execve_out.log"
|
||||||
#define LOG_OUT_FILE "/etc/exec_hook/logs/execve_out.log"
|
|
||||||
|
// #endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_PATH_LEN 256
|
#define MAX_PATH_LEN 256
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "init_cleanup.h"
|
#include "init_cleanup.h"
|
||||||
|
#include "terminal_utils.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "pty_dup.h"
|
#include "pty_dup.h"
|
||||||
#include "rules.h"
|
#include "rules.h"
|
||||||
|
|
@ -238,10 +239,29 @@ int enhance_execve(const char *filename, char *const argv[],
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
write_log(filename, argv);
|
// 检查日志权限,如果没有权限则跳过日志记录和PTY设置
|
||||||
|
int log_ret = write_log(filename, argv);
|
||||||
|
if (log_ret != 0) {
|
||||||
|
DEBUG_LOG("write_log failed, no permission for log directory");
|
||||||
|
#ifdef HOOK
|
||||||
|
return orig_execve(filename, argv, envp);
|
||||||
|
#else
|
||||||
|
return execve(filename, argv, envp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Duplicate stdout and stderr to the log file
|
// Duplicate stdout and stderr to the log file
|
||||||
dupIO(filename, argv, envp);
|
int ret = dupIO(filename, argv, envp);
|
||||||
|
if (ret != 0) {
|
||||||
|
DEBUG_LOG("dupIO failed with code: %d", ret);
|
||||||
|
restore_terminal();
|
||||||
|
#ifdef HOOK
|
||||||
|
return orig_execve(filename, argv, envp);
|
||||||
|
#else
|
||||||
|
return execve(filename, argv, envp);
|
||||||
|
// return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HOOK
|
#ifdef HOOK
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
#include "init_cleanup.h"
|
#include "init_cleanup.h"
|
||||||
#include "exec_hook.h"
|
#include "exec_hook.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "pty_dup.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
// // Constructor, executed when the library is loaded
|
// // Constructor, executed when the library is loaded
|
||||||
// __attribute__((constructor)) static void initialize() {
|
// __attribute__((constructor)) static void initialize() {
|
||||||
|
|
@ -15,7 +17,7 @@
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Destructor, executed when the library is unloaded
|
// Destructor, executed when the library is unloaded
|
||||||
__attribute__((destructor)) void cleanup_shared_memory() {
|
__attribute__((destructor)) void cleanup() {
|
||||||
DEBUG_LOG("execve_intercept library unloaded.");
|
DEBUG_LOG("execve_intercept library unloaded.");
|
||||||
// Log output paths
|
// Log output paths
|
||||||
DEBUG_LOG("Log file: %s", LOG_FILE);
|
DEBUG_LOG("Log file: %s", LOG_FILE);
|
||||||
|
|
@ -29,10 +31,26 @@ __attribute__((destructor)) void cleanup_shared_memory() {
|
||||||
// }
|
// }
|
||||||
// shared_config = NULL;
|
// shared_config = NULL;
|
||||||
// }
|
// }
|
||||||
#ifdef DEBUG
|
// #ifdef DEBUG
|
||||||
print_stacktrace();
|
// print_stacktrace();
|
||||||
#endif
|
// #endif
|
||||||
// Note: We don't delete the shared memory segment here, as it might be
|
// Note: We don't delete the shared memory segment here, as it might be
|
||||||
// used by other processes. A separate mechanism would be needed to manage
|
// used by other processes. A separate mechanism would be needed to manage
|
||||||
// the lifecycle of the shared memory if deletion is required.
|
// the lifecycle of the shared memory if deletion is required.
|
||||||
|
|
||||||
|
// 获取stderr日志文件名
|
||||||
|
const char* stdout_log = GET_LOG_FILE(child_pid, 1);
|
||||||
|
const char* stderr_log = GET_LOG_FILE(child_pid, 0);
|
||||||
|
|
||||||
|
// 如果存在日志文件且有写权限,则删除
|
||||||
|
if (access(stdout_log, F_OK) != -1 && access(stdout_log, W_OK) != -1) {
|
||||||
|
if (unlink(stdout_log) == 0) {
|
||||||
|
DEBUG_LOG("Stdout log file removed: %s", stdout_log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (access(stderr_log, F_OK) != -1 && access(stderr_log, W_OK) != -1) {
|
||||||
|
if (unlink(stderr_log) == 0) {
|
||||||
|
DEBUG_LOG("Stderr log file removed: %s", stderr_log);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
#define INIT_CLEANUP_H
|
#define INIT_CLEANUP_H
|
||||||
|
|
||||||
// void initialize();
|
// void initialize();
|
||||||
__attribute__((destructor)) void cleanup_shared_memory();
|
__attribute__((destructor)) void cleanup();
|
||||||
|
|
||||||
#endif // INIT_CLEANUP_H
|
#endif // INIT_CLEANUP_H
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
#include "exec_hook.h"
|
#include "exec_hook.h"
|
||||||
|
|
||||||
// Write log
|
// Write log
|
||||||
void write_log(const char *filename, char *const argv[]) {
|
int write_log(const char *filename, char *const argv[]) {
|
||||||
DEBUG_LOG("Writing exec log for command: %s", filename);
|
DEBUG_LOG("Writing exec log for command: %s", filename);
|
||||||
time_t now;
|
time_t now;
|
||||||
time(&now);
|
time(&now);
|
||||||
|
|
@ -27,39 +27,34 @@ void write_log(const char *filename, char *const argv[]) {
|
||||||
strdup(LOG_FILE); // Duplicate string as dirname might modify it
|
strdup(LOG_FILE); // Duplicate string as dirname might modify it
|
||||||
if (!log_file_path) {
|
if (!log_file_path) {
|
||||||
perror("strdup failed");
|
perror("strdup failed");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
char *log_dir = dirname(log_file_path);
|
char *log_dir = dirname(log_file_path);
|
||||||
if (!log_dir) {
|
if (!log_dir) {
|
||||||
perror("dirname failed");
|
perror("dirname failed");
|
||||||
free(log_file_path);
|
free(log_file_path);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the directory exists and is writable
|
// Check if the directory exists and is writable
|
||||||
if (access(log_dir, W_OK) != 0) {
|
if (access(log_dir, F_OK) != 0) {
|
||||||
if (errno == ENOENT) {
|
DEBUG_LOG("Log directory '%s' does not exist", log_dir);
|
||||||
DEBUG_LOG("Log directory '%s' does not exist, creating it...",
|
free(log_file_path);
|
||||||
log_dir);
|
return -1;
|
||||||
// Create the directory with appropriate permissions (e.g., 0755)
|
|
||||||
if (mkdir(log_dir, 0755) == -1) {
|
|
||||||
perror("mkdir failed");
|
|
||||||
free(log_file_path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DEBUG_LOG("Log directory '%s' created successfully.", log_dir);
|
|
||||||
} else {
|
|
||||||
perror("access failed for log directory");
|
|
||||||
free(log_file_path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (access(log_dir, W_OK) != 0) {
|
||||||
|
DEBUG_LOG("Log directory '%s' is not writable", log_dir);
|
||||||
|
free(log_file_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
free(log_file_path); // Free the duplicated string
|
free(log_file_path); // Free the duplicated string
|
||||||
|
|
||||||
FILE *log = fopen(LOG_FILE, "a");
|
FILE *log = fopen(LOG_FILE, "a");
|
||||||
if (!log) {
|
if (!log) {
|
||||||
perror("fopen failed for log file");
|
perror("fopen failed for log file");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(log, "[%s] Command: %s\n", ctime(&now), filename);
|
fprintf(log, "[%s] Command: %s\n", ctime(&now), filename);
|
||||||
|
|
@ -69,6 +64,7 @@ void write_log(const char *filename, char *const argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(log);
|
fclose(log);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 全局变量记录子进程 PID
|
// // 全局变量记录子进程 PID
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef LOGGING_H
|
#ifndef LOGGING_H
|
||||||
#define LOGGING_H
|
#define LOGGING_H
|
||||||
|
|
||||||
void write_log(const char *filename, char *const argv[]);
|
int write_log(const char *filename, char *const argv[]);
|
||||||
void duplicate_output_to_log();
|
void duplicate_output_to_log();
|
||||||
|
|
||||||
#endif // LOGGING_H
|
#endif // LOGGING_H
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include "signal_handlers.h"
|
#include "signal_handlers.h"
|
||||||
#include "terminal_utils.h"
|
#include "terminal_utils.h"
|
||||||
|
|
||||||
FILE *log_file = NULL;
|
FILE* log_file = NULL;
|
||||||
pid_t child_pid;
|
pid_t child_pid;
|
||||||
int child_status = -1;
|
int child_status = -1;
|
||||||
static int pty_master_fd = -1;
|
static int pty_master_fd = -1;
|
||||||
|
|
@ -66,7 +66,7 @@ void handle_sigwinch(int sig) {
|
||||||
// exit(1);
|
// exit(1);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
int dupIO(const char* filename, char* const argv[], char* const envp[]) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int master;
|
int master;
|
||||||
int stderr_pipe[2];
|
int stderr_pipe[2];
|
||||||
|
|
@ -79,7 +79,7 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
// 创建stderr的pipe
|
// 创建stderr的pipe
|
||||||
if (pipe(stderr_pipe) < 0) {
|
if (pipe(stderr_pipe) < 0) {
|
||||||
perror("pipe failed");
|
perror("pipe failed");
|
||||||
exit(1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
signal(SIGINT, handle_sigint);
|
signal(SIGINT, handle_sigint);
|
||||||
|
|
@ -88,7 +88,9 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
|
|
||||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) < 0) {
|
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) < 0) {
|
||||||
perror("ioctl TIOCGWINSZ failed");
|
perror("ioctl TIOCGWINSZ failed");
|
||||||
exit(1);
|
close(stderr_pipe[0]);
|
||||||
|
close(stderr_pipe[1]);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid = forkpty(&master, NULL, &term, &win);
|
pid = forkpty(&master, NULL, &term, &win);
|
||||||
|
|
@ -101,7 +103,9 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
perror("forkpty failed");
|
perror("forkpty failed");
|
||||||
exit(1);
|
close(stderr_pipe[0]);
|
||||||
|
close(stderr_pipe[1]);
|
||||||
|
return -1;
|
||||||
} else if (pid == 0) {
|
} else if (pid == 0) {
|
||||||
// 子进程
|
// 子进程
|
||||||
DEBUG_LOG("Child process ready.");
|
DEBUG_LOG("Child process ready.");
|
||||||
|
|
@ -113,11 +117,12 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
// 重定向stderr到pipe写端
|
// 重定向stderr到pipe写端
|
||||||
if (dup2(stderr_pipe[1], STDERR_FILENO) < 0) {
|
if (dup2(stderr_pipe[1], STDERR_FILENO) < 0) {
|
||||||
perror("dup2 failed");
|
perror("dup2 failed");
|
||||||
exit(1);
|
close(stderr_pipe[1]);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
close(stderr_pipe[1]);
|
close(stderr_pipe[1]);
|
||||||
|
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 父进程
|
// 父进程
|
||||||
|
|
@ -128,12 +133,22 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
fcntl(stderr_pipe[0], F_SETFL, O_NONBLOCK);
|
fcntl(stderr_pipe[0], F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
// 获取stderr日志文件名
|
// 获取stderr日志文件名
|
||||||
const char *stdout_log = GET_LOG_FILE(child_pid, 1);
|
const char* stdout_log = GET_LOG_FILE(child_pid, 1);
|
||||||
const char *stderr_log = GET_LOG_FILE(child_pid, 0);
|
const char* stderr_log = GET_LOG_FILE(child_pid, 0);
|
||||||
DEBUG_LOG("Ready to handle IO");
|
DEBUG_LOG("Ready to handle IO");
|
||||||
handle_io(master, stderr_pipe[0], stdout_log,
|
int ret = handle_io(master, stderr_pipe[0], stdout_log,
|
||||||
stderr_log); // 需要修改handle_io函数签名,传入stderr_pipe读端
|
stderr_log); // 需要修改handle_io函数签名,传入stderr_pipe读端
|
||||||
|
if (ret != 0) {
|
||||||
|
DEBUG_LOG("handle_io returned error: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复终端设置
|
||||||
|
restore_terminal();
|
||||||
|
|
||||||
|
int stdout_fd = STDOUT_FILENO;
|
||||||
|
int exit_code = 1;
|
||||||
|
|
||||||
// 打印子进程状态
|
// 打印子进程状态
|
||||||
if (child_status != -1) {
|
if (child_status != -1) {
|
||||||
if (WIFEXITED(child_status)) {
|
if (WIFEXITED(child_status)) {
|
||||||
|
|
@ -142,20 +157,20 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
code);
|
code);
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
int success =
|
int success =
|
||||||
send_exec_params(filename, argv, envp, stderr_log);
|
seeking_solutions(filename, argv, envp, stderr_log, &stdout_fd);
|
||||||
if (success != 0) {
|
if (success != 0) {
|
||||||
fprintf(stderr, "向服务器请求解决方案失败! \n");
|
fprintf(stderr, "向服务器请求解决方案失败! \n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exit(code);
|
exit_code = code;
|
||||||
} else if (WIFSIGNALED(child_status)) {
|
} else if (WIFSIGNALED(child_status)) {
|
||||||
DEBUG_LOG("\nChild process terminated abnormally by signal %d\n",
|
DEBUG_LOG("\nChild process terminated abnormally by signal %d\n",
|
||||||
WTERMSIG(child_status));
|
WTERMSIG(child_status));
|
||||||
int success = send_exec_params(filename, argv, envp, stderr_log);
|
int success = seeking_solutions(filename, argv, envp, stderr_log, &stdout_fd);
|
||||||
if (success != 0) {
|
if (success != 0) {
|
||||||
fprintf(stderr, "向服务器请求解决方案失败! \n");
|
fprintf(stderr, "向服务器请求解决方案失败! \n");
|
||||||
}
|
}
|
||||||
exit(128 + WTERMSIG(child_status));
|
exit_code = 128 + WTERMSIG(child_status);
|
||||||
} else if (WIFSTOPPED(child_status)) {
|
} else if (WIFSTOPPED(child_status)) {
|
||||||
DEBUG_LOG("\nChild process stopped by signal %d\n",
|
DEBUG_LOG("\nChild process stopped by signal %d\n",
|
||||||
WSTOPSIG(child_status));
|
WSTOPSIG(child_status));
|
||||||
|
|
@ -165,5 +180,5 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
close(master);
|
close(master);
|
||||||
close(stderr_pipe[0]);
|
close(stderr_pipe[0]);
|
||||||
|
|
||||||
exit(1);
|
exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
#define BUFFER_SIZE 1024
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
extern FILE *log_file;
|
extern FILE* log_file;
|
||||||
extern pid_t child_pid;
|
extern pid_t child_pid;
|
||||||
extern int child_status;
|
extern int child_status;
|
||||||
|
|
||||||
void dupIO(const char *filename, char *const argv[], char *const envp[]);
|
int dupIO(const char* filename, char* const argv[], char* const envp[]);
|
||||||
void print_child_status(void);
|
void print_child_status(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "pty_dup.h"
|
#include "pty_dup.h"
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
// 全局变量保存原始终端设置
|
||||||
|
struct termios orig_term;
|
||||||
|
int term_saved = 0;
|
||||||
|
|
||||||
void setup_termios(struct termios *term) {
|
void setup_termios(struct termios *term) {
|
||||||
// 初始化终端设置
|
// 初始化终端设置
|
||||||
|
|
@ -48,15 +54,16 @@ void setup_termios(struct termios *term) {
|
||||||
cfsetospeed(term, B38400);
|
cfsetospeed(term, B38400);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_io(int master_fd, int stderr_fd, const char *stdout_log,
|
int handle_io(int master_fd, int stderr_fd, const char *stdout_log,
|
||||||
const char *stderr_log) {
|
const char *stderr_log) {
|
||||||
struct termios orig_term, raw_term;
|
struct termios raw_term;
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[BUFFER_SIZE];
|
||||||
struct pollfd fds[3]; // 增加一个pollfd用于stderr
|
struct pollfd fds[3]; // 增加一个pollfd用于stderr
|
||||||
|
|
||||||
// 保存原始终端设置
|
// 保存原始终端设置
|
||||||
DEBUG_LOG("Saving original config.");
|
DEBUG_LOG("Saving original config.");
|
||||||
tcgetattr(STDIN_FILENO, &orig_term);
|
tcgetattr(STDIN_FILENO, &orig_term);
|
||||||
|
term_saved = 1;
|
||||||
|
|
||||||
// 设置原始模式
|
// 设置原始模式
|
||||||
DEBUG_LOG("Setting origin mode.");
|
DEBUG_LOG("Setting origin mode.");
|
||||||
|
|
@ -77,13 +84,30 @@ void handle_io(int master_fd, int stderr_fd, const char *stdout_log,
|
||||||
fds[2].fd = stderr_fd; // 新增stderr的fd
|
fds[2].fd = stderr_fd; // 新增stderr的fd
|
||||||
fds[2].events = POLLIN;
|
fds[2].events = POLLIN;
|
||||||
|
|
||||||
// 打开两个日志文件
|
// 检查日志目录权限,如果没有权限就直接返回
|
||||||
|
char *stdout_log_copy = strdup(stdout_log);
|
||||||
|
if (!stdout_log_copy) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *log_dir = dirname(stdout_log_copy);
|
||||||
|
|
||||||
|
// 检查目录是否存在以及是否有写权限
|
||||||
|
if (access(log_dir, F_OK) != 0 || access(log_dir, W_OK) != 0) {
|
||||||
|
free(stdout_log_copy);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(stdout_log_copy);
|
||||||
|
|
||||||
|
// 打开日志文件
|
||||||
int stdout_log_fd = open(stdout_log, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
int stdout_log_fd = open(stdout_log, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||||
int stderr_log_fd = open(stderr_log, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
int stderr_log_fd = open(stderr_log, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||||
|
|
||||||
if (stdout_log_fd == -1 || stderr_log_fd == -1) {
|
if (stdout_log_fd == -1 || stderr_log_fd == -1) {
|
||||||
perror("Failed to open log file");
|
if (stdout_log_fd >= 0) close(stdout_log_fd);
|
||||||
return;
|
if (stderr_log_fd >= 0) close(stderr_log_fd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
@ -209,7 +233,14 @@ void handle_io(int master_fd, int stderr_fd, const char *stdout_log,
|
||||||
|
|
||||||
// 在恢复终端设置之前清除可能的未完成输出
|
// 在恢复终端设置之前清除可能的未完成输出
|
||||||
tcflush(STDIN_FILENO, TCIFLUSH);
|
tcflush(STDIN_FILENO, TCIFLUSH);
|
||||||
|
|
||||||
// 恢复终端设置
|
return 0;
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void restore_terminal() {
|
||||||
|
if (term_saved) {
|
||||||
|
DEBUG_LOG("Restoring terminal settings.");
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
|
||||||
|
term_saved = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,10 @@
|
||||||
#ifndef TERMINAL_UTILS_H
|
#ifndef TERMINAL_UTILS_H
|
||||||
#define TERMINAL_UTILS_H
|
#define TERMINAL_UTILS_H
|
||||||
|
|
||||||
void setup_termios(struct termios *term);
|
void setup_termios(struct termios* term);
|
||||||
void handle_io(int master_fd, int stderr_fd, const char *stdout_log,
|
int handle_io(int master_fd, int stderr_fd, const char* stdout_log,
|
||||||
const char *stderr_log);
|
const char* stderr_log);
|
||||||
|
|
||||||
|
void restore_terminal();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -55,7 +55,7 @@ int main(int argc, char *argv[]) {
|
||||||
printf("--- 服务端响应 ---\n");
|
printf("--- 服务端响应 ---\n");
|
||||||
|
|
||||||
// 调用客户端函数
|
// 调用客户端函数
|
||||||
int result = send_exec_params(filename, test_argv, test_envp, log_path);
|
int result = seeking_solutions(filename, test_argv, test_envp, log_path, &STDOUT_FILENO);
|
||||||
|
|
||||||
printf("\n--- 响应结束 ---\n");
|
printf("\n--- 响应结束 ---\n");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
#include "../src/client.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 并发测试客户端 - 用于测试多个客户端并发访问服务端
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* ./test_concurrent_client <log_file_path> <线程数> [延迟ms]
|
||||||
|
*
|
||||||
|
* 示例:
|
||||||
|
* ./test_concurrent_client /tmp/test_error.log 10 # 10个并发线程
|
||||||
|
* ./test_concurrent_client /tmp/test_error.log 20 100 # 20个线程,每个延迟100ms
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 线程参数结构
|
||||||
|
typedef struct {
|
||||||
|
int thread_id;
|
||||||
|
const char *log_path;
|
||||||
|
int delay_ms; // 启动前延迟(毫秒)
|
||||||
|
int success; // 执行结果
|
||||||
|
} thread_args_t;
|
||||||
|
|
||||||
|
// 统计信息
|
||||||
|
typedef struct {
|
||||||
|
int total;
|
||||||
|
int success;
|
||||||
|
int failed;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
} stats_t;
|
||||||
|
|
||||||
|
static stats_t g_stats = {0, 0, 0, PTHREAD_MUTEX_INITIALIZER};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作线程函数
|
||||||
|
*/
|
||||||
|
void *worker_thread(void *arg) {
|
||||||
|
thread_args_t *args = (thread_args_t *)arg;
|
||||||
|
|
||||||
|
// 如果设置了延迟,先等待
|
||||||
|
if (args->delay_ms > 0) {
|
||||||
|
usleep(args->delay_ms * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟不同的命令和参数
|
||||||
|
char *commands[] = {
|
||||||
|
"/usr/bin/python3",
|
||||||
|
"/usr/bin/ls",
|
||||||
|
"/bin/bash",
|
||||||
|
"/usr/bin/grep"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *filename = commands[args->thread_id % 4];
|
||||||
|
|
||||||
|
// 构造测试参数
|
||||||
|
char arg1[64], arg2[64];
|
||||||
|
snprintf(arg1, sizeof(arg1), "arg1_thread_%d", args->thread_id);
|
||||||
|
snprintf(arg2, sizeof(arg2), "arg2_thread_%d", args->thread_id);
|
||||||
|
|
||||||
|
char *test_argv[] = {
|
||||||
|
"command",
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
char *test_envp[] = {
|
||||||
|
"PATH=/usr/bin:/bin",
|
||||||
|
"HOME=/home/test",
|
||||||
|
"THREAD_ID=test",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
printf("[线程 %d] 开始发送请求: %s\n", args->thread_id, filename);
|
||||||
|
|
||||||
|
// 调用客户端函数
|
||||||
|
int result = seeking_solutions(filename, test_argv, test_envp, args->log_path, &STDOUT_FILENO);
|
||||||
|
|
||||||
|
args->success = (result == 0);
|
||||||
|
|
||||||
|
// 更新统计信息
|
||||||
|
pthread_mutex_lock(&g_stats.mutex);
|
||||||
|
if (result == 0) {
|
||||||
|
g_stats.success++;
|
||||||
|
printf("[线程 %d] ✓ 成功\n", args->thread_id);
|
||||||
|
} else {
|
||||||
|
g_stats.failed++;
|
||||||
|
printf("[线程 %d] ✗ 失败 (返回值: %d)\n", args->thread_id, result);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&g_stats.mutex);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印使用说明
|
||||||
|
*/
|
||||||
|
void print_usage(const char *prog_name) {
|
||||||
|
fprintf(stderr, "用法: %s <log_file_path> <线程数> [延迟ms]\n", prog_name);
|
||||||
|
fprintf(stderr, "\n参数说明:\n");
|
||||||
|
fprintf(stderr, " log_file_path - 日志文件路径\n");
|
||||||
|
fprintf(stderr, " 线程数 - 并发线程数量 (1-1000)\n");
|
||||||
|
fprintf(stderr, " 延迟ms - 可选,每个线程启动前的延迟(毫秒)\n");
|
||||||
|
fprintf(stderr, "\n示例:\n");
|
||||||
|
fprintf(stderr, " %s /tmp/test.log 10 # 10个并发线程\n", prog_name);
|
||||||
|
fprintf(stderr, " %s /tmp/test.log 50 100 # 50个线程,每个延迟100ms\n", prog_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc < 3) {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *log_path = argv[1];
|
||||||
|
int num_threads = atoi(argv[2]);
|
||||||
|
int delay_ms = (argc >= 4) ? atoi(argv[3]) : 0;
|
||||||
|
|
||||||
|
// 验证参数
|
||||||
|
if (num_threads <= 0 || num_threads > 1000) {
|
||||||
|
fprintf(stderr, "错误: 线程数必须在 1-1000 之间\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("====== 并发测试开始 ======\n");
|
||||||
|
printf("socket地址: %s\n", SOCKET_PATH);
|
||||||
|
printf("日志路径: %s\n", log_path);
|
||||||
|
printf("并发线程数: %d\n", num_threads);
|
||||||
|
printf("启动延迟: %d ms\n", delay_ms);
|
||||||
|
printf("========================\n\n");
|
||||||
|
|
||||||
|
// 分配线程资源
|
||||||
|
pthread_t *threads = malloc(sizeof(pthread_t) * num_threads);
|
||||||
|
thread_args_t *args = malloc(sizeof(thread_args_t) * num_threads);
|
||||||
|
|
||||||
|
if (!threads || !args) {
|
||||||
|
fprintf(stderr, "错误: 内存分配失败\n");
|
||||||
|
free(threads);
|
||||||
|
free(args);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化统计信息
|
||||||
|
g_stats.total = num_threads;
|
||||||
|
g_stats.success = 0;
|
||||||
|
g_stats.failed = 0;
|
||||||
|
|
||||||
|
// 记录开始时间
|
||||||
|
struct timespec start_time, end_time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||||
|
|
||||||
|
// 创建并启动所有线程
|
||||||
|
printf("创建 %d 个线程...\n\n", num_threads);
|
||||||
|
for (int i = 0; i < num_threads; i++) {
|
||||||
|
args[i].thread_id = i;
|
||||||
|
args[i].log_path = log_path;
|
||||||
|
args[i].delay_ms = delay_ms;
|
||||||
|
args[i].success = 0;
|
||||||
|
|
||||||
|
if (pthread_create(&threads[i], NULL, worker_thread, &args[i]) != 0) {
|
||||||
|
fprintf(stderr, "错误: 创建线程 %d 失败\n", i);
|
||||||
|
// 继续创建其他线程
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待所有线程完成
|
||||||
|
printf("等待所有线程完成...\n\n");
|
||||||
|
for (int i = 0; i < num_threads; i++) {
|
||||||
|
pthread_join(threads[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录结束时间
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &end_time);
|
||||||
|
|
||||||
|
// 计算耗时
|
||||||
|
double elapsed = (end_time.tv_sec - start_time.tv_sec) +
|
||||||
|
(end_time.tv_nsec - start_time.tv_nsec) / 1e9;
|
||||||
|
|
||||||
|
// 打印统计结果
|
||||||
|
printf("\n====== 测试结果统计 ======\n");
|
||||||
|
printf("总线程数: %d\n", g_stats.total);
|
||||||
|
printf("成功: %d (%.1f%%)\n", g_stats.success,
|
||||||
|
g_stats.total > 0 ? (g_stats.success * 100.0 / g_stats.total) : 0);
|
||||||
|
printf("失败: %d (%.1f%%)\n", g_stats.failed,
|
||||||
|
g_stats.total > 0 ? (g_stats.failed * 100.0 / g_stats.total) : 0);
|
||||||
|
printf("总耗时: %.3f 秒\n", elapsed);
|
||||||
|
printf("平均每个请求: %.3f 毫秒\n",
|
||||||
|
g_stats.total > 0 ? (elapsed * 1000 / g_stats.total) : 0);
|
||||||
|
printf("吞吐量: %.1f 请求/秒\n",
|
||||||
|
elapsed > 0 ? (g_stats.total / elapsed) : 0);
|
||||||
|
printf("========================\n");
|
||||||
|
|
||||||
|
// 清理资源
|
||||||
|
free(threads);
|
||||||
|
free(args);
|
||||||
|
pthread_mutex_destroy(&g_stats.mutex);
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
if (g_stats.failed > 0) {
|
||||||
|
printf("\n✗ 测试完成,但有 %d 个请求失败\n", g_stats.failed);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
printf("\n✓ 所有测试通过!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue