BASH: Check if user is root even when using fakeroot [duplicate] - linux

This question already has answers here:
How to check if running as root in a bash script
(21 answers)
Closed 24 days ago.
How can I check whether a user is root or not within a BASH script?
I know I can use
[[ $UID -eq 0 ]] || echo "Not root"
or
[[ $EUID -eq 0 ]] || echo "Not root"
but if the script was invoked via fakeroot, UID and EUID are both 0 (of course, as fakeroot fakes root privileges).
But is there any way to check whether the user is root? Without trying to do something only root can do (i.e. creating a file in /)?

Fakeroot sets custom LD_LIBRARY_PATH that contains paths to libfakeroot. For example:
/usr/lib/x86_64-linux-gnu/libfakeroot:/usr/lib64/libfakeroot:/usr/lib32/libfakeroot
You can use this to detect if application is running inside the fakeroot iterating by paths and looking for libfakeroot.
Sample code:
IS_FAKEROOT=false
for path in ${LD_LIBRARY_PATH//:/ }; do
if [[ "$path" == *libfakeroot ]]; then
IS_FAKEROOT=true
break
fi
done
echo "$IS_FAKEROOT"

Here, The below script will find the user is root or not.
#!/bin/bash
touch /checkroot 2>/dev/null
uid=`stat -c "%u" /checkroot 2>/dev/null`
if [ "$uid" = "0" ]
then
echo "Root user"
else
echo "Not a root user"
fi
rm /checkroot 2>/dev/null
In the above example, I will try to create a file in the root directory, if I am not a root user and I don't have a permission it will give error, I was redirect that error to the /dev/null.
If the user have the permission, the file will be created. Then using the stat to get the user id for that file, and store that into the variable uid.
Using that variable in the if condition I will check.
If the temporary file will be created the rm command will remove that file.
But make sure the file is not already exist in the root directory.

Related

Differentiation on whether directory exists and permission error

Looking for a very simple way to check whether a file/directory exists while evaluating user permissions, returning different (code) errors:
There is command test that checks for permissions but fails to provide a better return code for case where file does not exist:
$ test -r /tmp/; echo $? # 0
$ test -r /tmp/root/; echo $? # 1
$ test -r /tmp/missing/; echo $? # 1
I am looking for something similar to ls where I get a different message for different errors:
$ ls /tmp/root
ls: root: Permission denied
$ ls /tmp/missing
ls: /tmp/missing: No such file or directory
I like the differentiation but the error code is 1 in both. To properly handle each error, I have to parse stderr which is honestly a very inelegant solution.
Isn't there a better and graceful way of doing this?
Something close to a pythonic way looks something like this:
import os
os.listdir("/tmp/root/dir/") # raises PermissionError
os.listdir("/tmp/foo/") # raises FileNotFoundError
Read the manual some more. There's also -d to specifically check whether the target is a directory, and a slew of other predicates to check for symlinks, device nodes, etc.
testthing () {
if ! [[ -e "$1" ]]; then
echo "$1: not found" >&2
return 2
elif ! [[ -d "$1" ]]; then
echo "$1: not a directory" >&2
return 4
elif ! [[ -r "$1" ]]; then
echo "$1: permission denied" >&2
return 8
fi
return 0
}
Usage:
testthing "/root/no/such/directory"
Notice that [[ is a Bash built-in which is somewhat more robust and versatile than the legacy [ aka test.
It's hard to predict what the priorities should be, but if you want the comparisons in a different order, by all means go for it. It is unavoidable that the shell cannot correctly tell the precise status of a directory entry when it lacks read access to the parent directory. Maybe solve this from the caller by examining the existence and permissions of every entry in the path, starting from the root directory.
The shell and standard utilities do not provide a command that does everything you seem to want:
with a single command execution,
terminate with an exit status that reports in detail on the existence and accessability of a given path,
contextualized for the current user,
accurately even in the event that a directory prior to the last path element is untraversable (note: you cannot have this one no matter what),
(maybe) correctly for both directories and regular files.
The Python os.listdir() doesn't do all of that either, even if you exclude the applicability to regular files and traversing untraversible directories, and reinterpret what "exit status" means. However, os.listdir() and ls both do demonstrate a good and useful pattern: to attempt a desired operation and deal with any failure that results, instead of trying to predict what would happen if you tried it.
Moreover, it's unclear why you want what you say you want. The main reason I can think of for wanting information about the reason for a file-access failure is user messaging, but in most cases you get that for free by just trying to perform the wanted access. If you take that out of the picture, then it rarely matters why a given path is inaccessible. Any way around, you need to either switch to an alternative or fail.
If you nevertheless really do want something that does as much as possible of the above, then you probably will have to roll your own. Since you expressed concern for efficiency in some of your comments, you'll probably want to do that in C.
Given:
$ ls -l
total 0
-rw-r--r-- 1 andrew wheel 0 Mar 22 12:01 can_read
---xr-x--x 1 andrew wheel 0 Mar 22 12:01 root
drwxr-xr-x 2 andrew wheel 64 Mar 22 13:09 sub
Note that permissions are by user for the first three, group for the second three and other or world for the last three.
Permission Denied error is from 1) Trying to read or write a file without that appropriate permission bit set for your user or group or 2) Tying to navigate to a directory without x set or 3) Trying to execute a file without appropriate permission.
You can test if a file is readable or not for the user with the -r test:
$ [[ -r root ]] && echo 'readable' || echo 'not readable'
not readable
So if you only are concerned with user permissions, -r, -w and -x test are what you are looking for.
If you want to test permissions generally, you need to use stat.
Here is a simple example with that same directory:
#!/bin/bash
arr=(can_read root sub missing)
for fn in "${arr[#]}"; do
if [[ -e "$fn" ]]
then
p=( $(stat -f "%SHp %SMp %SLp" "$fn") )
printf "File:\t%s\nUser:\t%s\nGroup:\t%s\nWorld:\t%s\nType:\t%s\n\n" "$fn" "${p[#]}" "$(stat -f "%HT" "$fn")"
else
echo "\"$fn\" does not exist"
fi
done
Prints:
File: can_read
User: rw-
Group: r--
World: r--
Type: Regular File
File: root
User: --x
Group: r-x
World: --x
Type: Regular File
File: sub
User: rwx
Group: r-x
World: r-x
Type: Directory
"missing" does not exist
Alternatively, you can grab these values directly from the drwxr-xr-x type data with:
for fn in "${arr[#]}"; do
if [[ -e "$fn" ]]
then
p=$(stat -f "%Sp" "$fn")
typ="${p:0:1}"
user="${p:1:3}"
grp="${p:4:3}"
wrld="${p:7:3}"
else
echo "\"$fn\" does not exist"
fi
done
In either case, you can then test the individual permissions with either Bash string functions, Bash regex, or get the octal equivalents and use bit masks.
Here is an example:
for fn in "${arr[#]}"; do
if [[ -e "$fn" ]]
then
p=$(stat -f "%Sp" "$fn")
user="${p:1:3}"
ty="$(stat -f "%HT" "$fn")"
printf "%s \"$fn\" is:\n" "$ty"
[[ $user =~ 'r' ]] && echo " readable" || echo " not readable"
[[ $user =~ 'w' ]] && echo " writeable" || echo " not writeable"
[[ $user =~ 'x' ]] && echo " executable" || echo " not executable"
else
echo "\"$fn\" does not exist"
fi
done
Prints:
Regular File "can_read" is:
readable
writeable
not executable
Regular File "root" is:
not readable
not writeable
executable
Directory "sub" is:
readable
writeable
executable
"missing" does not exist
(Note: stat tends to be platform specific. This is BSD and Linux will have different format strings...)
An example of use.
for d in 1 2 3; do
if [[ -e $d ]]; then printf "%s exists" $d
[[ -r $d ]] && echo " and is readable" || echo " but is not readable"
else echo "$d does not exist"
fi
stat -c "%A %n" $d
done
1 exists and is readable
drwxr-xr-x 1
2 exists but is not readable
d--------- 2
3 does not exist
stat: cannot stat ‘3’: No such file or directory
If you absolutely have to have it in one step with differentiated exit codes, write a function. (a/b is there and has accessible permissions.)
$: stat -c "%A %n" ? . a/b # note there is no directory named 3
drwxr-xr-x 1
drwxr-xr-x 2
drwxr-xr-x a
drwxrwxrwt .
drwxr-xr-x a/b
$: doubletest() { if [[ -e "$1" ]]; then [[ -r "$1" ]] && return 0 || return 2; else return 1; fi; }
$: result=( "exists and is readable" "does not exist" "exists but is unreadable" ) # EDITED - apologies, these were out of order
$: for d in . a a/b 1 2 3; do doubletest $d; echo "$d ${result[$?]}"; done
. exists and is readable
a exists and is readable
a/b exists and is readable
1 exists and is readable
2 exists and is readable
3 does not exist
$: chmod 0000 a
$: for d in . a a/b 1 2 3; do doubletest $d; echo "$d ${result[$?]}"; done
. exists and is readable
a exists but is unreadable
a/b does not exist
1 exists and is readable
2 exists but is unreadable
3 does not exist
"does not exist" for a/b is because a does not have read permissions, so there is no way for any tool to know what does or does not exist in that directory short of using root privileges.
$ sudo stat -c "%A %n" ? . a/b # sudo shows a/b
drwxr-xr-x 1
drwxr-xr-x 2
d--------- a
drwxrwxrwt .
drwxr-xr-x a/b
In that case your problem isn't the tool, it's that the tool can't do what you are asking it to do.

What is efficient way to ensure bash script variable is valid permission bits?

I'm inexperienced with bash scripts, mostly cobbling things together from google searches. The following simple script creates file foo and changes its permission bits to whatever the script's $1 is. Can an experienced bash scripter please advise what would be a proper and/or efficient way to ensure that $1 is valid permission bits, i.e. a valid arg1 to chmod?
#!/bin/sh
#mkfoo.sh
if [ $# -eq 0 ]
then
return
fi
perm=$1
touch ./foo
chmod "${perm}" ./foo
As part of my inexperience, I'm unclear when/why variables are strings or integers. A string-validation approach feels infeasible because of the number of values the permission bits could be. What comes to mind is that I'd want to validate whether $1 is an integer, and if so, that it's between 0 and 777. But I'm unclear if that's a sufficient/efficient validation: I've got a rudimentary understanding of linux file permissions, and I'm led to believe there are lots of subtleties. Grateful for help.
If you only want to allow numeric permissions, you can check them with a pattern check:
case "$perms" in
[0-7][0-7][0-7])
touch ./foo
chmod "${perm}" ./foo
;;
*)
echo "Invalid permissions $perms"
;;
esac
From your comments, your goal is to give up on the chmod if the permissions specified are invalid.
chmod "$1" ./foo 2>/dev/null
2 is the file descriptor for stderr. Redirecting to /dev/null will let it fail silently. chmod, as stated in the comments, does its own validation on if the permissions are acceptable.
This script will accept any valid permissions that chmod allows.
It will create the file (if it doesn't already exist) and attempt to set the permissions. If setting the permissions fails the file is removed.
It requires exactly two arguments; the filename to create and the permissions to set.
This allows you to use symbolic or 4-digit permissions, such as create foo u+x,g+rwx (assuming the script is named "create") or create foo 2640
This is pretty simple, as an example. I often include a usage() function which I would call in place of the first echo. You could also include default permissions if the seconds argument was omitted.
#!/bin/sh
if [ $# -ne 2 ]
then
echo "output a usage message, don't just return"
exit
fi
if [ -e "${1}" ]
then
echo "${1} already exists"
exit
fi
touch ./"${1}"
if chmod "${2}" "${1}" > /dev/null 2>&1
then
echo "${1} created with permissions ${2}"
else
rm "${1}"
echo "${1} not created: invalid permissions: ${2}"
fi
As others have mentioned, chmod does its own validation so it'd probably be best to just use that, but if you absolutely want to do this in a way similar to the code you provided you can do:
#!/bin/sh
if [ $# -eq 0 ]
then
echo "Invalid input"
exit 1
fi
if ! [[ $1 =~ [0-7][0-7][0-7] ]]
then
echo "Invalid input"
exit 1
else
perm=$1
fi
touch ./foo
chmod "${perm}" ./foo
This will ensure that each digit is valid.

Execute Command In Multiple Directories

I have a large set of single install WordPress sites on my Linux server. I want to create text files that contain directory names for groups of my sites.
For instance all my sites live in /var/www/vhosts and I may want to group a set of 100 websites in a text file such as
site1
site2
site3
site4
How can I write a script that will loop through only the directories specified in the group text files and execute a command. My goal is to symlink some of the WordPress plugins and I don't want to have to manually go directory by directory if I can just create groups and execute the command within that group of directories.
For each site in the group file, go to the /wp-content/plugins folder and execute the symlink command specified.
Depending on your goals, you may be able to achieve that with a one-liner using find and an -exec action. I tend to like doing it as a Bash loop, because it is easier to add additional commands instead of having a long and unwieldy command doing it all, as well as handle errors.
I do not know if this is what you intend, but here is a proposal.
#!/bin/bash
# Receives site group file as argument1
# Receives symlink name as argument 2
# Further arguments will be passed to the command called
sites_dir="/var/www/vhosts"
commands_dir="wp-content/plugins"
if ! [[ -f $1 ]] ; then
echo "Site list not found : $1"
exit
fi
while IFS= read -r site
do
site_dir="$sites_dir/$site"
if ! [[ -d $site_dir ]] ; then
echo "Unknown site : $site"
continue
fi
command="$site_dir/$commands_dir/$2"
if ! [[ -x $command ]] ; then
echo "Missing or non-executable command : $command"
continue
fi
"$command" "${#:3}"
done <"$1"

How to check directory exist or not in linux.? [duplicate]

This question already has answers here:
How do I check if a directory exists or not in a Bash shell script?
(35 answers)
Closed 2 years ago.
Given a file path (e.g. /src/com/mot), how can I check whether mot exists, and create it if it doesn't using Linux or shell scripting??
With bash/sh/ksh, you can do:
if [ ! -d /directory/to/check ]; then
mkdir -p /directory/toc/check
fi
For files, replace -d with -f, then you can do whatever operations you need on the non-existant file.
Check for directory exists
if [ -d "$DIRPATH" ]; then
# Add code logic here
fi
Check for directory does not exist
if [ ! -d "$DIRPATH" ]; then
# Add code logic here
fi
mkdir -p creates the directory without giving an error if it already exists.
Well, if you only check for the directory to create it if it does not exist, you might as well just use:
mkdir -p /src/com/mot
mkdir -p will create the directory if it does not exist, otherwise does nothing.
test -d /src/com/mot || mkdir /src/com/mot
This is baisc, but I think it works. You'll have to set a few variables if you're looking to have a dynamic list to cycle through and check.
if [ -d /src/com/mot ];
then
echo Directory found
else
mkdir /src/com/mot
fi
Hope that's what you were looking for...

bash: fail if script is not being run by root

I have a bash script that installs some software. I want to fail as soon as possible if it is not being run by root. How can I do that?
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" 1>&2
exit 1
fi
Source: http://www.cyberciti.biz/tips/shell-root-user-check-script.html
After digging around on this, the consensus seems to be that there is no need to use id -u in bash, as the EUID (effective user id) variable will be set. As opposed to UID, the EUID will be 0 when the user is root or using sudo. Apparently, this is around 100 times faster than running id -u:
#!/bin/bash
if (( EUID != 0 )); then
echo "You must be root to do this." 1>&2
exit 1
fi
Source: https://askubuntu.com/questions/30148/how-can-i-determine-whether-a-shellscript-runs-as-root-or-not

Resources