Php – Netstat doesn’t work on FreeBSD by php function exec()

freebsdnetstatPHP

In my php-app I need the list of all IP's connected on port 80. I've chosen to do it this way:

<?php
     $ips = exec("netstat -an |grep 'tcp\|udp' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c", $info);
?>

But it doesn't work on my VPS FreeBSD server. When I start netstat -an a notice appears and no connections are in output:

netstat: kvm not available: /dev/mem: No such file or directory

I tried to add device mem to conf, but I have an empty /usr/src/sys. I got to the point that I need to rebuild the core.))

netstat works correctly when user is root (from console). I haven't had any problems like this on a similar CentOS hosting platform.

Fot resolving /dev/mem: No such file or directory I've tried to do mknod -m 660 /dev/mem c 1 1 but it calls an mknod: /dev/mem: Operation not permitted

I've got the following users pw showuser

owl:*:1000:1003:default:0:0:owl:/home/owl/data:/bin/csh
root:*:0:0::0:0:Charlie &:/root:/bin/csh

Summary:
When I run netstat as owl – it returns an empty list of connections
When I run netstat as root – it returns notice netstat: kvm not available: /dev/mem: No such file or directory and IP's

Can anybody help me? Is there maybe another way that exists for resolving this task? Thank you

Best Answer

I believe your cut is not behaving as you expect, since in netstat's output, the IP and port are separated by a dot, not a colon.

I also get a netstat: kvm not available: /dev/mem: No such file or directory error when I run netstat -an within some of the jails I administer, but despite this error, I still get expected results from netstat. I haven't researched why this occurs, so I'm leaning towards thinking that this is a red herring.

In terms of your command line, you can reduce your plumbing by packing more functionality into the awk script:

netstat -an | \
  awk '/^(tcp|udp)/{ip=$5; sub(/.[0-9]+$/,"",ip); list[ip]++;} END{for(ip in list){printf("%7d %s\n",list[ip],ip)}}'

Split up for easier reading, this awk script is:

# Only work on tcp/udp lines...
/^(tcp|udp)/ {
  ip=$5;
  sub(/.[0-9]+$/,"",ip);
  list[ip]++;  # Populate an array of counters-per-IP
}

# After data are gathered, print the results.
END {
  for (ip in list) {
    printf("%7d %s\n",list[ip],ip);
  }
}

But since you're doing this in PHP, I'd do more of this text processing in PHP.

<?php

# Gather our data...
exec("netstat -an", $out);

# Easy access to the output we want (for isset() below)
$permit = array("udp4"=>1,"tcp4"=>1);

foreach ($out as $line) {

  $a = preg_split("/[[:space:]]+/", $line);

  # Only work on tcp4/udp4 lines...
  if (isset($permit[$a[0]])) {
    # Populate an array of counters...
    $ip = substr($a[4], 0, strrpos($a[4],".")-1);
    $list[$ip]++;
  }

}

# And print the results.
foreach ($list as $ip => $count) {
  printf("%7s\t%s\n", $count, $ip);
}

This may seem like more work, but it may actually be faster than spawning a collection of pipes.

Note that you also have the sockstat command in FreeBSD, so building on Chris S's suggestion:

<?php

exec("sockstat -4", $out);

$permit = array("udp4"=>1,"tcp4"=>1);

foreach ($out as $line) {

  $a = preg_split("/[[:space:]]+/", $line);

  if (isset($permit[$a[4]])) {
    preg_match("/^[^:]+/", $a[6], $ip);
    $list[$ip[0]]++;
  }

}

foreach ($list as $ip => $count) {
  printf("%7s\t%s\n", $count, $ip);
}

UPDATE:

I also came across this ancient note about the kvm error. Since routing table information is usually available, you should contact your VPS provider and find out whether they have intentionally restricted your access to routing table data on this host. If you are not in a jail, then I suspect that a post to one of the FreeBSD mailing lists would get results. There's also a ##FreeBSD channel at irc.freenode.net where knowledgeable people often hang out.