bash script executed by httpd does not read user environmental variables - bash

We have a script that is executed by httpd as the default ec2-user. However when executed the script does not see any of the environmental variables for that user
the variable is set under user ec2-user
myUseVarHome=/home/ec2-user
myScript.sh
#!/bin/bash
myFolder="${myUseVarHome}/test/www"
USER=$(whoami)
echo "Content-type: text/html"
echo ""
echo "hello $USER"
echo "myFolder=$myFolder"
executing script as ec2-user outputs
hello ec2-user
myFolder=/home/ec2-user/test/www
We then set httpd 2.4 conf
<IfModule unixd_module>
User ec2-user
Group ec2-user
</IfModule>
now call the script with
wget 127.0.0.1/myScript.sh
outputs
hello ec2-user
myFolder=/test/www
The output validates the httpd user is ec2-user, same as manually executing the script, however the env variable ${myUseVarHome} is blank or does not exist.
Is this expected behaviour or do we need to call the env variable another way when executed as httpd user?

bash acts differently whether it is a shell or a normal progamming language (like perl or python).
By designed, those settings in ~/.bash_profile, ~/.bashrc, etc. are for users to set things when bash plays the roll of a shell (login shell, interractive shell). Think about environment you have in a xterm (interractive shell) or in ssh sessions (login shell) or in consoles (login shell).
In other hand, bash is also a powerfull progamming language --think about many scripts for managing services in systemd-- which requires a different style of working. Example, when a developer write a system script or a bash program, he/she will not likely to source user defined ~/.bash_profile automatically. It is a normal program, not a shell. A normal program (including bash programs) would naturally inherrit setting in a current working evironement (shell), but not set them.
If we write a program for cron in bash --it is just happenly it is written in bash; in fact, we can write it in python or perl or any other progamming language-- then, we can have an option to sources bash's ~/.bash_profile (read: setting of user's shell, which happenly to be the same language of your programming language):
[ -f /home/user/.bash_profile ] && . /home/user/.bash_profile
However, what if that particular user do not use bash as his/her shell? He/she may use zsh, 'ksh,fish`, etc. So, that's practice does not really work when writing program for public use.
So, you can source ~/.bash_profile if you think it work. But, here, it is not about whether we are able to source a file, it is about how things should works in the system: the design concept. In short: we should view bash as something having 2 rolls: shell and progamming language. Then everything will be clear, easier to understand.
See: How to change cron shell sh to bash

Related

Effective Methods of changing Shells in UNIX

I used to work with UNIX a couple years ago, and I am just starting to get back into it again. I was wondering if anyone could help me with a question.
For example, if I am in bash, I say chsh --shell /bin/tcsh after this I am prompted to enter my password. If I try to say echo $SHELL it will not tell me I have changed shells. It still tells me I am in bash, not C shell. So I have to exit and restart. Once I log back it, then it tells I am in C shell.
Is there a more effective method to change shells? One that does not require me having to log in and out?
Thank you in advance.
chsh(1): change your login shell
Once you change your shell with chsh, it should automatically login to that shell every time you open a terminal.
If you want to use a different shell temporary, just run that shell directly: "tcsh", "zsh", etc..
If you want to use a particular shell for a script use shebang "#!".
Example -- The following on the first line of a shell script will ensure the script is run with sh (and you can do this for any shell available on your system):
#!/bin/sh
Always check your current shell by using :
echo $0
That way you will get the exact process ( your current shell ) you are running. If you print $SHELL it will return to you the default shell that will be open when you login to the server which unless that's what you need its not reliable.
ubuntu$ echo $SHELL
/bin/bash
ubuntu$ echo $0
-bash
ubuntu$ sh
\[\e[31m\]\u\[\e[m\]$ echo $SHELL
/bin/bash
\[\e[31m\]\u\[\e[m\]$ echo $0
sh
\[\e[31m\]\u\[\e[m\]$
Regards!

Spawning background process under different user in bash

I know I can run this command to spawn a background process and get the PID:
PID=`$SCRIPT > /dev/null 2>&1 & echo $!`
and to run a command under different user:
su - $USER -c "$COMMAND"
I don't want the script to run as root and I can't quite figure out how to combine the two and get the PID of the spawned process.
Thanks!
I think you want the runuser command. General syntax:
runuser -l userNameHere -c 'command'
I suspect that if you set your $SCRIPT variable to the above (with appropriate changes), your first command will do what you want.
To elaborate on: See the following: - stackoverflow.com/questions/9119885/…
See particularly the following quote from Chris Dodd:
Unfortunately there's no easy way to do this prior to bash version 4, when $BASHPID was
introduced. One thing you can do is to write a tiny program that prints its parent PID:...
If you have bash 4 and BASHPID, see $$ in a script vs $$ in a subshell
I don't have version 4, so I can't provide an example of it's usage.
Or write a tiny C program which execvs it's arguments and make it setuid to USER.
Or even make a setuid shell script (not generally recommended). Hopefully the USER is fixed; if not, get the source for runuser, this is essentially what runuser (not a POSIX command) does.
PID=`su - $USER -c "$SCRIPT > /dev/null 2>&1 & echo $!"`
The problems with the your use of su (above) include:
the $! is being executed in the context of the -c sub-shell of su, not the current shell where PID is,
you're requesting that your SCRIPT be run as a login shell, so you don't even know if USER's shell supports $!,
you have no control over the parent-child process chain that su (and the user's shell) create.
IOW, when you use
PID=`$SCRIPT > /dev/null 2>&1 & echo $!`
there's only one program involved, bash, and two (maybe three?) processes that you pretty much have complete control over. When you throw su into the mix, that changes things much more than is apparent on the surface -- bash and su support similar arguments, right?!?
For obvious reasons, su does mucho magic to protect it and its' children's environment from attacks; it doesn't even like being put in the background....
It's kind of late, but here is a two liner will work, seems to need to be two so that it doesn't wait for the $SCRIPT to complete:
su $USER -c "$SCRIPT 2>&1 & >> $LogOrNull echo $! > /some/writeable/path"
PID="$(cat /some/writeable/path)"
/some/writeable/path will need to be writeable by $USER
And the user running these commands will need to have read access

source .bashrc from .cshrc

Is it possible to source a .bshrc file from .cshrc in a non-interactive session?
I'm asking because tcsh is our default shell at work and the .cshrc has to be used to set up the environment initially.
However, I am not really familiar with the tcsh and I have my own set-up in bash, so right now I have the following lines at the end of my .cshrc file:
if ( $?prompt && -x /bin/bash) then
exec /bin/bash
endif
This works fine, loading my environment from .bashrc and giving me a bash prompt for interactive sessions but now I also need the same set-up for non-interactive sessions, e.g. to run a command remotely via SSH with all the correct PATHs etc.
I can't use 'exec' in that case but I can't figure out how to switch to bash and load the bash config files "non-interactively".
All our machines share the same home directory, so any changes to my local *rc files will affect the remote machiens as well.
Any ideas welcome - thank you for your help!
After some more research I'm now quite sure that this won't work, but of course feel free to prove me wrong!
To load the environment in bash I have to switch to a bash shell. Even if that is possible "in the background", i.e. without getting a prompt, it would still break any tcsh commands which would then be attempted to execute under bash.
Hmmmm, back to the drawing board...
If $command is set there are arguments to csh, so it is a remote shell command. This works for me in .cshrc:
if ($?command) then
echo Executing non-interactive command in bash: $command $*
exec /bin/bash -c "${command} $*"
endif
echo Interactive bash shell
exec bash -l
Test:
$ ssh remotehost set | grep BASH
BASH=/bin/bash
...
proves that it ran in Bash.

Why does using set -e cause my script to fail when called in crontab?

I have a bash script that performs several file operations. When any user runs this script, it executes successfully and outputs a few lines of text but when I try to cron it there are problems. It seems to run (I see an entry in cron log showing it was kicked off) but nothing happens, it doesn't output anything and doesn't do any of its file operations. It also doesn't appear in the running processes anywhere so it appears to be exiting out immediately.
After some troubleshooting I found that removing "set -e" resolved the issue, it now runs from the system cron without a problem. So it works, but I'd rather have set -e enabled so the script exits if there is an error. Does anyone know why "set -e" is causing my script to exit?
Thanks for the help,
Ryan
With set -e, the script will stop at the first command which gives a non-zero exit status. This does not necessarily mean that you will see an error message.
Here is an example, using the false command which does nothing but exit with an error status.
Without set -e:
$ cat test.sh
#!/bin/sh
false
echo Hello
$ ./test.sh
Hello
$
But the same script with set -e exits without printing anything:
$ cat test2.sh
#!/bin/sh
set -e
false
echo Hello
$ ./test2.sh
$
Based on your observations, it sounds like your script is failing for some reason (presumably related to the different environment, as Jim Lewis suggested) before it generates any output.
To debug, add set -x to the top of the script (as well as set -e) to show commands as they are executed.
When your script runs under cron, the environment variables and path may be set differently than when the script is run directly by a user. Perhaps that's why it behaves differently?
To test this: create a new script that does nothing but printenv and echo $PATH.
Run this script manually, saving the output, then run it as a cron job, saving that output.
Compare the two environments. I am sure you will find differences...an interactive
login shell will have had its environment set up by sourcing a ".login", ".bash_profile",
or similar script (depending on the user's shell). This generally will not happen in a
cron job, which is usually the reason for a cron job behaving differently from running
the same script in a login shell.
To fix this: At the top of the script, either explicitly set the environment variables
and PATH to match the interactive environment, or source the user's ".bash_profile",
".login", or other setup script, depending on which shell they're using.

Tell if a user has SUed in a shell script?

I have a script which executes a git-pull when I log in. The problem is, if I su to a different user and preserve my environment with an su -lp, the script gets run again and usually gets messed up for various reasons because I'm the wrong user. Is there a way to determine in a shell script whether or not I'm currently SUing? I'm looking for a way that doesn't involve hard coding my username into the script, which is my current solution. I use Bash and ZSH as shells.
You could use the output of the who command with the id command:
WHO=`who am i | sed -e 's/ .*//'`
ID_WHO=`id -u $WHO`
ID=`id -u`
if [[ "$ID" = "$ID_WHO" ]]
then
echo "Not su"
else
echo "Is su"
fi
if test "$(id -u)" = "0";
: # commands executed for root
else
: # commands executed for non root
fi
If you are changing user identities with an suid executable, your real and effective user id will be different. But if use use su (or sudo), they'll both be set to the new user. This means that commands that call getuid() or geteuid() won't be useful.
A better method is to check who owns the terminal the script is being run on. This obviously won't work if the process has detached from it's terminal, but unless the script is being run by a daemon, this is unlikely. Try stat -c %U $(tty). I believe who am i will do the same thing on most Unix-like OSes as well.
You can use "$UID" environment variable.
If its value is ZERO, then the user has SUDOed.. Bcos root as $UID==0
Well.... on linux, if I su to another user the process su is in the new user's process list.
sudo... doesn't leave such pleasant things for you.
I'm using zsh... but I don't think anything in this is shell specific.
if:
%ps | grep " su$"
returns anything, then you're running in an su'd shell.
Note: there is a space before su$ in that to exclude command simply ending in su. Doesn't guard against any custom program/script called su, though.

Resources