I have a makeself script which I expect to be run as root; It's a desktop installer.
At the end of the script, the software which was recently installed to the filesystem tries to launch in user-space.
This works well using sudo -u $(logname) /path/to/application (or alternately sudo -u $SUDO_USER ... in Ubuntu 16.04) however a critical environmental variable from the user is missing:
GNOME_DESKTOP_SESSION_ID
I need GNOME_DESKTOP_SESSION_ID because the child process belongs to Java and Java uses this environmental variable for detecting the GtkLookAndFeel.
However attempts to use sudo -i have failed.
From some basic tests, the GNOME_DESKTOP_SESSION_ID doesn't appear to be a natural environmental variable when this users logs in. For example, if I CTRL+ALT+F1 to a terminal, env |grep GNOME yields nothing whereas XTerm and gnome-terminal both yield GNOME_DESKTOP_SESSION_ID.
How can I get a hold of this GNOME_DESKTOP_SESSION_ID variable from within the installer script without requiring users to pass something such as the -E parameter to the sudo command?
Note, although GtkLookAndFeel is the primary look and feel for Linux, I prefer not to hard-code the export JAVA_OPTS either, I prefer to continue to fallback onto Oracle's detection techniques for support, longevity and scalability reasons.
Update: In Ubuntu, GNOME_DESKTOP_SESSION_ID lives in /usr/share/upstart/sessions/xsession-init.conf
initctl set-env --global GNOME_DESKTOP_SESSION_ID=this-is-deprecated
Which leads to using initctl get-env to retrieve it. Unfortunately this does not help within a new sudo shell, nor does any (optimistic) attempt at dbus-launch.
It turns out this is a two-step process...
Read the user's UPSTART_SESSION environmental variables from /proc/$pid/environ
Then export UPSTART_SESSION and call initctl --user get-env GNOME_DESKTOP_SESSION_ID
To make this a bit more scalable to other variables, I've wrapped this into a bash helper function. This function should assist fetching other user-environment variables as well. Word of caution, it won't work if the variable's value has a space in the name.
In the below example, only UPSTART_SESSION and GNOME_DESKTOP_SESSION_ID are required to answer the question.
Once sudo_env is called, the next call to sudo -u ... must be changed to sudo -E -u .... The -E will import the newly exported variables for use by a child process.
# Provide user environmental variables to the sudo environment
function sudo_env() {
userid="$(logname 2>/dev/null || echo $SUDO_USER)"
pid=$(ps aux |grep "^$userid" |grep "dbus-daemon" | grep "unix:" |awk '{print $2}')
# Replace null delimiters with newline for grep
envt=$(cat "/proc/$pid/environ" |tr '\0' '\n')
# List of environmental variables to use; adjust as needed
# UPSTART_SESSION must come before GNOME_DESKTOP_SESSION_ID
exports=( "UPSTART_SESSION" "DISPLAY" "DBUS_SESSION_BUS_ADDRESS" "XDG_CURRENT_DESKTOP" "GNOME_DESKTOP_SESSION_ID" )
for i in "${exports[#]}"; do
# Re-set the variable within this session by name
# Careful, this technique won't yet work with spaces
if echo "$envt" | grep "^$i=" > /dev/null 2>&1; then
eval "$(echo "$envt" | grep "^$i=")" > /dev/null 2>&1
export $i > /dev/null 2>&1
elif initctl --user get-env $i > /dev/null 2>&1; then
eval "$i=$(initctl --user get-env $i)" > /dev/null 2>&1
export $i > /dev/null 2>&1
fi
echo "$i=${!i}"
done
}
You need to create a new file on /etc/sudoers.d with this content:
Defaults env_keep+=GNOME_DESKTOP_SESSION_ID
But, there is a problem, if you already are inside sudo, it will not been read again.
So, the complete solution is use sudo inside your script to create this file and then execute your command in another sudo:
#!/bin/bash
# ignore sudo
if [[ -z $SUDO_USER ]]; then
#save current dir
DIR="$(pwd)"
#generate random string (file name compatible)
NEW_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
#create env_keep file
sudo -i -- <<EOF0
echo "Defaults env_keep+=GNOME_DESKTOP_SESSION_ID" > /etc/sudoers.d/"$NEW_UUID"_keep_java_laf
EOF0
sudo -u YOUR_USER -i -- <<EOF
#go to original directory
cd "$DIR"
#execute your java command
java YOUR_COMMAND
EOF
#clean file
sudo rm -f /etc/sudoers.d/"$NEW_UUID"_keep_java_laf
else
echo "sudo not allowed!";exit 1;
fi
Related
I have a file.sh with this, when run show : TERM environment variable not set.
smbmount //172.16.44.9/APPS/Interfas/HERRAM/sc5 /mnt/siscont5 -o
iocharset=utf8,username=backup,password=backup2011,r
if [ -f /mnt/siscont5/HER.TXT ]; then
echo "No puedo actualizar ahora"
umount /mnt/siscont5
else
if [ ! -f /home/emni/siscont5/S5.TXT ]; then
echo "Puedo actualizar... "
touch /home/emni/siscont5/HER.TXT
touch /mnt/siscont5/SC5.TXT
mv -f /home/emni/siscont5/CCORPOSD.DBF /mnt/siscont5
mv -f /home/emni/siscont5/CCTRASD.DBF /mnt/siscont5
rm /mnt/siscont5/SC5.TXT
rm /home/emni/siscont5/HER.TXT
echo "La actualizacion ha sido realizada..."
else
echo "No puedo actualizar ahora: Interfaz exportando..."
fi
fi
umount /mnt/siscont5
echo "/mnt/siscont5 desmontada..."
You can see if it's really not set. Run the command set | grep TERM.
If not, you can set it like that:
export TERM=xterm
Using a terminal command i.e. "clear", in a script called from cron (no terminal) will trigger this error message. In your particular script, the smbmount command expects a terminal in which case the work-arounds above are appropriate.
You've answered the question with this statement:
Cron calls this .sh every 2 minutes
Cron does not run in a terminal, so why would you expect one to be set?
The most common reason for getting this error message is because the script attempts to source the user's .profile which does not check that it's running in a terminal before doing something tty related. Workarounds include using a shebang line like:
#!/bin/bash -p
Which causes the sourcing of system-level profile scripts which (one hopes) does not attempt to do anything too silly and will have guards around code that depends on being run from a terminal.
If this is the entirety of the script, then the TERM error is coming from something other than the plain content of the script.
You can replace :
export TERM=xterm
with :
export TERM=linux
It works even in kernel with virgin system.
SOLVED: On Debian 10 by adding "EXPORT TERM=xterm" on the Script executed by CRONTAB (root) but executed as www-data.
$ crontab -e
*/15 * * * * /bin/su - www-data -s /bin/bash -c '/usr/local/bin/todos.sh'
FILE=/usr/local/bin/todos.sh
#!/bin/bash -p
export TERM=xterm && cd /var/www/dokuwiki/data/pages && clear && grep -r -h '|(TO-DO)' > /var/www/todos.txt && chmod 664 /var/www/todos.txt && chown www-data:www-data /var/www/todos.txt
If you are using the Docker PowerShell image set the environment variable for the terminal like this with the -e flag
docker run -i -e "TERM=xterm" mcr.microsoft.com/powershell
I have a script called foo.sh in my home folder.
When I navigate to this folder, and enter ./foo.sh, I get
-bash: ./foo.sh: Permission denied.
When I use sudo ./foo.sh, I get
sudo: foo.sh: command not found.
Why does this happen and how I can fix it?
Permission denied
In order to run a script the file must have an executable permission bit set.
In order to fully understand Linux file permissions you can study the documentation for the chmod command. chmod, an abbreviation of change mode, is the command that is used to change the permission settings of a file.
To read the chmod documentation for your local system , run man chmod or info chmod from the command line. Once read and understood you should be able to understand the output of running ...
ls -l foo.sh
... which will list the READ, WRITE and EXECUTE permissions for the file owner, the group owner and everyone else who is not the file owner or a member of the group to which the file belongs (that last permission group is sometimes referred to as "world" or "other")
Here's a summary of how to troubleshoot the Permission Denied error in your case.
$ ls -l foo.sh # Check file permissions of foo
-rw-r--r-- 1 rkielty users 0 2012-10-21 14:47 foo.sh
^^^
^^^ | ^^^ ^^^^^^^ ^^^^^
| | | | |
Owner| World | |
| | Name of
Group | Group
Name of
Owner
Owner has read and write access rw but the - indicates that the executable permission is missing
The chmod command fixes that. (Group and other only have read permission set on the file, they cannot write to it or execute it)
$ chmod +x foo.sh # The owner can set the executable permission on foo.sh
$ ls -l foo.sh # Now we see an x after the rw
-rwxr-xr-x 1 rkielty users 0 2012-10-21 14:47 foo.sh
^ ^ ^
foo.sh is now executable as far as Linux is concerned.
Using sudo results in Command not found
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 foo.sh is located. Hence the command is not found.
The PATH environment variable contains a list of directories which are searched for commands. Each user sets their own PATH variable according to their needs.
To see what it is set to run
env | grep ^PATH
Here's some sample output of running the above env command first as an ordinary user and then as the root user using sudo
rkielty#rkielty-laptop:~$ env | grep ^PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
rkielty#rkielty-laptop:~$ sudo env | grep ^PATH
[sudo] password for rkielty:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin
Note that, although similar, in this case the directories contained in the PATH the non-privileged user (rkielty) and the super user are not the same.
The directory where foo.sh resides is not present in the PATH variable of the root user, hence the command not found error.
The other solutions I've seen here so far are based on some system definitions, but it's in fact possible to have sudo use the current PATH (with the env command) and/or the rest of the environment (with the -E option) just by invoking it right:
sudo -E env "PATH=$PATH" <command> [arguments]
In fact, one can make an alias out of it:
alias mysudo='sudo -E env "PATH=$PATH"'
(It's also possible to name the alias itself sudo, replacing the original sudo.)
Check for secure_path on sudo
[root#host ~]# sudo -V | grep 'Value to override'
Value to override user's $PATH with: /sbin:/bin:/usr/sbin:/usr/bin
If $PATH is being overridden use visudo and edit /etc/sudoers
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
Check that you have execute permission on the script. i.e. chmod +x foo.sh
Check that the first line of that script is #!/bin/sh or some such.
For sudo you are in the wrong directory. check with sudo pwd
You can also create a soft link to your script in one of the directories (/usr/local/bin for example) in the super user PATH. It'll then be available to the sudo.
chmod +x foo.sh
sudo ln -s path-to-foo.sh /usr/local/bin/foo
Have a look at this answer to have an idea of which directory to put soft link in.
It seems that linux will say "command not found" even if you explicitly give the path to the file.
[veeam#jsandbox ~]$ sudo /tmp/uid.sh;echo $?
sudo: /tmp/uid.sh: command not found
1
[veeam#jsandbox ~]$ chmod +x /tmp/uid.sh
[veeam#jsandbox ~]$ sudo /tmp/uid.sh;echo $?
0
It's a somewhat misleading error, however it's probably technically correct. A file is not a command until its executable, and so cannot be found.
Try chmod u+x foo.sh instead of chmod +x foo.sh if you have trouble with the guides above. This worked for me when the other solutions did not.
Regarding "command not found" when using sudo far less hackier way would be to edit secure_path.
It is perfectly described here:
https://superuser.com/questions/927512/how-to-set-path-for-sudo-commands
Ok this is my solution:
in ~/.bash_aliases just add the following:
# ADDS MY PATH WHEN SET AS ROOT
if [ $(id -u) = "0" ]; then
export PATH=$PATH:/home/your_user/bin
fi
Voila!
Now you can execute your own scripts with sudo or set as ROOT without having to do an export PATH=$PATH:/home/your_user/bin everytime.
Notice that I need to be explicit when adding my PATH since HOME for superuser is /root
If you are not so comfortable with the command line and are using Ubuntu you can solve the problem as follows:
Open the folder window where the file is located
Right click on the executable file and choose Properties
Go to the Permissions tab and highlight Allow executing file as program
With this solution you allow the user to execute the file as a program and you don't need sudo (or change the PATH environment variable for root).
It seems sudo command not found
to check whether the sudo package is installed on your system, type sudo , and press Enter . If you have sudo installed the system will display a short help message, otherwise you will see something like sudo: command not found
To install sudo, run one of the following commands using root account:
apt-get install sudo # If your system based on apt package manager
yum install sudo # If your system based on yum package manager
I want to fetch the Java version in Linux in a single command then save the output to a variable.
Using the command below assigns the variable to the local version of java. I want to run java -version on the remote host, parse the version number then save the output to a variable.
ssh -q -t $server "OLDJAVA=`java -version 2>&1 | cut -s -d '"' -f2`"
You need to run the command on the remote host and set a local variable to the output, which ssh will forward:
OLDJAVA=$(ssh -q $server "java -version 2>&1 | cut -s -d '\"' -f2")
The '\"' responds to the need to send an escaped quote to the remote server. (I missed this on the first iteration.) However, it is easier to do the redirection and post-processing on the local machine, since ssh will pass both stdout and stderr back. This results in the somewhat simpler
OLDJAVA=$(ssh -q $server java -version 2>&1 | cut -s -d \" -f2)
In both cases, I removed the -t option because it doesn't seem to be needed. It's possible that the -q option will create more problems than it solves (since you will definitely want to react to an ssh error.)
I used to have multiple JDK installed on my Linux machine, and I like to switch from one version to another from the command-line ( I used to change my JAVA_HOME manually):
This is my current approach :
I source a ~/.paths in my .bashrc.
the .paths contain all the JDK installed on my machine.
JDK7="~/local/jdk1.7.0_15"
JDK8="~/local/jdk1.8.0"
// I use Jdk 7 by default
JDK_HOME=$JDK7;
// including $JDK_HOME/bin to the $PATH
When I want t switch to JDK8, I modify the JDK_HOME variable to point on JDK8 in the file, and I re-source my .paths file.
I know that IDE can manage multiple JDK easily, but I want an rvm like solution.
Is there any better trick ?
Better more, Is there any equivalent for rvm in Java ?
There is jdk_switcher although it is quite static to some ubuntu paths - it should be easy to modify it to run from other paths.
There is a plan to make RVM 2 support switching more then just Ruby versions, you can read more about it here.
anyone with the link can comment.
I don't think there is such solution.
See this question to a solution with symlinks.
Now , I'm using jdk-manager, a little bash script to manage multiple JDKS installations.
You can have as many Java versions installed as you want to. Just install to a folder of your choosing and use some convention.
If you want to make one-time use of a particular version run it with a full path (for example):
>C:\java\jdk-6u35\bin\java.exe
or
>/java/jdk-6u35/bin/java.exe
If you want to change to just use it, change your path to put the version you want at the front. The path might be similar to that shown above.
Be sure to change JAVA_HOME as well and any other environment settings that include a reference to the java location.
Note that some tools have internal configuration as to which java version to use. Eclipse is a good example. You have to set up a list of your JVMs and then select one for each project or for all projects.
I wrote my own script to manage java versions. I use some sites that require Oracle Java and usually the latest version, so I can't use apt/aptitude and therefore can't use update-alternatives or jdk-manager (which uses update-alternatives).
Here's my script. I don't develop professionally, so it's probably a mess, but it serves my purposes. It only considers java versions stored in /usr/lib/jvm.
#!/bin/bash
# the proper way....
# + update: update-alternatives only works for versions installed by apt or aptitude
# ... same for jdk-manager (uses update-alternatives to do the heavy lifting)
#echo "The proper way is:"
#echo "$ update-alternatives --config java"
#exit
# the rest is (no longer) depreciated....
echo "The current default java is $(readlink --canonicalize `which java`)"
PS3='Select Java to install: '
options=( $(find /usr/lib/jvm -iname java) )
noptions=${#options[#]}
(( loption=noptions-1 ))
options[${#options[#]}]="Quit"
select opt in "${options[#]}"
do
for i in $(seq 0 $loption); do
[ "$opt" == "${options[$i]}" ] && \
javapath=${options[$i]}
done
if [ "$javapath" ]; then
break
fi
if [ "$opt" == "Quit" ]; then
echo "Nothing installed.";
exit
else
echo "Invalid option. Try another one.";
continue
fi
done
# remove the old link (might be superfluous)
#rm -vf -- "$link"
# set new link (symbolic, force, verbose)
sudo ln -sTfv "$javapath" "/usr/bin/java"
default_java_dir=$(echo "$javapath" | grep --only-matching --regexp="\/usr\/lib\/jvm\/[^\/]*")
sudo ln -sTfv "$default_java_dir" "/usr/lib/jvm/default-java"
java_bin_dir=$(echo "$javapath" | sed 's/[^\/]*$//')
echo $java_bin_dir
[ -f "${java_bin_dir}javac" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}javac"
[ -f "${java_bin_dir}javadoc" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}javadoc"
[ -f "${java_bin_dir}javafxpackager" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}javafxpackager"
[ -f "${java_bin_dir}javah" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}javah"
[ -f "${java_bin_dir}javap" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}javap"
[ -f "${java_bin_dir}java-rmi.cgi" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}java-rmi.cgi"
[ -f "${java_bin_dir}java_vm" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}java_vm"
[ -f "${java_bin_dir}javaws" ] && sudo ln -sfv -t "/usr/bin" "${java_bin_dir}javaws"
find_dir=$(dirname "$java_bin_dir")
pluginpath=$(find "$find_dir" -name libnpjp2.so)
#exit
echo -n "Install $pluginpath as the Java plugin? [y/N]: "
read
response=$(echo $REPLY | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/')
if [ "${response:0:1}" == "Y" ]; then
# directories for plugins
plugin_dirs="/usr/lib/firefox/plugins
/usr/lib/firefox-addons/plugins
/usr/lib/mozilla/plugins
/opt/google/chrome
/home/james/.mozilla/plugins"
# first, clean out anything we might have improperly installed already
echo "Using 'sudo' to remove any installed java plugins..."
for pdir in $plugin_dirs; do
sudo rm --verbose --force "$pdir/libjavaplugin_oji.so" "$pdir/libnpjp2.so" "$pdir/IcedTeaPlugin.so"
done
# okay, trying brute force and awkwardness....
echo "Using 'sudo' to install \"$pluginpath\" in several places..."
for pdir in $plugin_dirs; do
sudo ln --symbolic --verbose --force "$pluginpath" "$pdir"
done
fi
exit
If you use Debian or a derived GNU/Linux distribution, you can use update alternatives to set what is currently being run when you type java.
Try typing
update-alternatives --display java
That will show you what alternatives you have available.
This command is non-destructive, that is, it doesn't change anything.
man update-alternatives
Will give you the manual for the tool.
The command you will likely want though is:
update-alternatives --config java
Which will give you a simple, interactive way of setting the java program. You can also use --set if you want to script it.
Of course, you should not trust me without running man first, because people sometimes go on the Internet and tell lies. ;)
EDIT: I forgot, this link introduces update-alternatives in a good (if Vi specific) way.
For Linux, you can use update-alternatives to not only set paths to java, javac, and other binaries but also your JAVA_HOME. Since all it does is manage links, you can install a link to your jdk directories and then set JAVA_HOME to point to that link. For example, "update-alternatives --install /usr/lib/jdk jdk /path/to/jdk8 1" will install a link to your jdk directory. You then add "export JAVA_HOME to /usr/lib/jdk" in .bashrc, .profile, or whatever file you use to set environment variables and any alternative you install under the name jdk will be pointed to by JAVA_HOME when you switch with update-alternatives --config jdk. If you are using alternatives already for the java executables, you can use --slave to make jdk whenever java does.
I am developing an application where i required to run some of the scripts of unix from Java Code.
Platform i am using is Unix, Tomcat 5.5..
For that, My Sample Code is as follows :
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec("netstat -i|tail -n +3|cut -d ' ' -f1");
System.out.println("exitValue = "+proc.exitValue());
I have given all the rights to tomcat user.
Now, my program output cases are :
Script exitValue()
======= ============
netstat -i 0
netstat -i|tail -n +3 4
sudo netstat -i 1
sudo netstat -i|tail -n +3 1
Above table suggest that only 1st script is executing in unix, all others are failing.
I am not sure, but i am just assuming that i have to run Tomcat Server as a root user..
Can anybody have any other solution, then please reply..
Thanks in advance...
If I remember correctly, pipes ("|") are handled by the shell. Java will probably not handle them at all ...
There are a few workarounds :
run bash with your commands as a parameter :
runtime.exec("bash -c \"netstat -i|tail -n +3|cut -d ' ' -f1\"");
write a bash script that run all those commands and run this script from Java :
#!/bin/bash
netstat -i|tail -n +3|cut -d ' ' -f1
create the pipes in Java : read the output of netstat -i and connect it in Java to tail -n +3 ...
Using | to chain commands in Unix is part of the shell, and Runtime.exec() runs the command directly, not though the shell. A quick fix may be (untested as I don't have a Unix box available at this moment) to prefix the shell as the first command.
Process proc = runtime.exec("/bin/sh netstat -i|tail -n +3|cut -d ' ' -f1");
Got the solution of above problem..
I have just created simple shell script file, and put the script inside that .sh file.
Now at the java side, i am just calling simple shell script file..
Process proc = runtime.exec("sh /usr/tmp/try1.sh");
That's it!!!