execl(“/ bin / bash”,“bash”,“ - l”,“ - c”,“env”,NULL),有什么问题?

时间:2013-09-09 07:09:05

标签: c linux bash

我想使用execl("/bin/bash","bash","-l","-c","env",NULL)来获取环境变量,我使用参数"-l"的原因是我不需要来源"/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 个答案:

答案 0 :(得分:2)

编辑注释:这是对原始示例代码的完全重写,因为OP发布了代码并且我意识到它导致bash阻止标准输出而不是输入为I原本以为。原因是bash输出被重定向到管道,在子项退出之前没有从管道读取任何内容。

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

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

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

请注意,我实际上并未验证所有 shell是否支持-c语法。我知道bashsh(原始Bourne shell),dash(POSIX shell),tcshzsh都做了 - 涵盖所有在我的/etc/shells中的shell,即允许的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.h I / O函数执行相同的操作。

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

wrerr()只是我喜欢使用的辅助函数,与fprintf() / printf() / perror()不同,它是异步信号安全的并且忽略信号传递( errno==EINTR)。在这里,需要,你可以使用例如fprintf()同样如此。 (与网上可以看到的每个例子不同,printf()等不应该在信号处理程序中工作。它们通常可以工作,但绝对没有保证。wrerr()会起作用,因为它符合POSIX。)

我还包括完整的错误检查。如果没有内核错误,一些错误情况是不可能的,但我还是喜欢它们。在您遇到错误的情况下,无论是在您自己的代码中还是在其他地方,您都非常想它们。

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

有问题吗?

答案 1 :(得分: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;
}

答案 2 :(得分: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;
}

答案 3 :(得分:0)

父级中的wait是错误的。您必须首先读取来自the的输出,然后然后等待它。正如在注释中已经解释的那样,shell正在等待您读取输出,并且您正在等待shell退出,因此您已经死锁。

您需要在 wait之后移动read