如何在 C 语言启动一个子进程,并获取 stdin, stdout, stderr 的句柄
前言
本文主要介绍一种在 C 语言中开进程执行命令行命令的方法,主要是参考 《Linux 高级环境编程》 实现。
主菜
测试代码:
m_exe_options *tp_opt = exe_alloc();
tp_opt->cmd = "adb shell \"logcat -d -v time\"";
tp_opt->flags = EXE_STDOUT | EXE_STDERR;
DLLOGD( "exe_parse_cmd ret:%d", exe_parse_cmd( tp_opt ) );
exe_show_opts( tp_opt );
exe_run( tp_opt );
exe_set_read_noblock( tp_opt );
do{
if( exe_isrunning( tp_opt ) ){
DLLOGD( "errno: %s", strerror( errno ) );
break;
}
char buf[40960];
memset( buf, 0, sizeof buf );
int rlen = exe_read_stdout( tp_opt, buf, sizeof buf - 1 );
if( rlen > 0 ){
DLLOGD( "stdout: %s", buf );
}else{
DLLOGD( "read 0" );
}
}while( 1 );
exe.h
#ifndef _EXE_H
#define _EXE_H
#include
#include
typedef struct{
const char *cmd;
#define EXE_STDIN (0x01)
#define EXE_STDOUT (0x02)
#define EXE_STDERR (0x04)
#define EXE_EXIT (0x80)
uint32_t flags;
// int mstdin;
// int mstdout;
// int mstderr;
pid_t pid;
} m_exe_options;
extern m_exe_options *exe_alloc( void );
extern int exe_alloc_free( m_exe_options *opt );
extern int exe_run( m_exe_options *iopt );
extern int exe_wait_exit( m_exe_options *iopt );
/*
* 0 isRunning
* -1 isStop or not exist
* */
extern int exe_isrunning( m_exe_options *iopt );
extern int exe_set_read_noblock( m_exe_options *opt );
extern int exe_read_stdout( m_exe_options *opt, char *rbuf, int rsize );
extern int exe_read_stderr( m_exe_options *opt, char *rbuf, int rsize );
extern int exe_write_stdin( m_exe_options *opt, char *wbuf, int wsize );
extern int exe_parse_cmd( m_exe_options *opt );
extern void exe_show_opts( m_exe_options *opt );
#endif // __EXE_H
exe.c
#include
#include
#include
#include
#include
#include
#include
#include "exe.h"
#include "Debug.h"
// =============================================================================
typedef unsigned char uint8_t;
typedef struct{
const char *cmd;
uint32_t flags;
// int mstdin;
// int mstdout;
// int mstderr;
pid_t pid;
char *executor;
char **args;
int argc;
int arg_size;
int fd_stdin[2];
int fd_stdout[2];
int fd_stderr[2];
} m_inner_options;
// =============================================================================
static void* lf_realloc_clear( void *p, int origin_size, int expend_size );
static int lf_strndup_args( m_inner_options *opt, bool need_translate,
const char *start, const char *stop );
// =============================================================================
m_exe_options *exe_alloc( void ){
m_inner_options *p = (m_inner_options*)calloc( sizeof( m_inner_options ), 1 );
// p->mstdin = -1;
// p->mstdout = -1;
// p->mstderr = -1;
return (m_exe_options*)p;
}
int exe_alloc_free( m_exe_options *opt ){
m_inner_options *p = (m_inner_options*)opt;
int i = 0;
if( opt == NULL ) return -1;
// if( p->executor ){
// free( p->executor );
// }
if( p->args ){
while( p->args[i] != NULL ){
free( p->args[i] );
i++;
}
free( p->args );
}
free( p );
return 0;
}
int exe_run( m_exe_options *iopt ){
if( iopt == NULL ) return -1;
m_inner_options *tp_opt = ( m_inner_options* )iopt;
/*
if( exe_parse_cmd( tp_opt ) < 0 ){
//
return -1;
}*/
if( tp_opt->flags & EXE_STDIN ){
pipe( tp_opt->fd_stdin );
}
if( tp_opt->flags & EXE_STDOUT ){
pipe( tp_opt->fd_stdout );
}
if( tp_opt->flags & EXE_STDERR ){
pipe( tp_opt->fd_stderr );
}
// split args
// create pip
// fork
//
pid_t pid;
if( ( pid = fork() ) < 0 ){
DLLOGE( "exe_run: fork failed!" );
return -1;
}
if( pid == 0 ){
int tv_null_in;
// setsid();
if( tp_opt->flags & EXE_STDIN ){
close( tp_opt->fd_stdin[1] );
dup2( tp_opt->fd_stdin[0], 0 );
}else{
int tv_null_out = open( "/dev/null", O_RDONLY );
if( tv_null_out < 0 ){
exit( -1 );
}
dup2( tv_null_out, 0 );
}
if( tp_opt->flags & EXE_STDOUT ){
close( tp_opt->fd_stdout[0] );
dup2( tp_opt->fd_stdout[1], 1 );
}else{
tv_null_in = open( "/dev/null", O_RDWR );
if( tv_null_in < 0 ){
exit( -1 );
}
dup2( tv_null_in, 1 );
}
if( tp_opt->flags & EXE_STDERR ){
close( tp_opt->fd_stderr[0] );
dup2( tp_opt->fd_stderr[1], 2 );
}else{
if( tv_null_in == -1 ){
tv_null_in = open( "/dev/null", O_RDWR );
if( tv_null_in < 0 ){
exit( -1 );
}
}
dup2( tv_null_in, 2 );
}
execvp( tp_opt->executor, tp_opt->args );
abort();
}
tp_opt->pid = pid;
if( tp_opt->flags & EXE_STDIN ){
close( tp_opt->fd_stdin[0] );
// tp_opt->mstdin = tp_opt->fd_stdin[1];
}
if( tp_opt->flags & EXE_STDOUT ){
close( tp_opt->fd_stdout[1] );
// tp_opt->mstdout = tp_opt->fd_stdout[0];
}
if( tp_opt->flags & EXE_STDERR ){
close( tp_opt->fd_stderr[1] );
// tp_opt->mstderr = tp_opt->fd_stderr[0];
}
return 0;
}
int exe_wait_exit( m_exe_options *iopt ){
m_inner_options *tp_opt = ( m_inner_options * )iopt;
if( tp_opt->pid > 0 ){
if( waitpid( tp_opt->pid, NULL, 0 ) != tp_opt->pid ){
return 0;
}
}
return 0;
}
int exe_isrunning( m_exe_options *iopt ){
m_inner_options *tp_opt = ( m_inner_options * )iopt;
if( tp_opt == NULL ) return -1;
int ret = 0;
if( tp_opt->pid > 0 ){
ret = waitpid( tp_opt->pid, NULL, WNOHANG );
if( ret == 0 ){
return 0;
}else if( ret == tp_opt->pid ){
return -1;
}
}
DLLOGE( "Shouldn't run this: ret(%d) errno(%d)(%s)",
ret, errno, strerror(errno));
exit( -1 );
}
#define EXE_PARSE_IDLE 0x01
#define EXE_PARSE_ONFOCUS 0x02
#define EXE_PARSE_START_WITH_39 0x04 // '
#define EXE_PARSE_START_WITH_34 0x08 // "
// #define EXE_PARSE_GOT_FILE 0x10
#define EXE_PARSE_NEED_TRANSLATE 0x20
int exe_parse_cmd( m_exe_options *opt ){
m_inner_options *tp_opt = ( m_inner_options * )opt;
const char *str_start;
const char *p;
// relloc size = origin size * 1.75
uint8_t tv_status = EXE_PARSE_IDLE;
if( tp_opt->cmd == NULL ){
DLLOGE( "opt->CMD not set!" );
return -1;
}
p = tp_opt->cmd;
if( tp_opt->executor || tp_opt->args ){
DLLOGE( "Your Options need to init" );
return -1;
}
tp_opt->args = (char **)calloc( sizeof(char **), 1 );
if( tp_opt->args == NULL ) return -1;
tp_opt->arg_size = 1;
while( true ){
char c = *p;
if( tv_status & EXE_PARSE_ONFOCUS ){
if( (c == 0x20) || (c == 0x09) || ( c == '\0' ) ){
if( ( tv_status & ( EXE_PARSE_START_WITH_34 |
EXE_PARSE_START_WITH_39 ) ) ){
if( c == '\0' ){
str_start--;
tv_status &= ~( EXE_PARSE_START_WITH_34 |
EXE_PARSE_START_WITH_39 );
}else{
goto GT_PARSE_LOOP_UNDER;
}
}
if( c != *str_start ){
if( lf_strndup_args( tp_opt, false, str_start, p ) < 0 ){
// TODO
return -1;
}
str_start = p+1;
}
tv_status &= ~EXE_PARSE_ONFOCUS;
tv_status |= EXE_PARSE_IDLE;
// end
if( c == '\0' ) break;
goto GT_PARSE_LOOP_UNDER;
}else if( c == '\'' ){
if( tv_status & EXE_PARSE_START_WITH_39 ){
// end
if( lf_strndup_args( tp_opt, false, str_start, p ) < 0 ){
// TODO
return -1;
}
str_start = p+1;
tv_status &= ~( EXE_PARSE_START_WITH_39 |
EXE_PARSE_ONFOCUS );
}else{
goto GT_PARSE_LOOP_UNDER;
}
}else if( c == '"' ){
if( tv_status & EXE_PARSE_START_WITH_34 ){
// end
if( lf_strndup_args( tp_opt, false, str_start, p ) < 0 ){
// TODO
return -1;
}
tv_status &= ~( EXE_PARSE_START_WITH_34 |
EXE_PARSE_ONFOCUS );
str_start = p+1;
}else{
goto GT_PARSE_LOOP_UNDER;
}
}else if( c == '\\' ){
tv_status |= EXE_PARSE_NEED_TRANSLATE;
p++;
}else if( c > 0x20 && c < 0x7F ){
goto GT_PARSE_LOOP_UNDER;
}else{
return -1;
}
}else{
if( (c == 0x20) && (c == 0x09) ){
tv_status |= EXE_PARSE_IDLE;
goto GT_PARSE_LOOP_UNDER;
}else if( c == '\0' ){
break;
}else if( c > 0x20 && c < 0x7F ){
if( tv_status & EXE_PARSE_IDLE ){
tv_status &= ~EXE_PARSE_IDLE;
tv_status |= EXE_PARSE_ONFOCUS;
// if( tv_status & EXE_PARSE_GOT_FILE ){
if( c == '\'' ){
tv_status |= EXE_PARSE_START_WITH_39;
str_start = p+1;
}else if( c == '"' ){
tv_status |= EXE_PARSE_START_WITH_34;
str_start = p+1;
}else{
str_start = p;
}
}else{
// error formay
return -1;
}
}else{
return -1;
}
}
GT_PARSE_LOOP_UNDER:
p++;
}
if( ( tp_opt->argc > 0 ) &&
( tp_opt->argc == tp_opt->arg_size ) ){
tp_opt->args = (char**)lf_realloc_clear( (void*)tp_opt->args,
tp_opt->arg_size*sizeof(char**),
( tp_opt->arg_size + 1 )*sizeof(char**) );
tp_opt->args[tp_opt->arg_size] = 0;
tp_opt->arg_size++;
}
tp_opt->executor = tp_opt->args[0];
return 0;
}
int exe_set_read_noblock( m_exe_options *opt ){
m_inner_options *tp_opt = (m_inner_options*)opt;
int set = 1;
int r;
if( opt == NULL ) return -1;
do
ioctl( tp_opt->fd_stdout[1], FIONBIO, &set );
while( r == -1 && errno == EINTR );
if( r ) return errno;
do
ioctl( tp_opt->fd_stderr[1], FIONBIO, &set );
while( r == -1 && errno == EINTR );
if( r ) return errno;
return 0;
}
int exe_read_stdout( m_exe_options *opt, char *rbuf, int rsize ){
m_inner_options *tp_opt = (m_inner_options*)opt;
return read( tp_opt->fd_stdout[0], rbuf, rsize );
}
int exe_read_stderr( m_exe_options *opt, char *rbuf, int rsize ){
m_inner_options *tp_opt = (m_inner_options*)opt;
return read( tp_opt->fd_stderr[0], rbuf, rsize );
}
int exe_write_stdin( m_exe_options *opt, char *wbuf, int wsize ){
m_inner_options *tp_opt = (m_inner_options*)opt;
return write( tp_opt->fd_stdin[1], wbuf, wsize );
}
void exe_show_opts( m_exe_options *opt ){
m_inner_options *tp_opt = (m_inner_options*)opt;
if( tp_opt == NULL ) return;
DLLOGV( "CMD: %s", tp_opt->cmd );
DLLOGV( "flags;" );
if( tp_opt->flags & EXE_STDIN )
DLLOGV( " STDIN" );
if( tp_opt->flags & EXE_STDOUT )
DLLOGV( " STDOUT" );
if( tp_opt->flags & EXE_STDERR )
DLLOGV( " STDERR" );
if( tp_opt->executor )
DLLOGV( "executor: %s", tp_opt->executor );
DLLOGV( "argc: %d", tp_opt->argc );
DLLOGV( "args size: %d", tp_opt->arg_size );
if( tp_opt->args ){
int i = 0;
DLLOGV( "args:" );
while( tp_opt->args[i] != NULL ){
DLLOGV( " {%d}:%s", i, tp_opt->args[i] );
i++;
}
}
DLLOGV( "DONE!\n" );
}
// =============================================================================
static void* lf_realloc_clear( void *p, int origin_size, int expend_size ){
char *tp = (char*)p;
tp = (char *)realloc( tp, expend_size );
if( tp == NULL ){
return NULL;
}
memset( &tp[ origin_size ], 0x00, expend_size - origin_size );
return (void*)tp;
}
static int lf_strndup_args( m_inner_options *opt, bool need_translate,
const char *start, const char *stop ){
if( opt->argc == opt->arg_size ){
int tv_next_space = opt->arg_size*1.75;
if( tv_next_space == opt->arg_size )
tv_next_space++;
opt->args = (char**)lf_realloc_clear( (void*)opt->args
, opt->arg_size*sizeof(char**)
, tv_next_space*sizeof(char **) );
if( opt->args == NULL ){
return -1;
}
// opt->args = (char**)realloc( (void*)opt->args,
// tv_next_space*sizeof(char **) );
// if( opt->args == NULL ){
// // TODO
// return -1;
// }
// void *p = (void*)(& opt->args[ opt->arg_size ]);
// memset( p, ( tv_next_space - opt->arg_size )*sizeof(char **), 0x00 );
opt->arg_size = tv_next_space;
}
opt->args[ opt->argc ] = strndup( start, stop-start );
if( opt->args[ opt->argc ] == NULL ){
// TODO
return -1;
}
if( need_translate ){
char *s;
char *d;
s = d = opt->args[ opt->argc ];
while( *s != '\0' ){
if( *s == '\\' ){
s++; *d = *s;
}else{
*d = *s;
}
d++;
s++;
}
*d = '\0';
}
opt->argc++;
return 0;
}
![]() |
![]() |
原创文章,版权所有,转载请获得作者本人允许并注明出处
我是留白;我是留白;我是留白;(重要的事情说三遍)



浙公网安备 33010602011771号