I have a makefile and configure shell in my project. I wrote code to ask user to run configure shell in root mode using the following code.
[ "$(whoami)" != "root" ] && exec sudo -- "$0" "$#"
But when I run 'make install'
I need to ask user to run from root mode.
So I just copied the code from the configure shell and copied it in another shell script file called 'runasroot.sh'. Then I run this shell script from the make install.
install:
#echo About to install XXX Project
./runasroot.sh
find . -name "*.cgi" -exec cp {} $(SCRIPTDEST)/ \;
When I run the above code, I got the below error.
About to install XXX Project
./runasroot.sh \;
make: *** [install] Error 1
runasroot.sh
#!/bin/bash
[ "$(whoami)" != "root" ] && exec sudo -- "$0" "$#"
target:
#if ! [ "$(shell id -u)" = 0 ];then
#echo "You are not root, run this target as root please"
exit 1
fi
Explanations
You can use ifneq to check that the user is not root and echo a message for instance, not performing the actual action is the user was indeed a root user. Since the id of the root user is usually 0 on a UNIX-like operating system, we can check that the user ID is 0 (or not) in a conditional.
Proposal solution
install:
ifneq ($(shell id -u), 0)
#echo "You must be root to perform this action."
else
#echo "TODO: The action when the user is root here..."
endif
Output
$ make install
You must be root to perform this action.
$ sudo make install
TODO: The action when the user is root here...
External resources
Conditional Parts of Makefiles
The shell Function
Recipe Echoing
Man for the id function
What is a root user
There are several mistakes done here.
The first problem is what you're trying to do. It is very bad form to require root as part of the build process. For the build part, try to not require root for compiling anything. If you need to create special files as part of the packaging, use fakeroot or fakeroot-ng to get the same effect without any actual privilege escalation.
For install, just let the user run your entire make file as root, if she so chooses. Many operations that usually require root sometimes don't. For example, make install does not require root if the install is to a DESTDIR where the user has privileges.
If you are dead set on doing this anyway, however, your flow is completely wrong. While runasroot.sh does exactly what you want it to do, it only does so for itself. When you look at your make receipt:
install:
#echo About to install XXX Project
./runasroot.sh # <<-- this line runs as root
find . -name "*.cgi" -exec cp {} $(SCRIPTDEST)/ \;
The runasroot.sh line runs as root, but the find line is a different process, and is completely unaffected.
That would be true for regular shell script, but it's doubly true for a make receipt. In a shell script, each command gets its own process. In a make receipt, each command gets its own shell. Your runasroot.sh does not, and cannot, affect the privileges under which the find runs.
So, what you are trying to do is both impossible and unwanted. Just try to install and fail if you don't have enough permissions.
Automatically call make again with sudo and all parameters preserved if user is not root:
install:
ifneq ($(shell id -u), 0)
sudo make $#
else
echo Your commands here...
endif
Related
I'm a beginner in Linux scripting. Somehow I can't get the right search string to find the answer.
I want to run a script relative to a specific user's directory like:
~user/rel/path/to/script.sh
but I don't know what "~user" will translate to. It may even contain spaces. Should I quote it? And if so, how? I always get the file or folder does not exist error when I try to use quotes.
Edit
This is not a duplicate I think.
My concern was that running the following with quotes:
"~user/rel/path/to/script.sh"
gives me "file or folder not found" error. But I don't know, what ~user will translate to. (The script will be called on many different computers. The username is given but the home directory may be changed freely by the owner of each computer.) So I was afraid (as a Linux scripting BEGINNER!!!) to run it without quotes like:
~user/rel/path/to/script.sh
The most down-voted answer (Java system properties and environment variables) actually helped me most. I just needed to confirm that it works the same way on Linux. So I installed a test VM in VirtualBox and tried:
cd /
sudo mkdir -p "test home dir/myuser"
sudo adduser myuser
sudo chown myuser:myuser "test home dir/myuser"
sudo usermod -d "/test home dir/myuser" myuser
su myuser
cd ~
echo '#!/bin/bash -x
echo "here!"
' > test.sh
chmod +x test.sh
exit
cd /
~myuser/test.sh
And it worked!
On Mac OS you don't need to quote. I'm not sure about Linux. However, if
ls ~user
would result in /dir with space/user/ then
sh ~user/rel/path/to/script.sh
would be
sh /dir\ with\ space/user/rel/path/to/script.sh
and this executes if you have set the execution flag on script.sh accordingly.
I'm facing an issue with creating init.d service. Following is my run.sh file which executes completely fine (As root user)
mvn install -DskipTests
mvn exec:java
But when I execute same file as service in init.d (service run start). I get
mvn command not found
Following is my start method
start() {
if [ -f /var/run/$PIDNAME ] && kill -0 $(cat /var/run/$PIDNAME); then
echo 'Service already running' >&2
return 1
fi
echo 'Starting service…' >&2
CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
su -c "$CMD" $RUNAS > "$PIDFILE"
echo 'Service started' >&2
}
Link to complete script which i'm using
https://gist.githubusercontent.com/naholyr/4275302/raw/9df4ef3f4f2c294c9585f11d1c8593b62bdd52d3/service.sh
RUN AS value is set as root
When you run a command using sudo you are effectively running it as the superuser or root.
The reason that the root user is not finding your command is likely that the PATH environment variable for root does not include the directory where maven is located (quite evident as in the comments). Hence the reason for command not found error.
Add PATH to your script and that it includes /opt/integration/maven/apache-maven-3.3.9/bin. Since the init script won't share the PATH environment variable with the rest of the system (since it being run much ahead of the actual updates of $PATH in the .bash_profile) you need to set it directly on your script and make sure maven is in there, for example, add the below line to the init script in the beginning.
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/opt/integration/maven/apache-maven-3.3.9/bin
This question already has answers here:
How to check if running as root in a bash script
(21 answers)
Closed 7 years ago.
Say you have a shell script that could potentially require root privileges. You don't want to force the user to run it as sudo. However, if it does require that privilege, you want to prompt the user to enter their password, rather than complaining and forcing them to re-enter the command with sudo.
How would you go about doing this in a Bash script? sudo true seems to work, but it feels like a hack. Is there a better way?
Here's what I often do. This is loose pseudo-code but should give you the idea:
# myscript -- possibly execute as sudo
if (passed -e option) then
read variables from envfile
else
...
need_root = ...
# set variables ...
if ($need_root && $uid != 0) then
env [or whatever] > /tmp/envfile
exec sudo myscript -e/tmp/envfile ...
fi
fi
# stuff to execute as root [or not] ...
The command
sudo -nv
checks whether the user has current sudo credentials (-v), but will fail rather than prompting if access has expired (-n).
So this:
if sudo -nv 2>/dev/null && sudo -v ; then
sudo whoami
else
echo No access
fi
will check whether the user's sudo credentials are current, and prompt for a password only if they're not.
There is a possible race condition: the user's credentials could expire just after the check.
As ghoti points out in a comment, this may not work if the sudoers file is set up to allow only certain commands to be executed. For that and other reasons, be sure to check whether each sudo command succeeded or failed.
If your plan is to use sudo for privilege escalation, one wrinkle you may have to deal with is that that sudo can be set up to permit root access to some commands and not others. For example let's imagine you've got a server that runs VirtualBox, with different people managing the applications than are managing the OS. Your sudoers file might contain something like the following:
Cmnd_Alias SAFE = /bin/true, /bin/false, /usr/bin/id, /usr/bin/who*
Cmnd_Alias SHUTDOWN = /sbin/shutdown, /sbin/halt, /sbin/reboot
Cmnd_Alias SU = /bin/su, /usr/bin/vi*, /usr/sbin/visudo
Cmnd_Alias SHELLS = /bin/sh, /bin/bash, /bin/csh, /bin/tcsh
Cmnd_Alias VBOX = /usr/bin/VBoxManage
%wheel ALL=(ALL) ALL, !SU, !SHELLS, !SHUTDOWN
%staff ALL=(ALL) !SU, !SHELLS, NOPASSWD: SAFE
%operator ALL=(ALL) SAFE, SHUTDOWN
%vboxusers ALL=(ALL) NOPASSWD: VBOX
In this case, a member of the vboxusers unix group will always get a successful response to a sudo -nv, because of the existence of the NOPASSWD entry for that group. But a member of that group running any other command than VBoxManage will get a password challenge and a security warning.
So you need to determine whether the command you need to run can be run without a password prompt. If you don't know how sudo is configured on the system where your script is running, the canonical test is to run the command. Running sudo -nv will only tell you whether you are authenticated; it won't tell you what commands you have access to.
That said, if you can safely rely on a sudo configuration where, say, membership in wheel group gives you access to all commands, for example with:
%wheel ALL=(ALL) ALL
then you can use sudo -nv to test for escalation capabilities. But your script might have some things that it runs as root, and some things it doesn't. You might also want to consider other tools besides sudo for privilege escalation.
One strategy might be to set a variable to preface commands if the need is there, but leave the variable blank if you're already root (i.e. running the entire script inside sudo).
if ! which sudo >/dev/null 2>/dev/null; then
PM_SU_CMD="su - root -c"
elif sudo -nv 2>/dev/null; then
PM_SU_CMD="sudo"
else
echo "ERROR: I can't get root." >&2
exit 1
fi
Of course, if we are already root, unset this to avoid potential conflict:
[ `ps -o uid= $$` -eq 0 ] && unset PM_SU_CMD
(Note that this is a query of the system's process table; we don't want to rely on the shell's environment, because that can be spoofed.)
Then, certain system utilities might be made more easily available using functions:
# Superuser versions for commands that need root privileges
find_s () { $PM_SU_CMD "/usr/bin/find $*"; }
mkdir_s () { $PM_SU_CMD "/bin/mkdir -p $1"; }
rm_s () { $PM_SU_CMD "/bin/rm $*"; }
Then within your script, you'd simply use the _s version of things that need to be run with root privileges.
This is of course by no means the only way to solve this problem.
I'm creating an install script for a Linux game. As part of the installation, I change the suid permissions of the game executable to the "games" group so that the game can update the highscore file even when its run by regular users.
Right now my Makefile.am looks like this:
AUTOMAKE_OPTIONS = foreign
SUBDIRS = src man
install-exec-hook:
chgrp games #bindir#/xjump
chmod +s #bindir#/xjump
mkdir -p #localstatedir#/xjump
touch #localstatedir#/xjump/record
chmod 660 #localstatedir#/xjump/record
chgrp games #localstatedir#/xjump/record
The problem I am having is that the chgrp command requires administrative privileges. If I am installing the game globally using sudo make install then its all works fine but if I change the prefix to somewhere in my home directory and try to do a regular make install it fails due to the chgrp
chgrp: changing group of ‘/home/hugo/Desktop/aaa/bin/xjump’: Operation not permitted
Since the locally installed version of the game only has a single player, I don't really need to do the chgrp thing. Is there a way to detect if the makefile is being run without root privileges and skip the permission changing? Or should I add a configuration flag to my configure script instead of trying to fix this permission issue automatically?
When the commands fail, you did not run as root. It seems nothing goes wrong, you just do not want the error messages.
You can see who you are, but the easiest solution is redirecting the output
Finish with true, so your step doesn't fail:
chgrp games #localstatedir#/xjump/record 2>/dev/null || true
If you run "whoami", you would be able to find out who the current user is.
runner=`whoami` ;
if test $$runner == "root" ;
then
chgrp games #localstatedir#/xjump/record
fi
Which pattern is preferable:
#!/bin/bash
echo Installing blah
apt-get install -y blah
...which will fail if run without root perms, or:
#!/bin/bash
echo Installing blah
sudo apt-get install -y blah
...which will succeed as long as the user has sudo access.
I have tended to use the second pattern, but it seems to be rare, so I'm asking what its disadvantages are. The benefits I see are:
It's clear which commands actually require superuser permissions to run (useful if the reader wants to pull the script apart)
Saves a few keystrokes for the user.
I guess downsides include that the use of root permissions might be surprising ("I didn't type sudo, so I didn't expect anything like apt-get to be run...."). What else?
I will prefer second Pattern,
because running few commands with sudo is better rather then running entire script with root permissions in which some commands are needless to have root access and so if you perform these commands with root user further using that command outputs will again need root access or you will have to change the ownership.
So I will prefer second approach where sudo is necessary do it, else go local user.
You could always test if the user is root (id -u gets the uid of the current user, and 0 is root), and run sudo if they aren't -
#!/bin/bash
echo Installing blah
CMD=""
if [ "$(id -u)" != "0" ]; then
CMD="sudo"
fi
${CMD} apt-get install -y blah
The more common approach would be to exit with an error condition if they aren't root,
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
echo "sudo $0 (must run as root)"
exit 1
fi
echo Installing blah
apt-get install -y blah
The second option is the correct option. Commands that don't require root access should not be run as root just to simplify your script. Commands that do require root access (or access by any specific user, for that matter) should be run by sudo. For clarity, you can use the -p option to sudo to present a custom prompt which can explain exactly why the user is asked for their password.
Option two is also preferable because sudo is highly configurable, and the user may already have permission to run specific commands with sudo without a password, so it's possible the user would not be inconvenienced by a password prompt. It's less likely that the user is allowed to sudo arbitrary commands (such as your script) without a password.