2

我想用来execl("/bin/bash","bash","-l","-c","env",NULL)获取环境变量,我使用参数的原因"-l"是我不需要 source "/etc/profile""~/.bash_login"等等。但是当我运行它时,程序被暂停,我必须使用 ctrl+c 或 ctrl+d 来停止它?你能告诉我如何修改它吗?

代码如下所示,getPtrArray用于将一维数组变为二维数组。

int pid;
int fd[2];
char buffer[10000];
char** envi;
int res=pipe(fd);
//create a child process to get environment variable
if((pid=fork())==0){
    close(fd[0]);
    dup2(fd[1],STDOUT_FILENO);
    struct passwd *pw=getpwnam("hgchen");
    char *shell_type=pw->pw_shell;
    if(execl("/bin/bash","bash","-l","-c","env",(char*)0)<0){
        printf("Error\n");
    }
    exit(0);     
}
// main process
else{
    wait(NULL);
    close(fd[1]);
    int nbytes=read(fd[0],buffer,sizeof(buffer));
    envi=getPtrArray(buffer);
}
4

4 回答 4

2

编辑说明:这是对原始示例代码的完全重写,因为 OP 发布了代码,我意识到它会导致bash阻塞标准输出而不是我最初认为的输入。原因是bash输出被重定向到管道,在子退出之前没有从管道读取任何内容。

在您之前execl(),重新打开STDIN_FILENOfrom/dev/nullSTDERR_FILENOto /dev/null。当STDOUT_FILENO(标准输出)被重定向到管道时,您不能只wait()让子进程退出:您必须在子进程运行时主动从管道中读取。

考虑这个示例程序。它采用一个命令行参数,即用户名。(不带任何参数或仅-h输出--help简短的使用信息。)

它获取struct passwd与该用户名对应的名称,创建存储在该结构中的用户 shell 路径的副本。它派生出一个子进程,path-to-shell shell-name -c env在子进程中执行,将输出捕获到动态分配的数组(使用execute()函数)。为简单起见,main 然后将输出写入原始标准输出。您可以省略最后一个while () { ... }循环,以查看输出是否真正捕获到动态分配的数组中。

请注意,我实际上并没有验证所有shell 都支持该-c语法。我确实知道bash, sh(original Bourne shells), dash(a POSIX shell) tcsh, 和zsh所有的——覆盖我的所有 shell /etc/shells,即允许的 shell 文件——,所以它应该在实践中工作;我只是不能保证。

#define  _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <pwd.h>
#include <string.h>
#include <errno.h>

/* Open file or device to the specified descriptor.
 * Will never create files.
 * Returns 0 if success, errno otherwise.
*/
static int reopen(const int descriptor, const char *const path, const int flags)
{
    int result, fd;

    if (descriptor == -1)
        return errno = EBADF;
    if (!path || !*path || flags & O_CREAT)
        return errno = EINVAL;

    do {
        fd = open(path, flags);
    } while (fd == -1 && errno == EINTR);
    if (fd == -1)
        return errno;

    if (fd == descriptor)
        return errno = 0;

    do {
        result = dup2(fd, descriptor);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        return errno;

    do {
        result = close(fd);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        return errno;

    return errno = 0;
}

/* Helper function: Close descriptor keeping errno unchanged.
 * Returns 0 if success, errno.h error code otherwise.
*/
static int closefd(const int descriptor)
{
    if (descriptor != -1) {
        const int saved_errno = errno;
        int       result;
        do {
            result = close(descriptor);
        } while (result == -1 && errno == EINTR);
        if (result == -1)
            result = errno;
        else
            result = 0;
        errno = saved_errno;
        return result;
    } else
        return EBADF;
}

/* Execute a command in a child process, capturing the output.
 * Standard input and error are redirected to /dev/null.
 * Returns zero if success, errno error code otherwise.
*/
int execute(const char *const cmdpath,
            const char *const args[],
            char      **const dataptr,
            size_t     *const sizeptr,
            size_t     *const lenptr,
            int        *const statusptr)
{
    pid_t   child, p;
    int     out[2], result, *childstatus;
    char   *data;
    size_t  size, used = 0;
    ssize_t bytes;

    if (!cmdpath || !*cmdpath || !args || !args[0] || !dataptr || !sizeptr || !lenptr)
        return errno = EINVAL;

    /* Create the standard output pipe. */
    if (pipe(out))
        return errno;

    /* Fork the child process. */
    child = fork();
    if (child == (pid_t)-1) {
        closefd(out[0]);
        closefd(out[1]);
        return errno;
    }

    if (!child) {
        /*
         * Child process.
        */
        closefd(STDIN_FILENO);
        closefd(STDOUT_FILENO);
        closefd(STDERR_FILENO);
        closefd(out[0]);

        /* Redirect standard output to the pipe. */
        if (out[1] != STDOUT_FILENO) {
            do {
                result = dup2(out[1], STDOUT_FILENO);
            } while (result == -1 && errno == EINTR);
            if (result == -1)
                _exit(127);
            closefd(out[1]);
        }

        /* Open standard input from /dev/null. */
        if (reopen(STDIN_FILENO, "/dev/null", O_RDONLY))
            _exit(127);

        /* Open standard error to /dev/null. */
        if (reopen(STDERR_FILENO, "/dev/null", O_WRONLY))
            _exit(127);

        /* Execute the specified command. */
        execv(cmdpath, (char **)args);

        /* Failed. */
        _exit(127);
    }

    /*
     * Parent process.
    */

    closefd(out[1]);

    if (*sizeptr > 0) {
        data = *dataptr;
        size = *sizeptr;
    } else {
        data = *dataptr = NULL;
        size = *sizeptr = 0;
    }

    while (1) {

        /* Grow data array if needed. */
        if (used >= size) {
            size = (used | 32767) + 32769;
            data = realloc(data, size);
            if (!data) {
                kill(child, SIGTERM);
                do {
                    p = waitpid(child, NULL, 0);
                } while (p == (pid_t)-1 && errno == EINTR);
                return errno = ENOMEM;
            }
            *dataptr = data;
            *sizeptr = size;
        }

        /* Read more data. */
        do {
            bytes = read(out[0], data + used, size - used);
        } while (bytes == (ssize_t)-1 && errno == EINTR);
        if (bytes > (ssize_t)0)
            used += (size_t)bytes;
        else
        if (bytes == (ssize_t)0)
            break; /* All read (end of input) */
        else {
            const int retval = (bytes == (ssize_t)-1) ? errno : EIO;
            kill(child, SIGTERM);
            do {
                p = waitpid(child, NULL, 0);
            } while (p == (pid_t)-1 && errno == EINTR);
            return errno = retval;
        }
    }

    /* We need to add the final '\0', which might not fit. */
    if (used + 1 >= size) {
        size = used + 1;
        data = realloc(data, size);
        if (!data) {
            kill(child, SIGTERM);
            do {
                p = waitpid(child, NULL, 0);
            } while (p == (pid_t)-1 && errno == EINTR);
            return errno = ENOMEM;
        }
        *dataptr = data;
        *sizeptr = size;
    }

    data[used] = '\0';
    if (lenptr)
        *lenptr = used;

    /* Reap the child process. */
    if (statusptr)
        childstatus = statusptr;
    else
        childstatus = &result;
    do {
        p = waitpid(child, childstatus, 0);
    } while (p == (pid_t)-1 && errno == EINTR);
    if (p == (pid_t)-1)
        return errno;

    /* Success. */
    return errno = 0;
}

/* A helper to write to standard error. Errno is kept unchanged.
 * Returns zero if success, errno error code otherwise.
 * Async-signal safe, in case you wish to use this safely in a signal handler.
*/
static int wrerr(const char *const message)
{
    if (message && *message) {
        const int   saved_errno = errno;
        const char *p = message;
        const char *q = message;
        ssize_t     n;

        /* q = message + strlen(message), except that strlen()
         * is not an async-signal safe function. */
        while (*q)
            q++;

        while (p < q) {
            n = write(STDERR_FILENO, p, (size_t)(q - p));
            if (n > (ssize_t)0)
                p += n;
            else
            if (n != (ssize_t)-1) {
                errno = saved_errno;
                return EIO;
            } else
            if (errno != EINTR) {
                const int retval = errno;
                errno = saved_errno;
                return retval;
            }
        }

        errno = saved_errno;
        return 0;
    } else
        return 0;
}

const char *basename_of(const char *const string)
{
    const char *r;

    if (!string)
        return NULL;

    r = strrchr(string, '/');
    if (r && r[1])
        return r + 1;

    return NULL;
}

int main(int argc, char *argv[])
{
    struct passwd *pw;
    char          *shell;
    const char    *args[4];
    char          *data = NULL;
    size_t         size = 0;
    size_t         used = 0;
    int            status;

    if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        wrerr("\n");
        wrerr("Usage: "); wrerr(argv[0]); wrerr(" [ -h | --help ]\n");
        wrerr("       "); wrerr(argv[0]); wrerr(" USERNAME\n");
        wrerr("\n");
        return 1;
    }

    pw = getpwnam(argv[1]);
    if (!pw) {
        wrerr(argv[1]);
        wrerr(": ");
        wrerr(strerror(errno));
        wrerr(".\n");
        return 1;
    }

    if (pw->pw_shell && pw->pw_shell[0] == '/')
        shell = strdup(pw->pw_shell);
    else
        shell = strdup("/bin/sh");
    args[0] = basename_of(shell);
    if (!args[0]) {
        wrerr(argv[1]);
        wrerr(": User has invalid shell, '");
        wrerr(shell);
        wrerr("'.\n");
        return 1;
    }

    args[1] = "-c";
    args[2] = "env";
    args[3] = NULL;

    if (execute(shell, args, &data, &size, &used, &status)) {
        wrerr("Failed to execute ");
        wrerr(shell);
        wrerr(": ");
        wrerr(strerror(errno));
        wrerr(".\n");
        return 1;
    }

    free(shell);

    /* Dump environment to standard output. */
    {
        const char       *p = data;
        const char *const q = data + used;
        ssize_t           n;

        while (p < q) {
            n = write(STDOUT_FILENO, p, (size_t)(q - p));
            if (n > (ssize_t)0)
                p += n;
            else
            if (n != (ssize_t)-1) {
                wrerr("Error writing to standard output.\n");
                return 1;
            } else
            if (errno != EINTR) {
                wrerr("standard output: ");
                wrerr(strerror(errno));
                wrerr(".\n");
                return 1;
            }
        }
    }

    free(data);
    data = NULL;
    size = 0;
    used = 0;

    /* All done. */
    return 0;
}

这是比真正需要(或首选)低得多的代码;您可以使用popen()和其他stdio.hI/O 函数执行相同的操作。

(我避免这些只是为了让我自己更感兴趣。)

wrerr()只是我喜欢使用的一个辅助函数,与fprintf()//不同printf()perror()它是异步信号安全的并且忽略信号传递 ( errno==EINTR)。在这里,它不是必需的,您也可以使用 eg fprintf()。(与您在网上看到的几乎所有示例不同,printf()et al. 不应该在信号处理程序中工作。它们通常可以工作,但绝对不能保证。wrerr()会工作,因为它符合 POSIX。)

我还包括完整的错误检查。如果没有内核错误,一些错误情况是不可能发生的,但无论如何我更喜欢拥有它们。在您遇到错误的情况下,您确实需要它们,无论是在您自己的代码中还是在其他地方。

在错误情况下,我不会费心去释放动态分配的内存(虽然我可以),因为内核总是会自动处理这些。但是,main()如果没有发生错误,程序会在返回之前释放所有动态分配的内存。

问题?

于 2013-09-09T08:13:34.510 回答
1

这并不能直接回答您的问题,但使用起来要容易得多popen(3)

这是经过测试和工作的(在 OSX 下,而不是 Linux 下):

#include <stdio.h>

int main(int argc, const char **argv) {
    char line[1024];
    FILE *pipefp = popen("/bin/bash -l -c env", "r");
    if (pipefp) {
        while (fgets(line, sizeof(line), pipefp)) {
            // Note: line contains newline
            printf("%s", line);
        }
        pclose(pipefp);
    }
    return 0;
}
于 2013-09-09T09:39:44.553 回答
0

一种查找导致命令挂起的原因的方法添加“-x”选项。这段代码对我有用:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    int r = execl("/bin/bash", "bash", "-x", "-l", "-c", "env", NULL);
    printf("r: %d\n", r); /* This should not be printed or else execl has errors. */
    return 0;
}
于 2013-09-09T08:18:12.810 回答
0

wait父母是错误的。您必须首先读取shall 的输出,然后等待它。正如评论中已经解释的那样,shell 正在等待您读取输出并且您正在等待 shell 退出,因此您陷入了僵局。

您需要将wait .read

于 2013-09-09T09:52:13.223 回答