Thursday, 14 March 2019

Check whether to patch Oracle OPatch

I was trying to figure out how to script if OPatch needed updating to the minimum required when patching Oracle software.

So I came up with the following.

Set my ORACLE_HOME to the software directory and add OPatch and bin to the PATH

export ORACLE_HOME=/u01/app/oracle/prod/mwwc12213
export PATH=$ORACLE_HOME/OPatch:$ORACLE_HOME/bin:$PATH

This is the minimum OPatch version required

export MIN_OP=13.9.4.0.0

This gets the installed OPatch version

opv=$(opatch version | head -1 | cut -f2 -d":" | sed 's/^ *//g') # Returns 13.9.3.0.0

And then converts the version number to an array of numbers, splitting on the period

op_ver=(${opv//./ }) # Returns an array - 13 9 3 0 0
op_min=(${MIN_OP//./ }) # Returns an array - 13 9 4 0 0

I then convert each member of the array to a zero padded number and combine to make a 10 digit number

min_total=$(printf "%02d%02d%02d%02d%02d" ${op_min[0]} ${op_min[1]} ${op_min[2]} ${op_min[3]} ${op_min[4]}) # Returns 1309040000
ver_total=$(printf "%02d%02d%02d%02d%02d" ${op_ver[0]} ${op_ver[1]} ${op_ver[2]} ${op_ver[3]} ${op_ver[4]}) # Returns 1309030000

Display a message, either success or failure

if (( $min_total > $ver_total )); then
  echo -e "\nOPatch Version\n~~~~~~~~~~~~~~\nMinimum   : $MIN_OP\nInstalled : $opv\nStatus    : \e[31mFailed - Patch OPatch and retest before continuing.\e[0m\n"
else
  echo -e "\nOPatch Version\n~~~~~~~~~~~~~~\nMinimum   : $MIN_OP\nInstalled : $opv\nStatus    : \e[32mSuccess - Continue patching.\e[0m\n"
fi

Success message example
OPatch Version
~~~~~~~~~~~~~~
Minimum   : 13.9.4.0.0
Installed : 13.9.4.0.0
Status    : Success - Continue patching.
Failure message example
OPatch Version
~~~~~~~~~~~~~~
Minimum   : 13.9.5.0.1
Installed : 13.9.4.0.0
Status    : Failed - Patch OPatch and retest before continuing.
This doesn't stop people ignoring this and continuing without patching but it's an easy way to calculate whether to patch OPatch or not.

Wednesday, 6 March 2019

SSH and sudo

Following on from my blog Fun with SSH here's a way to run sudo and pass the password on the command line in a script.

First you need to get the password from the user or set the password in a variable.

read -s -p "Enter remote user password: " usrpwd

This prompts the user to enter the password, without echoing to the screen and stores it in $usrpwd.

Now we can ssh to the remote server and run a command via sudo.
Here I'm just running date but you can do anything.

ssh raspberrypi sudo -S <<< $usrpwd date
james@raspberrypi's password:
[sudo] password for james: Wed  6 Mar 15:44:44 GMT 2019
If you incorporate this with my previous blog on SSH you can automate the logging into a remote server and running scripts.

As with all of this stuff the easier you make it for yourself the easier it is for somebody else to destroy your servers.

Fun with SSH

SSH to a remote host


This is the simplest way and will prompt for the remote users password every time.

ssh raspberrypi

If you need to login to the remote server under a different username then use:

ssh user@remote-host

If this is the first time you have used ssh to connect to this server you will see the below text.
The authenticity of host 'raspberrypi (xxx.xxx.xxx.xxx)' can't be established.
ECDSA key fingerprint is SHA256:??????????.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'raspberrypi,xxx.xxx.xxx.xxx' (ECDSA) to the list of known hosts.
Enter the remote users password and then you are in.

james@raspberrypi's password:
Linux raspberrypi 4.19.25-v7+ #1205 SMP Mon Feb 25 18:19:20 GMT 2019 armv7l
Last login: Wed Mar  6 10:55:48 2019 from yyy.yyy.yyy.yyy
Logout of the remote connection.

james@raspberrypi:~ $ logout
Connection to raspberrypi closed.

Automatic login to remote host (no password required)


You can set things up to allow you to ssh to a remote host without entering a password.
This is quick, but if you use a common account anybody will be able to login to the remote host without a password.

First you need to generate a key.

james@LAPTOP:~$ ssh-keygen

Accept the default file location and when prompted press enter rather than entering a password.
Generating public/private rsa key pair.
Enter file in which to save the key (/home/james/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/james/.ssh/id_rsa.
Your public key has been saved in /home/james/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:??????????. james@LAPTOP
The key's randomart image is:
+---[RSA 2048]----+
|                 |
+----[SHA256]-----+
Now we need to copy the key to the remote host.

james@LAPTOP:~$ ssh-copy-id raspberrypi
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/james/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
 james@raspberrypi's password:
Number of key(s) added: 1
Now try logging into the machine, with:   "ssh 'raspberrypi'" and check to make sure that only the key(s) you wanted were added.

james@LAPTOP:~$ ssh raspberrypi
Linux raspberrypi 4.19.25-v7+ #1205 SMP Mon Feb 25 18:19:20 GMT 2019 armv7l
Last login: Wed Mar  6 11:00:25 2019 from yyy.yyy.yyy.yyy
james@raspberrypi:~ $ logout
Connection to raspberrypi closed.

Automatic login to remote host (Key password required)


This time we are going to enter a passphrase when we run ssh-keygen to make connecting to the remote host a little more secure.

james@LAPTOP:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/james/.ssh/id_rsa):
/home/james/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/james/.ssh/id_rsa.
Your public key has been saved in /home/james/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:??????????. james@LAPTOP
The key's randomart image is:
+---[RSA 2048]----+
|                 |
+----[SHA256]-----+
james@LAPTOP:~$ ssh-copy-id raspberrypi
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/james/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
 james@raspberrypi's password:
Number of key(s) added: 1
Now try logging into the machine, with:   "ssh 'raspberrypi'" and check to make sure that only the key(s) you wanted were added.
This time connecting to the remote host prompts for the passphrase not the remote password.

james@LAPTOP:~$ ssh raspberrypi
Enter passphrase for key '/home/james/.ssh/id_rsa':
Linux raspberrypi 4.19.25-v7+ #1205 SMP Mon Feb 25 18:19:20 GMT 2019 armv7l
Last login: Wed Mar  6 11:05:14 2019 from yyy.yyy.yyy.yyy
james@raspberrypi:~ $ logout
Connection to raspberrypi closed.

Automatic login to remote host multiple times (Key password required once)


This is slightly different and will prompt for the passphrase once and allow multiple ssh login without prompting again.
It assume that you have already run ssh-keygen and entered a passphrase as in the above example.

Start running the ssh agent for your session.

james@LAPTOP:~$ eval $(ssh-agent -s)
Agent pid 382
Add you key to the agent.

james@LAPTOP:~$ ssh-add
Enter passphrase for /home/james/.ssh/id_rsa:
Identity added: /home/james/.ssh/id_rsa (/home/james/.ssh/id_rsa)
When you ssh to the remote host it will not prompt you for your passphrase.

james@LAPTOP:~$ ssh raspberrypi
Linux raspberrypi 4.19.25-v7+ #1205 SMP Mon Feb 25 18:19:20 GMT 2019 armv7l
Last login: Wed Mar  6 11:09:13 2019 from yyy.yyy.yyy.yyy
 james@raspberrypi:~ $ logout
Connection to raspberrypi closed.
Again, logging in for a second or subsequent times will also not require a password in this session.

james@LAPTOP:~$ ssh raspberrypi
Linux raspberrypi 4.19.25-v7+ #1205 SMP Mon Feb 25 18:19:20 GMT 2019 armv7l
Last login: Wed Mar  6 11:13:49 2019 from yyy.yyy.yyy.yyy
 james@raspberrypi:~ $ logout
Connection to raspberrypi closed.
Kill off the ssh agent.

james@LAPTOP:~$ eval $(ssh-agent -k)
Agent pid 382 killed
If you were now to attempt to ssh to the remote host again it will prompt for the passphrase.

Monday, 4 March 2019

Fix Linux time drift

Had a situation where two VM hosts had different times and the database/OIAM server (server1) was on one host and my Portal/UCM/etc. server (server2) was on the other host and wouldn't start because of the time difference.

Just ran the following on server 2 to get the time back in sync.

sudo date +%T --set $(ssh server1 date +%T)

This gets the time from server1 and uses it to set the time on server 2.

Bash brace expansion

Some examples of Bash's brace expansion.

The values inside the braces are repeated

echo a{b,c,d}e
abe ace ade
And you can embed braces within braces

echo a{b,c,d{e,f}}g
abg acg adeg adfg
Or it can be a sequence inside the braces

echo a{1..10}b
a1b a2b a3b a4b a5b a6b a7b a8b a9b a10b
Also includes an optional increment

echo a{1..10..2}
a1b a3b a5b a7b a9b
A simple real world example

mkdir -p james/{{logs,err}/{server1,server2},src,bin}

This makes the following directories:
james
james/logs
james/logs/server1
james/logs/server2
james/err
james/err/server1
james/err/server2
james/src
james/bin

Friday, 1 March 2019

Bash string and path manipulation

Just some notes on bash variable manipulation.

Sub stringing

name="James"
echo $name ${name} # Same result
James James
echo ${name:0:2} ${name::2} # Print first 2 characters, can omit the zero
Ja Ja
echo ${name::-2} # Remove n characters from the end
Jam
echo ${name:(-2):1} # Start 2 characters from the end and print 1 character
e
len=2
echo ${name:0:len} # Prints first 2 characters (note use of len not $len)
Ja
echo ${cake:-death} # Outputs $cake or the text death
death
cake="cakes"
echo ${cake:-death}
cakes
Moving on slightly to path maniplulation.

fName=/path/to/my/file.name
echo ${fName%.name} # Removes suffix .name
/path/to/my/file
echo ${fName#/path} # Removes prefix /path
/to/my/file.name
echo ${fName%.name}.type # Removes .name and replaces with .type
/path/to/my/file.type
echo ${fName##*.} # Prints the files extension
name
echo ${fName##*/} # Prints filename and extension, equivilent to basename
file.name
echo ${fName#*/} # Removes initial /
path/to/my/file.name
echo ${fName##*/} # Filename and extension
file.name
echo ${fName/a/A} # Replace first occurance of a with A
/pAth/to/my/file.name
echo ${fName//a/A} # Replace all occurances of a with A
/pAth/to/my/file.nAme
echo ${fName/%name/NAME} # Replace suffix
/path/to/my/file.NAME
echo ${#fName} # Print the length
21
a1=test
a3=${a2-$a1} # a3 = $a2 or $a1 if $a2 not set
a4=${a2-"this is a test"} # a3 = $a2 or "this is a test" if $a2 not set

echo "a1=$a1 : a2=$a2 : a3=$a3 : a4=$a4"
a1=test : a2= : a3=test : a4=this is a test
echo ${a5:=$a1} # Set a5 to $a1 if not set
test
echo ${a1:+$a4} # Prints $a4 id $a1 is set
this is a test
echo ${a2:?value is not set} # Prints error message is a2 not set
bash: a2: value is not set
: '
This is how
you print a
multi line
comment
'

Oracle CPU downloader

Every quarter I have to go through and download numerous patches for the Oracle CPU (Critical Patch Update). You have to view the CPU docume...