我有一个C函数来执行一个将被调用两次的fork和exec。
第一个调用执行一个shell脚本(称之为setenv.sh),它可以是任何一种设置环境变量的shell(bash / korn / c / perl等)。对于此调用,envp数组将为NULL,但意图是在setenv.sh运行后,它将从子进程返回基于environ的填充数组。
第二个调用将是需要某个特定环境运行的C或java程序,因此对于此调用,envp数组将是从第一次调用返回的填充数组。
int execute(char **args, int argc, char **envp)
{
char *function = "execute";
int status, i;
pid_t p, pid;
extern int errno;
sigset_t mask, savemask;
struct sigaction ignore, saveint, savequit;
int fd[2];
pipe(fd);
sigemptyset(&ignore.sa_mask);
ignore.sa_handler = SIG_IGN;
ignore.sa_flags=0;
sigaction(SIGINT, &ignore, &saveint);
sigaction(SIGQUIT, &ignore, &savequit);
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &savemask);
if ((pid=fork()) < 0) status = -1;
if (pid ==0) {
/* Child */
close(fd[0]);
sigaction(SIGINT, &saveint, (struct sigaction *) 0);
sigaction(SIGQUIT, &savequit, (struct sigaction *) 0);
sigprocmask(SIG_SETMASK, &savemask, (sigset_t *) 0);
printf("Command Line Parameters\n");
printf("-----------------------\n");
for (i = 0; i < argc; i++) {
printf("[%d]: %s\n", (i+1), args[i]);
}
if (execve(*args, args, envp) < 0)
{
sprintf(err_data,"Failed to execute %s", args[0]);
perror(err_data);
return(FAILED);
}
write(fd[1], &environ, sizeof(environ));
close(fd[1]);
}
while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR) {
status = -1;
break;
}
}
if (status==0) {
read(fd[0], &envp, sizeof(envp));
}
close(fd[0]);
sigaction(SIGINT, &saveint, (struct sigaction *) 0);
sigaction(SIGQUIT, &savequit, (struct sigaction *) 0);
sigprocmask(SIG_SETMASK, &savemask, (sigset_t *) 0);
return(status);
}
这个函数工作正常,没有管道代码来执行传入的实际程序,我也可以在envp数组中传递一组环境变量,它在那个环境中运行很好。
但是,在使用包含管道的测试中,我发现在setenv.sh的exec之后,子进程从不执行向管道写入environ,而父进程只是阻塞了对管道的读取。
我理解为什么它不起作用 - 因为shell脚本的exec会覆盖子代中的原始C代码。问题是,有没有办法实现使用exec运行shell脚本并将结果环境捕获回父级(与捕获stdin / stdout / stderr不同)的目的。假设您无法更改setenv.sh的内容,因为它可能由第三方提供。
无需挑错错误处理等。这是一项正在进行中的工作,所以只是在如何实现目标的一些输入之后。
我考虑的一个替代方法是解析父级中的setenv.sh脚本以将变量获取到一个数组中,然后可以将该数组传递给实际程序。问题是setenv.sh脚本可能包含if语句块和包含其他shell脚本,所以我真的想在setenv.sh运行结束时捕获环境(通过exec&#39; ing)并传递这个回到父母。
有什么建议值得赞赏吗?
答案 0 :(得分:3)
如果不使用操作系统的调试工具并深入了解子进程的内存,基本上无法解决这个问题。这基本上要求你写一半调试器。
使用第三方脚本可以获得的最接近的是这样的。假设脚本是/ bin / bash。你可以编写自己的包装脚本:
#!/bin/bash
. setenv.sh
env >&3
其中3是管道的文件描述符编号。您可以为其他shell编写等效的脚本。这种方法的唯一原因是因为“setenv.sh”脚本在包装器脚本中执行而不创建子进程。环境变量只能传达给流程的子项。
在我在工作中使用的系统中,我们有需要在来自各种脚本的许多不同程序之间统一的环境变量,其中许多程序我们无法控制。我们解决这个问题的方法是,我们要求这些脚本输出“KEY = VALUE \ n”行,而不是环境变量,然后通过简单的脚本(如果需要)将它们导入脚本,makefile等。这可能是你能做的最好的事情。
答案 1 :(得分:-1)
您可以使用:
extern char **environ;
environ被定义为Glibc源文件posix / environ.c中的全局变量。