前言
在bash代码中,看到关闭了一个fd后,屏幕上才出现linux命令执行后的回显内容.
做了一个试验,实现了回显数据的捕获。
实现思路:
- dup2重定向stdin到自己建立的管道
- select管道,如果有数据就读取管道中的数据,并转发. 如果超时,就跳出.
做了好久的试验,终于达到预期的效果了:)
demo
// @file main.cpp
// @note on fedora22 view syslog use 'journalctl -f'
// 'tail -f /var/log/message' is invalid
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 日志级别 - 调试
#ifndef MYLOG_D
#define MYLOG_D(fmt, ...) \
do { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "LS_LOG", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
} while (0);
#endif // #ifndef MYLOG_D
void init(const char* psz_log_owner_name);
void uninit();
void proc_sig_term(int num);
int fn_test();
int main(int argc, char** argv)
{
char sz_buf[1024] = {'\0'};
#ifdef MAKE_FILE_MACRO__BIN_NAME
sprintf(sz_buf, "%s", MAKE_FILE_MACRO__BIN_NAME);
init(sz_buf);
MYLOG_D("MAKE_FILE_MACRO__BIN_NAME = [%s]", MAKE_FILE_MACRO__BIN_NAME);
#else
init(NULL);
#endif // #ifdef MAKE_FILE_MACRO__BIN_NAME
fn_test();
uninit();
MYLOG_D("THE END");
return EXIT_SUCCESS;
}
void init(const char* psz_log_owner_name)
{
int i = 0;
// daemon(0, 0);
openlog(((NULL != psz_log_owner_name) ? psz_log_owner_name : "my_syslog"), LOG_NOWAIT | LOG_PID, LOG_LOCAL1);
// clear screen (print 25 empty line)
for (i = 0; i < 25; i++) {
MYLOG_D("");
}
signal(SIGTERM, proc_sig_term);
}
void uninit()
{
closelog();
}
void proc_sig_term(int num)
{
MYLOG_D("SIGTERM = %d, num = %d", SIGTERM, num);
MYLOG_D("maybe can do some clean task before quit");
exit(1);
}
int fn_test()
{
int fd_stdin_org = -1;
int fd_stdout_org = -1;
int fd_stderr_org = -1;
char sz_buf[1024] = {'\0'};
// char* p_rd = NULL;
int n = 0;
int my_pipe[2] = {0};
int fd0 = -1;
int fd1 = -1;
FILE* fp = NULL;
const char* msg = NULL;
fd_set rfds;
struct timeval tv;
int retval;
MYLOG_D(">> fn_test()");
// printf("STDIN_FILENO = %d\n", STDIN_FILENO);
// printf("STDOUT_FILENO = %d\n", STDOUT_FILENO);
// printf("STDERR_FILENO = %d\n", STDERR_FILENO);
/*
STDIN_FILENO = 0
STDOUT_FILENO = 1
STDERR_FILENO = 2
*/
do {
// backup original STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO
fd_stdin_org = dup(STDIN_FILENO);
if (fd_stdin_org < 0) {
break;
}
fd_stdout_org = dup(STDOUT_FILENO);
if (fd_stdout_org < 0) {
break;
}
fd_stderr_org = dup(STDERR_FILENO);
if (fd_stderr_org < 0) {
break;
}
// if want view a file will be create later, can use 'tail -F obj_file_path_name'
msg = "1. write to stdout";
printf("%s\n", msg); // write to stdout
if(pipe(my_pipe) == -1)
{
break;
}
fd0 = dup2(my_pipe[0], STDIN_FILENO);
if (fd0 != STDIN_FILENO) {
break;
}
fd1 = dup2(my_pipe[1], STDOUT_FILENO);
if (fd1 != STDOUT_FILENO) {
break;
}
msg = "2. write to my_pipe[0]";
printf("%s\n", msg); // write to my_pipe
msg = "3. write to my_pipe[0]";
printf("%s\n", msg); // write to my_pipe
fp = fdopen(fd0, "r");
if (NULL != fp) {
FD_ZERO(&rfds);
FD_SET(fd0, &rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
do {
retval = select(1, &rfds, NULL, NULL, &tv);
if (retval < 0) {
// error happen
break;
} else if (retval > 0) {
// now, data is available
if (FD_ISSET(fd0, &rfds)) {
// method 1
memset(sz_buf, 0, sizeof(sz_buf));
// read 可以将用户多次输入的数据都读全
n = read(fd0, sz_buf, sizeof(sz_buf));
if (n < 0) {
break;
}
// MYLOG_D("read from pipe : %s", buf);
// method 2
// get user input
/*
// 输入多句,fgets只能读第一句, 再select时,就超时了, 第2句没读到
memset(sz_buf, 0, sizeof(sz_buf));
// 因为上面就写了1句,fgets执行一次就没数据了
// 为了防止阻塞, 不能直接调用fgets, 必须用select的超时机制来防止阻塞
p_rd = fgets(sz_buf, sizeof(sz_buf) - 1, fp); // if user not input and fp is valid, will block here
if (NULL == p_rd) {
break;
}
*/
} else {
// fd is invalid, break
break;
}
}
else {
// time out
break;
}
// trans user input to real stdout
// can trans user input to other from this point
write(fd_stdout_org, sz_buf, sizeof(sz_buf));
// read user input continue
} while (1);
fclose(fp);
fp = NULL;
}
close(my_pipe[0]);
my_pipe[0] = -1;
close(my_pipe[1]);
my_pipe[1] = -1;
} while (0);
if (fd0 >= 0) {
close(fd0);
fd0 = -1;
}
if (fd1 >= 0) {
close(fd1);
fd1 = -1;
}
if (fd_stdin_org >= 0) {
close(STDIN_FILENO);
dup2(fd_stdin_org, STDIN_FILENO); // restore original stdin
close(fd_stdin_org);
fd_stdin_org = -1;
}
if (fd_stdout_org >= 0) {
close(STDOUT_FILENO);
dup2(fd_stdout_org, STDOUT_FILENO); // restore original stdout
close(fd_stdout_org);
fd_stdout_org = -1;
}
if (fd_stderr_org >= 0) {
close(STDERR_FILENO);
dup2(fd_stdout_org, STDERR_FILENO); // restore original stderr
close(fd_stderr_org);
fd_stderr_org = -1;
}
return 0;
}