log模块
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <thread>
static void doLog(const char *file, int line, const char *format, ...)
{
char *buffer = nullptr;
va_list ap;
va_start(ap, format);
vasprintf(&buffer, format, ap);
va_end(ap);
fprintf(stderr, "[%s:%04d] [%05ld] -- %s\n", file, line, syscall(SYS_gettid), buffer);
if (buffer) {
::free(buffer);
buffer = nullptr;
}
}
#define LOG(...) do { \
doLog(__FILE__, __LINE__, __VA_ARGS__); \
} while (0);
自定义cmdInfo_t数据结构
typedef struct cmdInfo {
FILE *pfile;
int fd;
int childpid;
} cmdInfo_t;
读取数据接口
int systemRead(cmdInfo_t *info, char *buffer, int len)
{
int retval = -1;
if (!info || !buffer) {
return retval;
}
memset(buffer, 0, len);
do {
retval = read(info->fd, buffer, len);
} while (-1 == retval && EINTR == errno);
return retval;
}
int systemGetline(cmdInfo_t *info, char **line, size_t *len)
{
if (!info || !info->pfile || !line || !len) {
return -1;
}
return getline(line, len, info->pfile);
}
char *systemGets(cmdInfo_t *info, char *dst, int len)
{
if (!info || !info->pfile || !dst) {
return nullptr;
}
return fgets(dst, len, info->pfile);
}
写数据接口
int systemWrite(cmdInfo_t *info, const char *buffer, int len)
{
int retval = -1;
if (!info || !buffer) {
return retval;
}
do {
retval = write(info->fd, buffer, len);
} while (-1 == retval && EINTR == errno);
return retval;
}
int systemWrite2(cmdInfo_t *info, const char *format, ...)
{
int retval = -1;
char *buffer = nullptr;
if (!info || !format) {
return retval;
}
va_list ap;
va_start(ap, format);
vasprintf(&buffer, format, ap);
va_end(ap);
if (buffer) {
do {
retval = write(info->fd, buffer, strlen(buffer));
} while (-1 == retval && EINTR == errno);
::free(buffer);
}
return retval;
}
创建cmdInfo_t对象
cmdInfo_t *systemOpen(const char *command, const char *mode, char **reason)
{
int pipefds[2] = {-1};
bool doRead = false;
bool doWrite = false;
int doCloexec = false;
int parentEnd = -1;
int parentEndDup = -1;
int childEnd = -1;
int childPid = -1;
cmdInfo_t *info = nullptr;
char *mode2 = strdup(mode);
while ('\0' != *mode) {
switch (*mode++) {
case 'r':
doRead = true;
break;
case 'w':
doWrite = true;
break;
case 'e':
doCloexec = true;
break;
default:
if (reason) {
*reason = strdup("Input mode error, please input correct mode[r|w|e]");
}
return nullptr;
}
}
#if 0
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipefds)) {
if (reason) {
*reason = strdup("socketpair failure!");
}
return nullptr;
}
#endif
if (pipe2(pipefds, O_CLOEXEC)) {
if (reason) {
*reason = strdup("pipe failure!");
}
return nullptr;
}
if (doRead) {
parentEnd = pipefds[0];
childEnd = pipefds[1];
} else {
parentEnd = pipefds[1];
childEnd = pipefds[0];
}
info = new cmdInfo_t;
if (nullptr == info) {
if (reason) {
*reason = strdup("alloc memory failure!");
}
goto error;
}
info->childpid = childPid = fork();
if (childPid < 0) {
goto error;
} else if (0 == childPid) {
for (int idx = 3; idx < 1024; idx++) {
if (idx != parentEnd && idx != childEnd) {
close(idx);
}
}
int childStdEnd = doRead ? 1 : 0;
if (childEnd != childStdEnd) {
dup2(childEnd, childStdEnd);
} else {
fcntl(childEnd, F_SETFD, 0);
}
execl("/bin/sh", "sh", "-c", command, (char *)0);
exit(127);
}
close(childEnd);
info->fd = parentEnd;
parentEndDup = dup(parentEnd);
info->pfile = fdopen(parentEndDup, mode2);
if (!info->pfile) {
if (reason) {
*reason = strdup("Conversion of FD to stream failed");
}
goto error;
}
if (!doCloexec) {
fcntl(parentEnd, F_SETFD, 0);
}
if (mode2) {
::free(mode2);
mode2 = nullptr;
}
return info;
error:
if (mode2) {
::free(mode2);
mode2 = nullptr;
}
if (childEnd > 0) {
close(childEnd);
childEnd = -1;
}
if (parentEnd > 0) {
close(parentEnd);
parentEnd = -1;
}
if (parentEndDup > 0) {
close(parentEndDup);
parentEndDup = -1;
}
if (info) {
delete info;
info = nullptr;
}
return nullptr;
}
原理说明:
- systemOpen接口调用fork创建一个子进程
- 调用pipe创建一个管道,子进程会继承该管道。当以读方式打开时,子进程通过dup2系统调用将自身标准输出重定向到写管道,并关闭自身标准输出。
销毁cmdInfo_t对象
int systemClose(cmdInfo_t *info)
{
int status = -1;
pid_t waitpPid = -1;
if (nullptr == info) {
return -1;
}
if (info->fd > 0) {
close(info->fd);
info->fd = -1;
}
if (info->pfile) {
fclose(info->pfile);
info->pfile = nullptr;
}
do {
waitpPid = waitpid(info->childpid, &status, 0);
} while (-1 == waitpPid && EINTR == errno);
delete info;
return 0;
}
测试用例
void threadTaskPing(const char *command, const char *mode)
{
LOG("%s enter ...", __FUNCTION__);
char *reason = nullptr;
cmdInfo_t *info = systemOpen(command, mode, &reason);
if (info) {
char *line = nullptr;
size_t len = 0;
while (-1 != systemGetline(info, &line, &len)) {
LOG("%s", line);
::free(line);
line = nullptr;
}
if (line) {
::free(line);
line = nullptr;
}
#if 0
char line[1024] = {0};
while ( cmdRead(info, line, sizeof(line) - 1) > 0) {
LOG("%s", line);
}
#endif
systemClose(info);
}
if (reason) {
LOG("%s", reason);
::free(reason);
reason = nullptr;
}
}
void threadTaskTest(const char *command, const char *mode)
{
LOG("%s enter ...", __FUNCTION__);
cmdInfo_t *info = systemOpen(command, mode, nullptr);
if (info) {
int tried = 0;
do {
systemWrite2(info, "This is a %d", tried);
} while (++tried < 50);
systemClose(info);
}
}
int main(int argc, const char *argv[])
{
std::thread threads[100] = {};
for (int i = 0; i < sizeof(threads) / sizeof(threads[0]); i++) {
if (0 == i % 2) {
threads[i] = std::thread(threadTaskPing, "ping -c 10 www.baidu.com", "r");
} else {
threads[i] = std::thread(threadTaskTest, "/Asterisk/test4", "w");
}
}
for (auto & t : threads) {
if (t.joinable()) {
t.join();
}
}
sleep(20);
return 0;
}