This glab post is essentially about running a certain shell command remotely on multiple systems within a network that has been set up for public-key authentication. It's a standard task for experienced system administrators I am sure, but for me it was a little adventure in bash scripting — and I wanted to share it with you.
<span id="more-1806"></span> I am on a large network. It's one of those large university networks with public-key authentication. So everyone has his own computer, but you can login to a bunch of machines, even the ones in other peoples' offices. Over night, many of my colleagues do not need their computer. Me on the other hand, I need to crunch some numbers every now and then. My mission: Find all the machines that I have access to and list the number of CPUs they have.
<h2>List all computers</h2>
First, let's list all computers on the network. Easy enough, grab <a href="http://www.nmap.org/" target="_blank">nmap</a> and put the following into `quest.sh`:
```bash
nmap -sL $1 | egrep "for.*\(" | sed "s/.*for \([^\\( ]*\).*$/\1/" > netmap
```
A simple call to `quest.sh 192.168.*.*` will give you all the hosts that are around you. You can give it any IP range ((If you do not know the network you are on, you should probably not be doing this anyway - still, call `/sbin/ifconfig` to get your own IP address and the network you are on.)). What does it do? It first uses <a href="http://nmap.org/book/man-host-discovery.html" target="_blank">nmap host discovery</a> to make a list of hosts. The list looks a little something like this:
```bash
rattle@discovery1[~]$ nmap -sL 192.168.2.10-23
Starting Nmap 6.25 ( http://nmap.org ) at 2013-05-09 18:37 CEST
Nmap scan report for 192.168.2.10
Nmap scan report for 192.168.2.11
Nmap scan report for saturn.yournetwork.com (192.168.2.12)
Nmap scan report for jupiter.yournetwork.com (192.168.2.13)
Nmap scan report for 192.168.2.14
Nmap scan report for 192.168.2.15
Nmap scan report for 192.168.2.16
Nmap scan report for 192.168.2.17
Nmap scan report for uranus.yournetwork.com (192.168.2.18)
Nmap scan report for neptune.yournetwork.com (192.168.2.19)
Nmap scan report for 192.168.2.20
Nmap scan report for 192.168.2.21
Nmap scan report for 192.168.2.22
Nmap scan report for pluto.yournetwork.com (192.168.2.23)
Nmap done: 14 IP addresses (0 hosts up) scanned in 0.01 seconds
```
The pipe through `egrep` reduces it to the following:
```bash
rattle@discovery1[~]$ nmap -sL 192.168.2.10-23 | egrep "for.*\("
Nmap scan report for saturn.yournetwork.com (192.168.2.12)
Nmap scan report for jupiter.yournetwork.com (192.168.2.13)
Nmap scan report for uranus.yournetwork.com (192.168.2.18)
Nmap scan report for neptune.yournetwork.com (192.168.2.19)
Nmap scan report for pluto.yournetwork.com (192.168.2.23)
```
And finally, the call to `sed` will cut out only the hostnames.
<h2>Probe your targets</h2>
For each host on the network, we want to know if we have access to it. If that is the case, I also find it quite relevant how many CPU kernels it has. It's essentially a simple loop: For each host which was detected by the nmap run, we attempt to execute a command remotely on the host which counts the number of CPUs.
```bash
for host in $(cat netmap); do
count=$(ssh -oConnectTimeout=2 -oBatchMode=yes $host \
"cat /proc/cpuinfo" 2>&1 | grep proc | wc -l)
if [ "$count" != "0" ]; then echo $host \($count CPUs\); fi
done
```
The most important ingredient to this script is <a href="http://serverfault.com/questions/61915/how-do-i-make-ssh-fail-rather-than-prompt-for-a-password-if-the-public-key-authe" target="_blank">this question on serverfault</a>, which makes the script completely non-interactive.
<h2>Scan Subnet by default</h2>
Finally, I wanted the script to be more fancy. It now grabs my own IP address and the subnet mask from ifconfig and scans the current subnet by default. This is what I ended up with:
```bash
if [ -n "$1" ]; then a="$1"; else
a=$(/sbin/ifconfig eth0 | perl -nle"/dr:(\S+)/ && print $1")
m=$(/sbin/ifconfig eth0 | perl -nle"/sk:(\S+)/ && print $1")
m=$(python -c"print(32-sum(bin(int(x)^255).count('1') for x in '$m'.split('.')))")
a="$a/$m"
fi
for host in $(nmap -sL $a |egrep "for.*\(" |sed "s/.*for \([^\\( ]*\).*$/\1/"); do
count=$(ssh -oConnectTimeout=2 -oBatchMode=yes $host \
"cat /proc/cpuinfo" 2>&1 | grep proc | wc -l)
if [ "$count" != "0" ]; then echo $host \($count CPUs\); fi
done
```
It works like a charm and can easily be modified to extract lots of other information. Oh right, I almost forgot. <a href="http://www.commandlinefu.com" target="_blank">Commandlinefu</a> is an awesome website, check it out if you do shell scripting.