I have an Ubuntu machine with a native Hadoop client installed on it, meaning i can run for example:hadoop fs -ls as long as I'm on a certain user, let's say userA.Now i have a scala application running on this machine under root (cannot change this) and i need to be able to switch to userA and then run hadoop fs -lsI'm working with scala.process in order to run my commands but I'm unable to run them in way they'll be connected to each other, so if run:Process(Seq("bash","-c","su userA && hadoop fs -ls")).! i will get an error since the 2nd command is actually executed on root, i know this because when i do: Process(Seq("bash","-c","su userA && whoami")).! I get root and not userAAny idea how to tie them together (exmple in linux command will suffice, i'll be able to replicate it in scala.process i believe)
Your question has nothing to do with Scala, Ubuntu or Hadoop per se. It is all about shell scripting and understanding the way shell works.
First of all, you don't need to invoke bash, in your case it is just another (unnecessary) layer of indirection. Removing bash with leave you with this:
Process("su userA && hadoop fs -ls").!
... which will fail. Why? Because this code is equivalent to this:
Process(Seq("su", "userA", "&&", "hadoop", "fs", "-ls")).!
As you can see all the pieces of your command including && are passed to su as command line arguments. Naturally su doesn't know what to do with it and thus would fail.
Now let's forget about Scala code for a while and drop to bash shell and try the command there. Let's use whoami to test whether the user has changed to userA:
su userA && whoami
The fist thing that you would notice is that su when invoked this way provides interactive shell prompt (asking for password first, unless you run it from root). This is probably not what you want when running su from a program. Moreover, after you log out of that shell you will see that the user was not changed to userA.
Here's why. The notion of effective user is tied to a process. Each process has exactly one effective user at a given point of time. The initial effective user of a process is inherited from the spawning process.
So here's what's happening when you run su userA && whoami from the bash prompt:
bash spawns su as a child process, su inherits the effective user from its parent:
bash (root)
+- su (root)
su changes its effective user via setuid:
bash (root)
+- su (userA)
by default su spawns the default shell of a specified user as given in /etc/passwd as a child process (e.g. sh); sh inherits the effective user from its parent:
bash (root)
+- su (userA)
+- sh (userA)
Now let's get to the the && part. It turns out to be part of the shell syntax. command1 && command2 asks shell to run command1 first and then only after command1 exists without any errors (i.e. its exit code is zero) run command2. It is crucial to understand two things:
command2 is run only after command1 has finished successfully;
both command1 and command2 are spawned by the same shell process, and thus they both inherit its effective user.
So here's what we would get for su userA && whoami invocation:
bash (root)
+- su (userA, already exited by the time whoami started)
| +- sh (userA, already exited by the time whoami started)
+- whoami (root)
So no matter what user would you change to via su process whoami wouldn't be affected.
In order to fix the issue you should make whoami to be spawned by su so that it could inherit the changed user.
To make this happen invoke su like this:
su -c whoami
This would yield the following process tree:
bash (root)
+- su (userA)
+- whoami (userA)
Now let's get back to Scala. Unfortunately if you run the command above from Scala you would get su: must be run from a terminal. To be honest I don't know how to cope with this problem properly, so I would suggest to switch to sudo instead.
So the final solution would be:
Process(Seq("sudo", "-u", "userA", "hadoop fs -ls")).!
Related
I am running some shell commands with os.system that needs to be run as root.
I tried-
os.system("sudo su")
os.system("other commands")
and also-
home_dir = os.system("sudo su")
os.system("other commands")
But both the above scripts just become root and then stop executing, so the rest of my commands aren't executed.
I'm running Python 3.6.9 on an Ubuntu 18.04 VM.
The root privileges gained by sudo only apply to the command that is run through sudo, and do not raise the privileges of the caller (in this case, your python script). So your first command os.system("sudo su") would run an interactive root shell, but after you have exited from that and then your python code does the subsequent call to os.system("other commands"), these will run under its ordinary user privileges.
You could run each command one at a time via sudo:
os.system("sudo some_command")
os.system("sudo some_other_command")
Note that each command will be separately logged by sudo in the system log, and that even if there are several commands, sudo shouldn't ask for a password more than once within a short time interval.
Or if you need to do a sequence of steps like changing directories that might not be possible in the caller (for example, if the directory is not accessible by the non-root user that is running the python script), then you could do for example:
os.system("sudo sh -c 'cd some_dir && some_other_command'")
(Just for info, && is similar to ; but the other command is only run if the cd succeeded, so it is safer, although this point relates to shell syntax rather than python.)
If there are a lot of commands, of course you also have the option of just making a separate "helper" shell-script and running the entire script through sudo.
os.system("sudo sh /path/to/myscript.sh")
Finally to note, if you are running your python script in a non-interactive environment, you may need to tell sudo not to prompt for a password, at least for the relevant invoking user and target commands. For details, do man sudoers and look for examples involving NOPASSWD.
I need to remotely start bash scripts that perform sudo tasks, such as chmod and ntpdate and echoing to gpio.
A cron job might be the best solution for some of this, but cron is giving me headaches. I'd like to pass on this venue if I can...
I've confirmed that my scripts work locally (I can ssh into the machine and run them without a hiccup.)
However, If I try to run them remotely like so: (this is within a C++ system call)
ssh user#pc 'bash -s' < /home/user/myScript.sh
Commands with sudo fail.
sudo chmod fails with: no tty present and no askpass program specified
echo to gpio fails with: write error: Device or resource busy
sudo ntpdate fails with: no tty present and no askpass program specified
Can anyone help explain, or help me determine whats happening here?
I'm open to band-aids and different approaches, thanks!
You already found the problem yourself:
sudo chmod fails with: no tty present and no askpass program specified
If you run you shell script via ssh and the script wants to run the command sudo, sudo itself will ask for the users password. But the ssh session is not a tty! How should sudo now prompt for a password and how to get your password?
You can do it if you provide the password in the script ( what makes it very dangerous if someone else can read that script! )
script.sh:
echo "your passwd" | sudo -S
As alternative solution you can run the ssh session with a more privileged user.
ssh privileged_user#pc 'bash -s' < /home/user/myScript.sh
All that comes with some danger. Running all commands from the cript with a more privileged user can also be dangerous!
Why the output file from this is owned by root and not w3svcsadm?
sudo -u w3svcsadm echo "TEST ran" > /home/your/emaildigest/TEST_$( date +%Y%m%d%H%M%S ).output
I'm running into some issues with cron, and I believe this is the key to my problems.
Using the -u flag with sudo executes the command 'echo "TEST ran"' as the user w3svcasadm, but that command isn't the thing doing the work of outputting to a file, which is done by the '>' operator. By the time bash is using that operator, it's already switched back to the user running the shell. If that user is root, then the file will be created under root. In your script, you could use "su w3svcsadm" to switch the shell user before executing that command, then you wouldn't have to use that -u flag at all.
Ok I'm about to pull my hair out. I have a perl script that just will not run in the crontab however I have a previously written perl script that runs just fine every day on the same box. I have checked all of the given solutions on this site and others around the web and nothing seems to make a difference. Here is my cron and the first part of my script
55 13 * * * su oracle; cd /u02/oraclebackup;./move_em_bkup.pl >> /u02/oraclebackup/move_em_backup.log > move_em_bkup.dbg 2>$1
It touches the .dbg file but does not put anything in there. There are no errors or anything that I can use to go by.
#!/usr/bin/perl
use Strict;
use Archive::Tar;
use Net::SCP qw/ scp /;
use Net::SCP::Expect;
use DateTime;
Can anybody help?
The command you're running is:
su oracle; cd /u02/oraclebackup; ...
su oracle normally launches an interactive shell under the oracle account (assuming you have permission to do so). I'm not sure what that would do in a non-interactive cron environment, but even assuming it works, the cd /u02/oraclebackup and following sub-commands will be executed after that shell terminates, i.e., under the account that owns the crontab. The su oracle will either block the rest of the command or do nothing.
You can use su -c command to run a command as a specified user. In you case, you'd want something like:
su -c oracle sh -c 'cd /u02/oraclebackup; ...'
Or change su to su - if you need the oracle account's login environment.
Better yet, drop the su and put the whole thing in the oracle account's crontab. You might still need to play some more tricks to get the environment right; cron jobs run with a limited set of environment variables by default.
I've got a shell script which I am trying to run as a specific user. My command looks like this:
su - jetty sh ./runProgram.sh
When I attempt to run this command through the console I get an error saying:
/bin/sh: /bin/sh: cannot execute binary file
I also tried:
su - jetty sh runProgram.sh
And I still get the same error..
It DOES work if I do this:
sh runProgram.sh
But this shell script is meant to be run by a specific user. Any advice on how to get this working??
Try
su - jetty -c sh runProgram.sh
According to su's documentation (info coreutils 'su invocation'), it will by default execute a shell, and the arguments to su are passed as arguments to the shell.
The implication is simply this: su is doing in essence:
/bin/sh *arguments_to_su*
but it does it as another user (the "effective user id")... that's all... So
su - jetty sh ./runprogram.sh
is akin to
(become the user jetty via login or su)
/bin/sh sh ./runprogram.sh
...and the shell will report an error, because the first /bin/sh, called by su, is trying to run the program sh as a shell script, with ./runprogram.sh as its argument. But sh itself is not a shell script, it is a binary (whose job it is is to run shell scripts).
if you were to simply do this:
su - jetty ./runprogram.sh
Then the su command will call /bin/sh with the program ./runprogram.sh as its argument, and jetty as the effective user id, and all should be well. ...SHOULD be well, because since you are doing an su - you are making the shell a login shell and changing to the user's home directory. If runprogram.sh is not in the home directory, you will get an error.
This is why, also, you cannot run for example run a cp command by simply:
su - jetty cp file1 file2
...because, again, after su changes effective user id to jetty it will try this:
/bin/sh cp file1 file2
...and cp is not a shell script. But the -c option works in this case; because you are telling su that you want to run /bin/sh with the -c option to the shell:
su - jetty -c "cp file1 file2"
does the job. Note that you must quote the command, because the entire string is passed to the shell and (I believe) any following arguments are ignored.
Finally, the previous poster's answer doesn't work for me on Linux, it requires the entire command string to be quoted.