popen 与system函数笔记

本文详细介绍了如何使用popen和system函数执行命令,并强调了在特权进程中使用这些函数时的安全风险。文章还提供了使用popen替代system函数的方法,以及在调用system函数前设置SIGCHLD信号的解决方案。

 

通过popen查看selinux是否打开:

#include <stdio.h>
#include <stdlib.h>
int main ()   {

	char cmd[64] = {0};
	char buf[64] = {0};

	FILE *fd;
	sprintf(cmd,"getenforce\n");
	fd = popen(cmd, "r" );
	if(NULL == fd) {
		LOGD("%s: popen error.\n", __FUNCTION__);
		return 0;
	}
	fread(buf, sizeof(char), sizeof(buf), fd);
	LOGD("chenpuo popen buf=%s",buf);
	rc= pclose(fd2);
           if(-1==rc){
             ALOGD("getenforce close file failed");
             if(ECHILD==errno) {
                 ALOGD("cyx pclose cannot obtain the child status.\n");
             } else {
                ALOGD( "getenforce Close file failed. %s, with errno %d.\n", strerror(errno), errno);
             }
         }else{
             ALOGD("cmd getenforce child proccess %d status=%d  strerr=%s\r\n", rc, WEXITSTATUS(rc), strerror(errno));
         }
}

注意:这里cmd只要执行成功,则errno就为success;

奇怪的ls /data/test.log命令,如果文件不存在errno居然也是success,此时 No such file or directory 信息似乎打印不出来,buf为空。如果存在则buf为正常,为/data/test.log

 

 

 

#include “stdio.h”

#include “stdlib.h”

  int main()

  {

  FILE *fp;

  char buf[200] = {0};

  if((fp = popen(“cat > test1″, “w”)) == NULL) {

  perror(“Fail to popen\n”);

  exit(1);

  }

  fwrite(“Read pipe successfully !”, 1, sizeof(“Read pipe successfully !”), fp);

  pclose(fp);

  return 0;

  }


 

 

 

int main()  

{  

        FILE * fp;  

        char buf[40] = {0};  

        fp = popen(NULL, "w");  

        if(NULL == fp)  

        {  

                perror("popen error.\n");  

                return -1;  

        }  

        printf("Input command:");  

        fgets(buf, 40, stdin);  

        fputs(buf, fp);  

        pclose(fp);  

        return 0;  

}  

执行结果:

[root@localhost codetest]# ./a.out 
sh: -c: option requires an argument
Input command:pwd
 

 

 

 

 

 

==============================system函数====================================

 

看到popen就很容易想到system, system这个函数在老的平台里经常看到。现在很少看到使用了。 究其原因:

1)system 缺点:

 
1
2
#include <stdlib.h>
int system(const char *command);

system()函数执行过程为:fork()->exec()->waitpid()。

system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.

system()函数调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串command中读取命令;
在该command执行期间,SIGCHLD是被阻塞的,好比在说:hi,内核,这会不要给我送SIGCHLD信号,等我忙完再说;
在该command执行期间,SIGINT和SIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。

 

system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆

 

system的实际问题:

 转自  http://my.oschina.net/renhc/blog/54582     这里自己看后比较有感受,遂简单转述了下。

简单概括就是:系统的同事在LIBGEN 里新增加了SIGCHLD的处理。将其ignore。为了避免僵尸进程的产生。 于是我们调用system就会出错ECHILD 即No child processes 。man手册查找system函数,没有此errno;再看waitpid里正好有此errno。

ECHILD
(for waitpid() or waitid()) The process specified by pid (waitpid()) or idtype and id (waitid()) does not exist or is not a child of the calling process. (This can happen for one's own child if the action for SIGCHLD is set to SIG_IGN. See also the Linux Notes section about threads.)
果然有料,如果SIGCHLD信号行为被设置为SIG_IGN时,waitpid()函数有可能因为找不到子进程而报ECHILD错误。似乎我们找到了问题的解决方案:在调用system()函数前重新设置SIGCHLD信号为缺省值,即signal(SIGCHLD, SIG_DFL)。

 

最好如下修改可以:

 

typedef void (*sighandler_t)(int);int pox_system(const char *cmd_line)
{
   int ret = 0;
   sighandler_t old_handler;
  
++ old_handler =signal(SIGCHLD, SIG_DFL);
   ret =system(cmd_line);
++ signal(SIGCHLD, old_handler);
  
   return ret;
}

 

这里推荐使用popen()函数替代,关于popen()函数的简单使用也可以通过上面的链接查看。

popen()函数较于system()函数的优势在于使用简单,popen()函数只返回两个值:
成功返回子进程的status,使用WIFEXITED相关宏就可以取得command的返回结果;
失败返回-1,我们可以使用perro()函数或strerror()函数得到有用的错误信息。

 

 

 

 

 

=================================最后PS================================

在特权(setuid、setgid)进程中千万注意不要使用system和popen

popen()、popen()会继承环境变量,通过环境变量可能会造成系统安全的问题。

 

 

 

 

 

static char *popen_excuter(char *cmd, char *res, int rlen)
{
    FILE *fp = NULL;
    int readn = 0;
    char *tmp = res;
    char cmdtmp[128] = {0};

    if (cmd == NULL || res == NULL) {
        ALOGE("excute cmd res null\n");
        return NULL;
    }

    memset(res, 0, rlen);
    strcpy(cmdtmp, cmd);
    
    ALOGE("cmd %s entry\n", cmdtmp);
    fp = popen(cmdtmp, "r");
    if (fp == NULL) {
        ALOGE("excute %s fail\n", cmdtmp);
        return NULL;
    }

    while (!feof(fp)) {
        if(fgets(tmp, rlen, fp) != NULL) {
            readn = strlen(tmp);
            if (readn != 1 || *tmp != '\n') {
                tmp += readn;
            }
        }
    }
    ALOGE("cmd %s end, errno=%d\n", cmdtmp, errno);

    pclose(fp);
    return res;
}


static int system_excuter(char *cmd)
{
       pid_t status;

    if (cmd == NULL) {
        ALOGE("excute cmd res null\n");
        return -1;
    }

    ALOGE("system excuter=%s\n", cmd);
    status = system(cmd);
 
    if (-1 == status) {
        printf("system error!");
    } else {
        if (WIFEXITED(status)) {
            if (0 != WEXITSTATUS(status)) {
                printf("run shell script fail, status=0x%x, script exit code: %d\n",
                    status, WEXITSTATUS(status));
            }
        } else {
            printf("status=0x%x, exit code = [%d]\n", status, WEXITSTATUS(status));
        }
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值