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_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
|
||||
ifeq ($(DEBUG),1)
|
||||
CFLAGS += -DDEBUG -g
|
||||
|
|
@ -40,12 +45,14 @@ ifeq ($(NO_CONFIG_CHECK),1)
|
|||
CFLAGS += -DNO_CONFIG_CHECK
|
||||
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)
|
||||
|
||||
test_client: pre_build $(TEST_CLIENT)
|
||||
|
||||
test_concurrent_client: pre_build $(TEST_CONCURRENT_CLIENT)
|
||||
|
||||
pre_build:
|
||||
ifeq ($(DEBUG),1)
|
||||
@echo "Building with debug flags..."
|
||||
|
|
@ -77,6 +84,10 @@ $(TEST_CLIENT): $(TEST_CLIENT_SRC) $(TEST_CLIENT_DEPS)
|
|||
@mkdir -p $(BUILD_DIR)
|
||||
$(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:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
|
|
|
|||
21
src/client.c
21
src/client.c
|
|
@ -1,5 +1,4 @@
|
|||
#include "client.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
|
@ -9,10 +8,12 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#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;
|
||||
// 先读取消息长度
|
||||
if (read(sock, &messageLen, sizeof(messageLen)) != sizeof(messageLen)) {
|
||||
|
|
@ -35,8 +36,8 @@ ssize_t readMessage(int sock, char *buffer, size_t maxSize) {
|
|||
return messageLen;
|
||||
}
|
||||
|
||||
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) {
|
||||
char abs_path[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);
|
||||
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));
|
||||
close(sock);
|
||||
return -1;
|
||||
|
|
@ -146,8 +147,14 @@ int send_exec_params(const char *filename, char *const argv[],
|
|||
|
||||
strncpy(display_buffer, buffer, BUFFER_SIZE - 1);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@
|
|||
#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
|
||||
|
|
|
|||
|
|
@ -22,18 +22,17 @@
|
|||
|
||||
#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_OUT_FILE "./logs/execve_out.log"
|
||||
// #define LOG_FILE "./logs/execve.log"
|
||||
// #define LOG_OUT_FILE "./logs/execve_out.log"
|
||||
|
||||
#else
|
||||
// #else
|
||||
|
||||
#define CONFIG_FILE ""
|
||||
#define LOG_FILE "/etc/exec_hook/logs/execve.log"
|
||||
#define LOG_OUT_FILE "/etc/exec_hook/logs/execve_out.log"
|
||||
|
||||
#endif
|
||||
#define LOG_FILE "/var/log/bash-smart/execve.log"
|
||||
#define LOG_OUT_FILE "/var/log/bash-smart/execve_out.log"
|
||||
|
||||
// #endif
|
||||
|
||||
#define MAX_PATH_LEN 256
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "init_cleanup.h"
|
||||
#include "terminal_utils.h"
|
||||
#include "logging.h"
|
||||
#include "pty_dup.h"
|
||||
#include "rules.h"
|
||||
|
|
@ -238,10 +239,29 @@ int enhance_execve(const char *filename, char *const argv[],
|
|||
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
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
#include "init_cleanup.h"
|
||||
#include "exec_hook.h"
|
||||
#include "debug.h"
|
||||
#include "pty_dup.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/shm.h>
|
||||
#include <termios.h>
|
||||
|
||||
// // Constructor, executed when the library is loaded
|
||||
// __attribute__((constructor)) static void initialize() {
|
||||
|
|
@ -15,7 +17,7 @@
|
|||
// }
|
||||
|
||||
// Destructor, executed when the library is unloaded
|
||||
__attribute__((destructor)) void cleanup_shared_memory() {
|
||||
__attribute__((destructor)) void cleanup() {
|
||||
DEBUG_LOG("execve_intercept library unloaded.");
|
||||
// Log output paths
|
||||
DEBUG_LOG("Log file: %s", LOG_FILE);
|
||||
|
|
@ -29,10 +31,26 @@ __attribute__((destructor)) void cleanup_shared_memory() {
|
|||
// }
|
||||
// shared_config = NULL;
|
||||
// }
|
||||
#ifdef DEBUG
|
||||
print_stacktrace();
|
||||
#endif
|
||||
// #ifdef DEBUG
|
||||
// print_stacktrace();
|
||||
// #endif
|
||||
// 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
|
||||
// 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
|
||||
|
||||
// void initialize();
|
||||
__attribute__((destructor)) void cleanup_shared_memory();
|
||||
__attribute__((destructor)) void cleanup();
|
||||
|
||||
#endif // INIT_CLEANUP_H
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
#include "exec_hook.h"
|
||||
|
||||
// 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);
|
||||
time_t 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
|
||||
if (!log_file_path) {
|
||||
perror("strdup failed");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
char *log_dir = dirname(log_file_path);
|
||||
if (!log_dir) {
|
||||
perror("dirname failed");
|
||||
free(log_file_path);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if the directory exists and is writable
|
||||
if (access(log_dir, W_OK) != 0) {
|
||||
if (errno == ENOENT) {
|
||||
DEBUG_LOG("Log directory '%s' does not exist, creating it...",
|
||||
log_dir);
|
||||
// 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, F_OK) != 0) {
|
||||
DEBUG_LOG("Log directory '%s' does not exist", log_dir);
|
||||
free(log_file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
FILE *log = fopen(LOG_FILE, "a");
|
||||
if (!log) {
|
||||
perror("fopen failed for log file");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(log, "[%s] Command: %s\n", ctime(&now), filename);
|
||||
|
|
@ -69,6 +64,7 @@ void write_log(const char *filename, char *const argv[]) {
|
|||
}
|
||||
|
||||
fclose(log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// // 全局变量记录子进程 PID
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef 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();
|
||||
|
||||
#endif // LOGGING_H
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
#include "signal_handlers.h"
|
||||
#include "terminal_utils.h"
|
||||
|
||||
FILE *log_file = NULL;
|
||||
FILE* log_file = NULL;
|
||||
pid_t child_pid;
|
||||
int child_status = -1;
|
||||
static int pty_master_fd = -1;
|
||||
|
|
@ -66,7 +66,7 @@ void handle_sigwinch(int sig) {
|
|||
// 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;
|
||||
int master;
|
||||
int stderr_pipe[2];
|
||||
|
|
@ -79,7 +79,7 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
|||
// 创建stderr的pipe
|
||||
if (pipe(stderr_pipe) < 0) {
|
||||
perror("pipe failed");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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) {
|
||||
perror("ioctl TIOCGWINSZ failed");
|
||||
exit(1);
|
||||
close(stderr_pipe[0]);
|
||||
close(stderr_pipe[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = forkpty(&master, NULL, &term, &win);
|
||||
|
|
@ -101,7 +103,9 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
|||
|
||||
if (pid < 0) {
|
||||
perror("forkpty failed");
|
||||
exit(1);
|
||||
close(stderr_pipe[0]);
|
||||
close(stderr_pipe[1]);
|
||||
return -1;
|
||||
} else if (pid == 0) {
|
||||
// 子进程
|
||||
DEBUG_LOG("Child process ready.");
|
||||
|
|
@ -113,11 +117,12 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
|||
// 重定向stderr到pipe写端
|
||||
if (dup2(stderr_pipe[1], STDERR_FILENO) < 0) {
|
||||
perror("dup2 failed");
|
||||
exit(1);
|
||||
close(stderr_pipe[1]);
|
||||
return -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);
|
||||
|
||||
// 获取stderr日志文件名
|
||||
const char *stdout_log = GET_LOG_FILE(child_pid, 1);
|
||||
const char *stderr_log = GET_LOG_FILE(child_pid, 0);
|
||||
const char* stdout_log = GET_LOG_FILE(child_pid, 1);
|
||||
const char* stderr_log = GET_LOG_FILE(child_pid, 0);
|
||||
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读端
|
||||
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 (WIFEXITED(child_status)) {
|
||||
|
|
@ -142,20 +157,20 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
|||
code);
|
||||
if (code != 0) {
|
||||
int success =
|
||||
send_exec_params(filename, argv, envp, stderr_log);
|
||||
seeking_solutions(filename, argv, envp, stderr_log, &stdout_fd);
|
||||
if (success != 0) {
|
||||
fprintf(stderr, "向服务器请求解决方案失败! \n");
|
||||
}
|
||||
}
|
||||
exit(code);
|
||||
exit_code = code;
|
||||
} else if (WIFSIGNALED(child_status)) {
|
||||
DEBUG_LOG("\nChild process terminated abnormally by signal %d\n",
|
||||
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) {
|
||||
fprintf(stderr, "向服务器请求解决方案失败! \n");
|
||||
}
|
||||
exit(128 + WTERMSIG(child_status));
|
||||
exit_code = 128 + WTERMSIG(child_status);
|
||||
} else if (WIFSTOPPED(child_status)) {
|
||||
DEBUG_LOG("\nChild process stopped by signal %d\n",
|
||||
WSTOPSIG(child_status));
|
||||
|
|
@ -165,5 +180,5 @@ void dupIO(const char *filename, char *const argv[], char *const envp[]) {
|
|||
close(master);
|
||||
close(stderr_pipe[0]);
|
||||
|
||||
exit(1);
|
||||
exit(exit_code);
|
||||
}
|
||||
|
|
@ -17,11 +17,11 @@
|
|||
|
||||
#define BUFFER_SIZE 1024
|
||||
|
||||
extern FILE *log_file;
|
||||
extern FILE* log_file;
|
||||
extern pid_t child_pid;
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@
|
|||
#include "config.h"
|
||||
#include "debug.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) {
|
||||
// 初始化终端设置
|
||||
|
|
@ -48,15 +54,16 @@ void setup_termios(struct termios *term) {
|
|||
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) {
|
||||
struct termios orig_term, raw_term;
|
||||
struct termios raw_term;
|
||||
char buffer[BUFFER_SIZE];
|
||||
struct pollfd fds[3]; // 增加一个pollfd用于stderr
|
||||
|
||||
// 保存原始终端设置
|
||||
DEBUG_LOG("Saving original config.");
|
||||
tcgetattr(STDIN_FILENO, &orig_term);
|
||||
term_saved = 1;
|
||||
|
||||
// 设置原始模式
|
||||
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].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 stderr_log_fd = open(stderr_log, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
|
||||
|
||||
if (stdout_log_fd == -1 || stderr_log_fd == -1) {
|
||||
perror("Failed to open log file");
|
||||
return;
|
||||
if (stdout_log_fd >= 0) close(stdout_log_fd);
|
||||
if (stderr_log_fd >= 0) close(stderr_log_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
|
@ -209,7 +233,14 @@ void handle_io(int master_fd, int stderr_fd, const char *stdout_log,
|
|||
|
||||
// 在恢复终端设置之前清除可能的未完成输出
|
||||
tcflush(STDIN_FILENO, TCIFLUSH);
|
||||
|
||||
// 恢复终端设置
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
#define TERMINAL_UTILS_H
|
||||
|
||||
void setup_termios(struct termios *term);
|
||||
void handle_io(int master_fd, int stderr_fd, const char *stdout_log,
|
||||
const char *stderr_log);
|
||||
void setup_termios(struct termios* term);
|
||||
int handle_io(int master_fd, int stderr_fd, const char* stdout_log,
|
||||
const char* stderr_log);
|
||||
|
||||
void restore_terminal();
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -55,7 +55,7 @@ int main(int argc, char *argv[]) {
|
|||
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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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