【问题标题】:bash shellshock update causing script to behave differentlybash shellshock 更新导致脚本行为不同
【发布时间】:2014-11-29 22:48:14
【问题描述】:

这是我们更新 bash 后发生的事情之一(由于 Shellshock 事件)

这是我正在测试的代码:

#!/usr/bin/python2.4
import subprocess, os
    p = subprocess.Popen(
            cmd,        
            shell = True,
            stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE
        )
        out, err = p.communicate()

        print "out:", out
        print "err:", er

首先运行命令:

cmd = "cd /home/me/; pwd; p4 client -o"
out: /home/me/
err:
perforce client that is NOT mine ( some kind of a default template is trying to be used here )

第二个测试,我添加如下Python args:

env = os.environ.copy()
# and add "env" variable to the Popen command after "cmd, like:
env = env,

输出:

cmd = "cd /home/me/; pwd; p4 client -o"
out: /home/me/
err:
my perforce client information - as it should.

我的问题是, 我似乎无法理解为什么“env = env”在这里很重要。 我尝试运行几个命令,例如“导出”等,以了解有/无它之间的区别 - 但结果是相同的,还检查了“shell”都使用“sh”。所以我不确定环境的哪一部分导致它不能像示例 #1 那样工作。

我确定它与 Perforce 本身并没有真正的关系,Perforce 可能只是需要一些环境变量,而这些环境变量由于这个 bash Shellshock 事件而受到了影响。

编辑 - 澄清@5gon12eder 的建议

我尝试在没有“env=env”(这是一个 os.environ.copy() )的情况下查看 ENV,

# 1
# The outouput is wrong ( generic Perforce views )
cd /home/me/; pwd; p4 client -o

# 2 - manually adding P4CONFIG
# The outouput is *correct*
cd /home/me/; pwd; P4CONFIG='.perforce'; p4 client -o


# But the environment variable looks like is there with the correct information ( can't paste it here )
subprocess.call("env") | grepping the script's output P4
> P4CONFIG=...
> P4PORT=...
> P4USER ...

注意,P4 变量配置了两次:

/etc/profile
/home/me/.bash_profile

编辑 2 - 也使用 Perl 进行复制:

环境忽略 %ENV 中的 P4CONFIG 条目。

#!/usr/bin/perl -w

use strict;
use Data::Dumper;

my $dir = "/home/me/";
my $cmd1 = "cd $dir; p4 client -o";
my $cmd2 = "cd $dir; P4CONFIG='.perforce'; p4 client -o";

# Scenario 1 - does NOT work ! $ENV does have P4CONFIG with a correct value.  
`$cmd1`;
# ** still wrong result - generic Perforce views

# Scenario 2 - adding P4CONFIG= to the command:
`$cmd2`;
# Correct result - my .perforce client's views.

# Scenario 3 - Adding to the ENV P4CLIENT ( which does not exists in %ENV )
$ENV{'P4CLIENT'} = "my_client_name";
`$cmd1`;    # The one without the P4CONFIG enforcement. - WORK.

编辑 3 - 如果我使用/不使用 >> 和 |

会有区别

实际上,考虑到这一点,当我的一个库在上面的子进程(Python)“cmd”示例中有一个“>/dev/null”重定向导致脚本挂起并退出时,就引入了整个问题超时,我用“-o file-output”替换它,问题消失了,但后来我陷入了这个问题,所以我打开了这篇文章。

# I already found that adding this row - solving the ENV thing ( not really solving ... but )
$ENV{'P4CONFIG'} = ".perforce";

# Work, I see the excepted output
my $bla = `p4 client -o`;

# Doesn't work, script hangs and Perforce exit with a timeout ( like a P4PORT missing error )
# (I was just trying to remove all the comments-junk )
my $bla = `p4 client -o | grep -v '^#'`;

# Script doesn't hang for example if I just "echo"
my $bla = `echo 'p4 client bla bla' | grep -v '^#'`;

只想再说一遍,这一切都是在炮击事件发生之前(可能是真实的,也可能是巧合),但是在 exec() 调用的环境中有些不同... 有什么想法吗?

【问题讨论】:

  • 如果指定的客户端规范不存在,Perforce 确实会生成默认客户端规范。因此,在这种情况下,最重要的环境变量是 P4CLIENT。要观察 P4CLIENT 的设置是否与您看到的行为相关,请尝试将“p4 client -o”修改为“p4 -c client -o”,其中“”是 P4CLIENT 环境变量的值. -c 标志覆盖 p4 从环境中获取的 P4CLIENT 值;请参阅“p4 help usage”以了解您可以用来代替 env vars 的其他类似标志。
  • 请尝试生成一个独立的示例,可能不使用p4,我们可以运行它来演示使用默认值env 和环境副本之间的相同差异.
  • @bryan 我在“cd”到的目录中有一个 .perforce 文件,如果我之前没有说过的话 - 相同的“cmd”字符串 - 如果我复制粘贴它就可以了并按原样通过 shell/bash 运行它。
  • @chepner 我试过了,但我不确定我能想到什么没有 ENV 将无法工作......我今天会再试一次
  • 听起来您的“.perforce”文件是一个 P4CONFIG 文件,并且包含 Perforce 配置设置,例如您的客户端名称。也就是说,在您的 .perforce 文件中,有一行“P4CLIENT=”。这意味着最重要的外部环境变量是 P4CONFIG,您通常必须将其设置为“.perforce”。

标签: python linux bash perl perforce


【解决方案1】:

您的代码是否在修改环境?事情是这样的

os.putenv('VAR', 'VAL')

(可能)直接修改环境但不更新os.environ

os.environ['VAR'] = 'VAL'

确实如此。来自Python documentation

直接调用putenv()不会改变os.environ,所以最好修改os.environ

如果未提供putenv(),则可以将此映射的修改副本传递给适当的进程创建函数,以使子进程使用修改后的环境。

在阅读了os Python 模块和随附的posixmodule C 模块的有些晦涩的源代码之后,从本文档中变得不太清楚的是,如果底层平台的C 库没有@987654322 @C 函数,然后在 os.environ 中设置键只影响 Python 字典,os.putenv 是空操作。来自Lib/os.py

try:
    _putenv = putenv
except NameError:
    _putenv = lambda key, value: None
else:
    if "putenv" not in __all__:
        __all__.append("putenv")

来自Modules/posixmodule.c

static PyMethodDef posix_methods[] = {
  /* Lots and lots of code skipped... */
#ifdef HAVE_PUTENV
  {"putenv", posix_putenv, METH_VARARGS, posix_putenv__doc__},
#endif
  /* Even more code skipped... */
};

因此,您可以在所展示的两种情况下观察到不同的行为,因为 - 令我惊讶的是 - 如果将 env=None 传递给 subprocess.Popen 构造函数,它确实不会替代 os.environ作为默认值,但使用 C 标准库中的值。

为子进程(Lib/subprocess.py)设置环境的代码是

if env is not None:
    env_list = [os.fsencode(k) + b'=' + os.fsencode(v)
                for k, v in env.items()]
    else:
        env_list = None  # Use execv instead of execve.

并且最近没有改变。从exec(3) 的手册页(强调我的):

execle()execvpe() 函数允许调用者通过参数 envp 指定执行程序的环境。 envp 参数是一个指向以空字符结尾的字符串的指针数组,并且必须以空指针结尾。 其他函数从调用进程中的外部变量environ获取新进程映像的环境。

我查看了Modules/_posixsubprocess.c(对于execv/execve)和Modules/posixmodule.c(对于putenv)的源代码,以检查它们是否真的按照描述以及我所知道的那样调用系统函数,他们似乎这样做了。两个 C 模块都没有收到似乎与此功能相关的最新更改。

阅读您的问题后,我的第一个想法是,Python 开发人员最终为传递给子进程的环境引入了健全性检查,但似乎他们没有。抱歉,如果这不是一个答案,但我认为它可能仍然对其他人有用,以帮助他们避免深入研究 CPython 源代码。

作为进一步调试的建议,请尝试运行

$ python -c "import os; import subprocess; os.putenv('VAR', 'VAL'); subprocess.call('env');" | grep VAR=

以及各种变化来追踪正在发生的事情。

脚注:我认为subprocess.Popen 的行为更多是错误而不是功能,因为它使 Python 代码在提供或不提供 putenv C 函数的平台上表现不同很好的理由。

【讨论】:

  • 请注意,您引用的代码来自 Python 3 版本的 subprocess.py; OP 使用的是 Python 2.4。至少在 Python 2.6 中,subprocess 只是在execvpexecvpe 之间进行选择,而不是对环境本身进行任何处理。
  • @chepner 很好看;我错过了 OP 的版本号。现在 OP 的观察似乎更奇怪,因为在 Python 2 中,os.execvp 进一步调用 os._execvpeenv=None,而后一个函数包含 if env is None: env = environ。至少,在 Python 2.7 中是这样。恐怕我的回答现在有点没有意义了……
【解决方案2】:

所以我想与大家分享这些发现,以防其他人会在此问题上停留一段时间。 问题是,我们在以下位置导出了一些函数:

/etc/profile

这导致实施 bash 补丁后环境的行为有所不同。 删除这些函数解决了我们所有脚本中的问题......并且环境变量不再需要更改......

感谢大家的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-05
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    • 2017-08-31
    • 1970-01-01
    • 2022-11-10
    相关资源
    最近更新 更多