Calling one bash function from inside another as a specified user - linux

So I've got the following function, which executes another function (defined the same way, executed the same way this one is successfully called).
The reason for the syntax I've used here is to ensure the code is executed as a particular user- as I say, outside of functions, this syntax works with no issues.
f2(){
echo "test" > test.txt
}
f1(){
a=1
if (($a < 0 ))
then
echo "Not running function."
else
echo "Running function."
sudo su servuser -c "$(declare -f f2); f2"
fi
}
sudo su user1 -c "$(declare -f f1); f1"
Why is this the case? Is there a change that can be made to this code to allow me to call one function from another function as a certain user?
Any help would be most welcome :)

When you execute a script using su, that script has only access to the exported variables from your current shell script. You used $(declare -f f1) to work around this issue, but forgot that the 2nd su inside f1 loses access to f2. When f1 executes su user -c '$(declare -f f2); f2' there is no f2 so you cannot print/export it and therefore cannot execute it.
You have to explicitly include the dependencies of f1 in your first export:
f2() {
echo "test" > test.txt
}
f1() {
sudo su servuser -c "$(declare -f f2); f2"
}
sudo su user1 -c "$(declare -f f2 f1); f1"
By the way: Bash allows exporting functions.
f2() {
echo "test" > test.txt
}
f1() {
sudo su servuser -c f2
}
export -f f1 f2
sudo su user1 -c f1

Related

Unexpected behaviour running remote script

I'm attempting to write a script that runs on a remote machine.
As this script is complex, I've broken parts of it down into functions that I then copy into the script using typeset.
When I run the script, I get the following error:
bash: -c: line 4: syntax error: unexpected end of file
However, there is no unexpected end of file! I ensured all ifs had fis, all { had }, etc.
I've ensured all indentation uses tabs (and spaces), and ensured new-line characters are consistent.
I've stripped the code down to a minimal example. It seems as though for a function to be valid, it needs to end in a construct such as if fi or while done.
Here's what isn't working.
func() {
ls ~/
}
ssh -A -T user#machine_ip '
'$(typeset -f func)'
func
'
Line 4 coincides with the end } of the func function (counting lines from after ssh -A -T... as this error is happening on the remote machine).
Yet, if we add a construct to the end of the function, the home directory is printed as expected.
func() {
ls ~/
if [[ 1 ]]; then
echo "Hello"
fi
}
or
func() {
ls ~/
while false; do
echo "Here"
done
}
Output of typeset -f func is
func ()
{
ls --color=auto ~/;
if [[ -n 1 ]]; then
echo "Hello";
fi
}
I'm running Ubuntu 18.04 LTS, remote machine is running Centos 6.
Because bash plays with whitespace if you let it. Let me explain:
$(typeset -f func) will evaluate typeset -f func and insert its output into the current command line. If not quoted, it will also segment into parameters, which will have as a side effect collapsing of all whitespace to a single space. Thus, if typeset -f func prints (as it does on my system)
func ()
{
/bin/ls --color=auto ~/
}
what you get with $(typeset -f func) is
func () { /bin/ls --color=auto ~/ }
(Try echo $(typeset -f func) if you don't believe me :D )
Now, bash is really bashful about accepting smushed-up code. For example, you may know that this is not grammatical:
if true then echo "yes" fi
and this is:
if true; then echo "yes"; fi
In the same way, the function definition's closing parenthesis is picky. Thus, this works:
func () { /bin/ls --color=auto ~/; }
but this doesn't:
func () { /bin/ls --color=auto ~/ }
For some reason, bash is fine with a keyword being just before the parenthesis:
func () { /bin/ls --color=auto ~/; if [[ -n 1 ]]; then echo "Hello"; fi }
func () { /bin/ls --color=auto ~/; while false; do echo "Here"; done }
To combat this... try not sending stuff from command line, which mangles your whitespace, but from redirection:
ssh -A -T user#machine_ip < <(typeset -f func; echo func)
Or, simplest of all, prevent bash mangling of whitespace using double quotes:
ssh -A -T user#machine_ip "$(typeset -f func)
func"
first of all, the way you use quotes make no sense to me.
ssh -A -T user#machine_ip '
'$(typeset -f func)'
func
'
the 2 first ' just echo an empty line. ( the 2nd ' close the 1st. logic ? )
Anyway.
$(typeset -f func)
func
the code above will execute func 2 times.
At least, thats how it works for me
....

How to set sudo in current shell mid-script without leaving current shell?

I would like to enable sudo within this bash function mid-script. I believe the issue is that sudo -s opens up a new shell, and all subsequent lines aren't printed because the shell is exited.
function example {
echo "one"
sudo -s
echo "two"
echo "three"
}
How can I have two and three also get printed as sudo? Without adding sudo before each?
What about a double function? Can that help?
#!/bin/bash
function test2 {
sudo echo "two"
sudo echo "three"
}
function test {
echo "one"
test2
}
test
Try this in your script test.sh:
#!/bin/bash
testfunc() {
echo "test";
sudo sh -c "echo 'haha'";
echo "hooo";
}
testfunc;
Then call it with:
bash test.sh

Linux difference between when to use parentheses

Why do I get extra empty line when running 2). To me 1 is like 2. So why the extra line in 2)?
1)
export p1=$(cd $(dirname $0) && pwd)
# ^
echo p1
2)
export p2=$(cd $(dirname $0)) && pwd
# ^
echo p2
$echo $0
/bin/bash
$ echo $(cd $(dirname $0) && pwd)
/bin
$ echo $(cd $(dirname $0)) && pwd
/home/user
$
In the 1st expression it becomes echo $(cd /bin && pwd). Therefore the inner 2 commands execute in a subshell and return back the pwd value which is then echoed.
In the 2nd expression it gets reduced to echo $(cd /bin) && pwd. Therefore only the cd command executes in a subshell and returns nothing to echo (hence by default echo just prints an empty line). Since echo ran successfully(exit code=0) && results in true and pwd cmd is run in current shell and pwd gets printed
p1 captures the output of cd (empty) and pwd.
p2 only captures the output of cd, and then runs pwd without redirection.
echo p1 prints a literal p1 (with a newline). I guess you didn't actually copy-paste from your terminal, but instead typed in some thing else.
peter#tesla:~$ export p2=$(true) && pwd
/home/peter
peter#tesla:~$ echo "x${p2}x"
xx
cd in a subshell doesn't affect the parent shell's pwd, so I just substituted the true command to make it more readable.

Passing value from one shell script to other

This is what I am trying
script1
var=10
sh -x script2 "$var"
script2
su - someuser << EOF
1
cd dir
echo "This is : $1 Error" >> somefile
2
2
0
exit
EOF
Everything in script2 is executing. When I am checking the file "somefile" the output is
This is : Error
It is not showing the value of var
It is working fine for me:
cat s1
var=10
sh -x /tmp/s2 "$var"
cat s2
su - my_id << EOF
id
echo $1
EOF
./s1
+ su - my_id
+ 0<<
id
echo 10
my_id's Password: <<< su is prompting for my password
uid=222(my_id) gid=222(my_group) ...
10
Because you are not adding the #!/bin/xxxxx I believe the default is to either execute /bin/sh or maybe what $SHELL is set to. Check that both of those are what you expect. Maybe add the explicit #!/bin/ksh (or #!/bin/sh ...) to make sure you are getting the shell that you want / expect.

Variable is not being echoed

When I run the following script in Bash 3.2.48:
#!/bin/bash
export var1='var1'
echo "UID=$UID"
if [ x"$UID" != x"0" ]
then
export var2='var2'
while ! { sudo -v; }; do { sudo -v; }; done;
sudo $0
exit
fi
echo $var1
echo $var2
exit 0
What I get as output is:
UID=1000
UID=0
var1
Why is var2 not exported and echoed? I'm pretty sure that the same script worked with older Bash versions.
you enter first time with UID == 1000, you enter the if clause
you sudo to execute the script with UID == 0;
sudo doesn't preserve the environment if env_reset is set in /etc/sudoers (default in most distros). You need sudo -E to preserve env.
you exit (before echoing)
from the sudo call you enter with clean env.
you enter with UID == 0
you don't enter the if clause, var2 is not set
you echo the variables.
The answer is much more simpler than is seems: you never echo those vars (when not running as root, obviously), because you already exit :))
Try avoiding/minimizing confusion by adding more appropriate/concise debug statements. For instance, use a single echo that contains everything that's relevant to your problem (i.e. process ID, user ID, var1, var2):
#!/bin/bash
export var1='var1'
if [ "$UID" != "0" ] ; then
export var2='var2'
while ! { sudo -v; }; do { sudo -v; }; done;
sudo $0
# this is "the key exit" ;-)
#exit
fi
echo "pid=[$$] uid=[$UID] var1=[$var1] var2=[$var2]"
With the exit commented out you get what you expect (obviously, in the "parent" process, as the "child" one - the one running as "root" - never reaches that part of the code that exports var2):
pid=[12346] uid=[0] var1=[var1] var2=[]
pid=[12345] uid=[1] var1=[var1] var2=[var2]
++ sometimes running scripts in debug mode (bash -x) helps too ;-)

Resources