zoukankan      html  css  js  c++  java
  • UNIX环境编程学习笔记(22)——进程管理之system 函数执行命令行字符串

    lienhua34
    2014-10-15

    ISO C 定义了 system 函数,用于在程序中执行一个命令字符串。其声明如下,

    #include <stdlib.h>

    int system(const char *cmdstring);

    system 函数在其实现中调用了 fork、exec 和 waitpid 函数。system 函数调用 fork 函数创建子进程,然后由子进程调用’/bin/sh -c cmdstring’ 来执行命令行参数 cmdstring,此命令执行完后便返回调用的进程。在调用system 函数期间 SIGCHLD 信号会被暂时搁置,SIGINT 和 SIGQUIT 信号则会被忽略。

    system 函数的返回值有点复杂,有下面几种情况,

    1. 如果 cmdstring 是空指针,则仅当命令处理程序处理程序可用时,返回非 0 值。这一特征可用于确定一个操作系统是否支持 system 函数。UNIX 系统总是支持 system 函数的。

    2. 如果 fork 失败或者 waitpid 返回除 EINTR 之外的出错,则 system返回 -1,而且 errno 中设置错误类型值。

    3. 如果 exec 失败(表示不能执行 shell),则其返回值相当于 shell 执行了 exit(127) 后的终止状态。

    4. 如果所有三个函数(fork、exec 和 waitpid)都执行成功,则 system的返回值是 shell 执行命令参数 cmdstring 后的终止状态。

    通过上面对返回值的说明,我们发现当命令行参数 cmdstring 不为空指针时,我们可以通过判断返回值是否为 -1 来判定 system 函数是否成功。而其他情况,我们则可以像文档“获取进程终止状态的 wait 和 waitpid 函数”中说明的处理终止状态一样来处理 system 函数的返回值。下面我们看一下例子,

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    extern void print_exit(int status);
    
    int
    main(void)
    {
      int status;
    
      if ((status = system("date")) < 0) {
        printf("system() error: %s
    ", strerror(errno));
        exit(-1);
      }
      print_exit(status);
    
      if ((status = system("nosuchcommand")) < 0) {
        printf("system() error: %s
    ", strerror(errno));
        exit(-1);
      }
      print_exit(status);
    
      if ((status = system("who; exit 44")) < 0) {
        printf("system() error: %s
    ", strerror(errno));
        exit(-1);
      }
      print_exit(status);
      
      exit(0);
    }
    
    void print_exit(int status)
    {
      if (WIFEXITED(status)) {
        printf("normal termination, exit status = %d
    ",
               WEXITSTATUS(status));
      } else if (WIFSIGNALED(status)) {
        printf("abnormal termination, signal number =%d
    ",
               WTERMSIG(status));
      }
    }

    如上面程序所述,我们可以像处理进程终止状态一样来处理 system 函数的返回值。编译该程序,生成并执行文件 systemdemo,

    lienhua34:demo$ gcc -o systemdemo systemdemo.c
    lienhua34:demo$ ./systemdemo
    2014年 10月 15日 星期三 23:15:28 CST
    normal termination, exit status = 0
    sh: 1: nosuchcommand: not found
    normal termination, exit status = 127
    lienhua34 tty7 2014-10-15 22:28
    lienhua34 pts/0 2014-10-15 22:37 (:0.0)
    lienhua34 pts/3 2014-10-15 23:10 (:0.0)
    normal termination, exit status = 44
    lienhua34:demo$

    system 函数创建进程相对于 fork 与 exec 函数组合的优点是:system函数进行了所需的各种出错处理,以及各种信号处理。但是,system 函数也有其致命弱点:在设置用户 ID 程序中使用 system 函数会存在安全性漏洞。下面,我们来看个例子。


    下面程序使用 system 函数执行第一个命令行参数。将该程序编译为可执行文件 tsys。

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    
    int
    main(int argc, char *argv[])
    {
      int status;
      if (argc < 2) {
        printf("command-line argument required.
    ");
        exit(-1);
      }
      if ((status = system(argv[1])) < 0) {
        printf("system error: %s
    ", strerror(errno));
        exit(-1);
      }
      exit(0);
    }

    下面提供一个打印进程实际用户 ID 和有效用户 ID 的程序,将该程序编译为可执行文件 printuids。

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    int
    main(void)
    {
        printf("real uid=%d, effective uid=%d
    ", getuid(), geteuid());
        exit(0);
    }

    运用这两个可执行文件,我们执行下面的操作,

    lienhua34:demo$ ./tsys ./printuids
    real uid=1000, effective uid=1000
    lienhua34:demo$ su
    # chown root tsys
    # chmod u+s tsys
    # ls -l tsys
    -rwsrwxr-x 1 root lienhua34 7358 10月 15 23:37 tsys
    # exit
    exit
    lienhua34:demo$ ./tsys ./printuids
    real uid=1000, effective uid=0

    在上面的执行过程中,我们将 tsys 文件的所有者设置为超级用户 root,并且设置了设置用户 ID 位。于是,执行 tsys 文件时,进程的有效用户 ID为 0(即 root)。而调用 system 函数执行 printuids 文件的子进程继承了这个有效用户 ID,于是子进程就拥有了很大的权限,可以执行任何可能会造成致命伤害的程序命令。这就是 system 函数存在的安全性漏洞。

    如果一个进程以特殊权限(设置用户 ID 或设置组 ID)运行,它又想生成另一个进程执行另一个程序,则应当直接使用 fork 和 exec,而且在fork 之后、exec 之前要改回到普通权限。设置用户 ID 或设置组 ID 程序决不应调用 system 函数。

    (done)

  • 相关阅读:
    jdbc crud
    xcode升级10
    Java 多线程系列 CountDownLatch
    51小项目——使用proteus搭建简易的光照度计-(2)
    51小项目——使用proteus搭建简易的光照度计-(1)
    关于proteus中仿真STM32F103芯片的注意事项
    MOOC_TCP简述
    STM32F411RE片内资源
    左移、右移——极简
    初学51——中断
  • 原文地址:https://www.cnblogs.com/lienhua34/p/4029910.html
Copyright © 2011-2022 走看看