如何在 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; }
![]() |
![]() |
原创文章,版权所有,转载请获得作者本人允许并注明出处
我是留白;我是留白;我是留白;(重要的事情说三遍)