When executing commands, Facter will expand the first word in the command string to be a fully qualified path. I.e. ls -l will become /usr/bin/ls -l. If the command string is a compound command that contains a pipeline or looping construct, then Facter will wrap the executing in /bin/sh -c. However, the first word of the compound is still expanded to an absolute path. This breaks shell builtins like cd as they are expanded to external commands like /usr/bin/cd or fail to be found on the PATH. Reproduction Case
- Install the latest version of puppet-agent on CentOS 7, along with strace:
yum install -y http://yum.puppetlabs.com/puppet-release-el-7.noarch.rpm |
yum install -y puppet-agent strace
|
- Create a test script that loads Facter, and uses it to execute a compound command that begins with cd:
cat <<EOF > test.rb |
#!/opt/puppetlabs/puppet/bin/ruby |
require 'facter' |
|
puts Facter::Core::Execution.execute('cd /opt/puppetlabs && ls') |
EOF |
chmod +x test.rb
|
Outcome The script prints the contents of the current working directory instead of /opt/puppetlabs:
# ./test.rb |
1 |
anaconda-ks.cfg |
linux.iso |
test.rb
|
Running the script under strrace reveals that cd is being expanded to /usr/bin/cd before being passed to sh -c:
# strace -f -e trace=execve ./test.rb |
execve("./test.rb", ["./test.rb"], [/* 23 vars */]) = 0 |
strace: Process 20373 attached |
strace: Process 20374 attached |
[pid 20374] execve("/usr/bin/sh", ["sh", "-c", "/usr/bin/cd /opt/puppetlabs && l"...], [/* 24 vars */]) = 0 |
strace: Process 20375 attached |
[pid 20375] execve("/usr/bin/cd", ["/usr/bin/cd", "/opt/puppetlabs"], [/* 24 vars */]) = 0 |
[pid 20375] +++ exited with 0 +++ |
[pid 20374] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=20375, si_uid=0, si_status=0, si_utime=0, si_stime=0} --- |
strace: Process 20376 attached |
[pid 20376] execve("/usr/bin/ls", ["ls"], [/* 24 vars */]) = 0 |
[pid 20376] +++ exited with 0 +++ |
[pid 20374] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=20376, si_uid=0, si_status=0, si_utime=0, si_stime=0} --- |
[pid 20374] +++ exited with 0 +++ |
[pid 20372] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=20374, si_uid=0, si_status=0, si_utime=0, si_stime=0} --- |
1 |
anaconda-ks.cfg |
linux.iso |
test.rb |
[pid 20373] +++ exited with 0 +++ |
+++ exited with 0 +++
|
Expected Outcome The script prints the content of /opt/puppetlabs:
# ./test.rb |
bin |
facter |
puppet |
pxp-agent
|
Suggested Workaround The expansion only affects the first word in the command line, so adding an extra true && to the compound command acts as a sacrificial noop that takes the hit instead. |