Enter sudo password while in expect script - bash

I have an expect code inside bash script, something like this
env input1=${INPUT1} input2=${INPUT2} expect << "EOS"
set timeout -1
spawn ./another_script.sh
expect {
"Input 1" { send -- "$env(input1)\r";exp_continue }
"Input 2" { send -- "$env(input2)\r";exp_continue }
eof
}
EOS
During the execution of another_script.sh I am prompted to enter sudo password, but I'm not able since I'm stil in expect.
What can I do in order for me to be able to enter the sudo password and for the script to continue running after that?
I don't want to save the password in the script and then pass it to expect, but I want to be able to type it in.

The answer from Donal Fellows is the right way to go, but for completeness here is the interact solution you were trying for. The command to use is
expect {
"Input 1" { send -- "$env(input1)\r"; exp_continue }
"Input 2" { send -- "$env(input2)\r"; exp_continue }
"password for" { stty -echo
interact -u tty_spawn_id -o "\r" return
stty echo
exp_continue }
eof
}
The problem you have is that you are running expect with the script on stdin, so interact has trouble unless you use -u tty_spawn_id to get it to work with /dev/tty and the user. You need to set the echo on/off on this tty explicitly, as sudo will only have done it on the pty between the spawned command and expect.

The standard way of handling an event like this is to watch for a prompt for the sudo password, supply it, and then continue. But with a long-running script, you'll want to cache the password within the expect script so that you don't have to go back to the user for it several times over a few hours. Indeed, for usability you probably need to ask for the password up front rather than waiting until the underlying system needs it.
Fortunately, expect includes support for stty so it's an easy thing to add.
env input1=${INPUT1} input2=${INPUT2} expect << "EOS"
# Prompt for password, cribbed/converted from example on expect(1) manpage
stty -echo
send_tty "sudo password: "
expect_tty -re "(.*)\n"
send_tty "\n"
set password $expect_out(1,string)
stty echo
# Rest of the script, with clause for sending the password at the right time
set timeout -1
spawn ./another_script.sh
expect {
"Input 1" { send -- "$env(input1)\r"; exp_continue }
"Input 2" { send -- "$env(input2)\r"; exp_continue }
"assword: " { send -- "$password\r"; exp_continue }
eof
}
EOS

From man sudo: The sudoers policy caches credentials for 15 minutes, unless overridden in sudoers(5).
With that, call sudo -v before calling expect. It checks for the credentials, and if they aren't cached, it will ask for the root password. That way you can enter them before expect and later sudo commands don't need to ask again.

for what you comment I think you have it in cron. In that case you would neeed to run the script as sudo. you can just call it in the sudo crontab. you can access it by running sudo crontab -e

Related

How to pass username/password into ambari-server sync-ldap command

I am trying to run a playbook with sync ldap command :
ambari-server sync-ldap --all
The thing is, after the executing of the command, it asks for username and then password.
Is there anyway to pass the username and password automatically from echo or using a script shell, without having to pass it manually ?
Check out expect.
An simple example is to use the following script.
#!/usr/bin/expect -f
ambari-server sync-ldap --all
# wait until the prompt shows "enter username". Change this to adapt to your application.
expect "enter username"
send "your_username\r"
# wait until the prompt shows "password: ". Change this to adapt to your application.
expect "password: "
send "yourPAssWoRD\r"
interact
Disclaimer: I have never used ambari-server command before.
try with below script
# cat /tmp/ambari-server-sync-ldap-unattended.sh
#!/usr/bin/expect
set timeout 20
spawn /usr/sbin/ambari-server sync-ldap --groups=/etc/ambari-server/ambari-groups.csv
expect "Enter Ambari Admin login:" { send "admin\n" }
expect "Enter Ambari Admin password:" { send "notTheRealPasswordOfCourse\n" }
interact

Why does this expect code not work?

Not sure if this is more of a scripting/unix question or a programming one, but I tried on the unix stackexchange and got no responses, so:
The following expect code seems to work; that is, it appears to enter text in answer to the password prompt. However, the device never actually mounts.
But if I simply enter the command into a shell and type the password in by hand the device mounts successfully.
So I'm curious where the input is actually ending up, as it never seems to 'catch' the password yet doesn't present an error message either? In fact the output looks exactly the same in both instances, but only in the case of running the command and typing the password manually do I see my files appear over the network.
Here is the code:
#!/usr/bin/expect
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=Me
expect "Password: " {
set send_slow {1 .1}
send -s "a_password"
}
UPDATE: Got the help I needed to make it work: had to insert 'expect eof' after sending the password so that it doesn't end prematurely. However I now wish to progress to changing it to 'expect_background' so that I can have the same trigger response to issuing multiple mount commands. The following ends prematurely, and 'expect eof' at the end causes an error:
expect_background "Password:" {
send "a_password\r"
expect eof
}
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=Me
expect eof
What should it look like?
UPDATE: the following code block illustrates the current problem:
expect_background "Password: " {
send "a_password\r"
expect eof
}
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=someone
expect "Password: " {
send "a_password\r"
expect eof
}
#The password prompt gets answered by 'expect' but not 'expect_background'.
#If I delete the last 'expect' and insert 'expect eof' it hangs for a short
#while at the password prompt (around 3 seconds) then exits.
#Why?
Add one more expect statement after sending the password.
send -s "a_password\r"
expect eof
The eof will make the Expect to wait till the end of program.

Handle multiple statements in an Expect script

I am new to Expect scripting.
I wrote an Expect script for ssh in a Linux machine, where I am facing a problem in ssh'ing to different Linux machines. Below I have copied the script.
!/usr/local/bin/expect
set LinuxMachine [lindex $argv 0]
spawn ssh root#$LinuxMachine
expect "root#$LinuxMachine's password:"
send "root123\n"
expect "[root#Client_FC12_172_85 ~]#"
send "ls"
interact
When I supply 10.213.172.85 from command line the expect in the 4th line, it reads as "root#10.213.172.85's password:" and logs in successfully
But some Linux will expect
The authenticity of host '10.213.172.108 (10.213.172.108)' can't be established.
RSA key fingerprint is da:d0:a0:e1:d8:7a:23:8b:c7:d8:40:8c:b2:b2:9b:95.
Are you sure you want to continue connecting (yes/no)
In this case the script will not work.
How can I have two Expect statements in one Expect command?
You can use exp_continue in such a case:
expect {
"Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
exp_continue
}
"root#$LinuxMachine's password:" {
send "root123\r"
expect "[root#Client_FC12_172_85 ~]#"
send "ls\r"
interact
}
}
In the above, the Expect block waits for either the yes/no question OR the prompt for password. If the latter, it moves on with providing password, expecting prompt, sending ls command and giving control back.
If the former, it will answer 'yes' and repeat the expect block, ready to find the prompt for a password (or even the yes/no question again, for that matter - not that you will need that).
I would also include some timeouts with meaningful messages in case some expect option does not match as expected, but the above should work.
As a side comment, you don't want to set a root password in a script... I recommend using ssh key authentication.
We like to call it "long log in". There are ssh options that don't check the host keys:
send -- "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no username#host\n"
expect {
"Password" {
send -- "$passwd\n"
}
Part of the Bash script that calls on the expect sets the password:
echo -n "Enter LDAP password: "
read -s passwd
echo

How to use error codes inside bash script

Apologies if this has been asked before, I was unable to find an answer...
I have created a bash script for OS X to mount an AFP share. The script works successfully and allows a user to type their username and password into a graphical popup using cocoa dialog
My issue is that if the username and password are entered incorrectly, I want to be able to display a new pop up window explaining this.
I was trying to do this based on the exit status of the script but the script returns 0 regardless of whether or not the mount was successful
The script is as follows;
cd=/etc/cocoaDialog.app/Contents/MacOS/cocoaDialog
rvUsername=($($cd standard-inputbox --title "Network Mount" --informative-text "Please enter your username"))
username=${rvUsername[1]}
rvPassword=($($cd secure-standard-inputbox --title "Network Mount" --informative-text "Please enter your password"))
password=${rvPassword[1]}
mkdir "/Volumes/Test"
expect <<- DONE
set timeout -1
spawn /sbin/mount_afp -i "afp://servername/Software" /Volumes/Test
expect "*?ser:*"
send "$username\n"
expect "*?assword:*"
send "$password\r"
expect EOF
DONE
If I run this via Terminal or Textmate, I receive the following (as an example)
spawn /sbin/mount_afp -i afp://server/Software /Volumes/Test
User: username
Password:
mount_afp: the volume is already mounted
Program exited with code #0 after 7.32 seconds.
So mount_afp is giving me a message, which I would then like to use in my script...but the exit code is 0 and I don't know how else to get hold of that message to use
Any ideas? (Hope that makes sense!)
To get the exit code of the spawned command, use something like this:
# wait for the command to end : wait for the prompt $ followed by space
expect "\\$ "
send "echo \$?\r"
expect -re "(\\d+)" {
set result $expect_out(1,string)
}
send_user "command exit with result code : $result"
This will take the content of the variable $? (which is the exit code of the prevously ended command) and save it to $result.
Thanks for the responses all, helped point me in the right direction
I ended up taking this approach and setting the expect command as a variable
output=$(su -l $loggedInUser -c expect <<- DONE
set timeout -1
spawn /sbin/mount_afp -i "afp://$serverName" /Volumes/mount
expect "*?ser:*"
send "$username\n"
expect "*?assword:*"
send "$password\r"
expect EOF
DONE)

Automate in Shell Script Terminal

Oh my god! I have tried everything about expect. What I want to do is create a script to automate input password when changing to root in terminal. My code look like this
expect -i
spawn sudo pmset schedule sleep "{query}" expect { Password: send "password\r" }; interact;
exit
result
expect1.1> spawn sudo pmset schedule sleep "06/19/11 23:40:00" expect { Password: echo "lovegun\r" }; interact; exit
spawn sudo pmset schedule sleep 06/19/11 23:40:00 expect Password: send "password\r"
Password:
I am stuck in password:
Can somebody look at my code? What is wrong with this.
expect -c '
proc abort {} {
puts "You have Authenticated\n"
exit 1
}
spawn sudo pmset schedule sleep "{query}"
expect {
Password: { send "password\n" }
default abort
}
puts "Finished OK\n"
'
Finally I found the answer
If you're going to have your password buried in an expect script, you could either 1) change pmset to run set-user-id so you don't have to use sudo, or 2) give yourself NOPASSWD access to sudo so you aren't required to enter a password. Either way would keep you from having to store your password in a file.

Resources