How to use error codes inside bash script - macos

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)

Related

Enter sudo password while in expect script

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

Why does my expect script exit prematurely?

Here is my except script:
#!/usr/bin/expect
spawn openvpn --config peter.ovpn
expect -exact "Enter Private Key Password: "
send -- "mypassword\r"
I run it and see OpenVPN ask for my client password. But the script exits, apparently without ever sending the password. When I try with an incorrect password it is the same (no incorrect password message). It is also exactly the same result if I delete the send -- "mypassword\r" line from the end of the expect script.
It's my first expect script so probably my syntax is wrong. Or could it be that OpenVPN is kicking me off for using an expect script to connect?
Your syntax is fine. The problem is the script has no more commands to run after you send the password, so the expect script exits and that kills openvpn.
What do you need to do after you send the password?
If you just need to keep openvpn running, then do this:
#!/usr/bin/expect
spawn openvpn --config peter.ovpn
expect -exact "Enter Private Key Password: "
send -- "mypassword\r"
set timeout -1
expect eof
-1 means "infinite", and expect eof means you are waiting for the spawned process to exit before the expect script can exit.

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.

Script to get info from multiple servers

I want to collect info from a number of servers whether their grub.conf contains the "elevator" parameter or not.
Now, password-less key authentication is something I can not do as of now. I am okay with specifying password in script.
Can someone please help me achieving this?
This is what I did:
#!/bin/bash
GRUB="/etc/grub.conf"
_pair="$(</home/wadhwaso/login.txt)"
Server_info="/tmp/server_info"
for e in $_pair
do
# extract user and ips for each $e in $_pair
IFS='#'
set -- $e
_user="$1"
_ip="$2"
sleep 2
echo "Connecting to server '${_ip}' via SSH..."
ssh ${_user}#${_ip} "sudo grep -q "elevator=noop" "$GRUB" >/dev/null"
if [ $? -eq 0 ]; then
echo "Present on ${_ip}" | tee -a "${Server_info}"
else
echo "not present on ${_ip}" | tee -a "${Server_info}"
fi
done
I don't want to give password every time, and as password-less authentication is not present in my environment and will not be possible, I have to pass the password in script itself which really doesn't bother me, I can do that. I know it could be done through expect but I messed everything every time I tried using it.
I tried using expect the way it was told in 1st answer but I failed.
The "expect" tool sounds perfect for what you need: http://expect.sourceforge.net/
To install expect (on ubuntu, for example), do:
sudo apt-get install expect
Here's a code snippet showing you how to use expect as part of a bash script:
#!/bin/bash
IP="111.111.11.1"
login="root"
password="some_password"
dest_dir="/etc/"
expect_sh=$(expect -c "
spawn ssh $login#$IP
expect \"password:\"
send \"$password\r\"
expect \"#\"
send \"cd $dest_dir\r\"
expect \"#\"
send \"chmod +x $server_side_script"
expect \"#\"
send \"./$server_side_script\r\"
expect \"#\"
send \"cd /lib\r\"
send \"cat $file_count\r\"
expect \"#\"
send \"exit\r\"
")
echo "$expect_sh"
Alternatively, you can put the logic in a separate expect script for neater syntax and source it from your bash script:
#!/usr/bin/expect
set login "root"
set addr "111.111.1.1"
set pw "root"
spawn ssh $login#$addr
expect "$login#$addr\'s password:"
send "$pw\r"
expect "#"
send "cd /etc\r"
# Then other things you need to do
As for the "3rd thing" with RSA key fingerprint: SSH will warn you if connecting to a host you haven't seen before (because of possibility of man-in-the-middle attacks), theoretically you should verify that the fingerprint matches what you anticipate it to be to verify that the host is who the host claims to be.
Hope this helps =)

Automating pear install with expect

I'm working on a small script to try and automate the installation of pear on a system should it be missing - basically, taking the interactive portions of the installation process and making them non-interactive so all I need to do is execute the script and it does it for me on its own. This is my first time trying to use expect and it's, well, not what I expected.
Sample code below:
/usr/bin/expect <<EOD
set timeout 20
set number [lindex $argv 0]
set path [lindex $argv 1]
spawn php -q go-pear.phar
expect "1-11, 'all' or Enter to continue:"
send "$number\r"
interact
expect "Installation base ($prefix) [/usr/local] :"
send "$path\r"
interact
EOD
This is not quite working at the moment as it's trying to fill in the /usr/local portion in the first menu as follows:
1-11, 'all' or Enter to continue: invalid command name "/usr/local"
while executing
"/usr/local"
invoked from within
"expect "Installation base () [/usr/local] :""
Can anyone show me what I'm missing here? Also, I'd like to get this so I don't have to run it as ./script $var0 $var1 but rather just run it as ./script with everything being contained within and filled in, but not sure how to do that.
Thanks in advance for any help you can offer.
One problem is that the [...] inside the string tells expect to execute a command. I think you need to escape them with \[...\].
Hope this helps a little =)
I ended up figuring this out. In case anyone else wants to do something similar, here's what worked for me:
/usr/bin/expect <<EOF
set send_slow {1 .1}
set timeout -1
spawn php -q go-pear.phar
expect "1-11, 'all' or Enter to continue:"
sleep .1
send -s -- "1\r"
expect -exact "1\r"
sleep .1
send -s -- "/usr/local\r"
expect -exact "/usr/local\r"
expect "1-11, 'all' or Enter to continue:"
sleep .1
send -s -- "\r"
expect -exact "\r"
expect EOF

Resources