Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / sockstat / sockstat.pl
1 #!/usr/bin/perl -w
2 #-
3 # Copyright (c) 1999 Dag-Erling Coïdan Smørgrav
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer
11 #    in this position and unchanged.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #    documentation and/or other materials provided with the distribution.
15 # 3. The name of the author may not be used to endorse or promote products
16 #    derived from this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #
29 # $FreeBSD: src/usr.bin/sockstat/sockstat.pl,v 1.6.2.6 2003/05/05 06:44:26 murray Exp $
30 #
31
32 use strict;
33 use Getopt::Std;
34
35 my %netstat;
36 my %fstat;
37 my $unknown = [ "?", "?", "?", "?", "?", "?", "?", "?", "?" ];
38
39 my $inet_fmt = "%-8.8s %-8.8s %5.5s %4.4s %-6.6s %-21.21s %-21.21s\n";
40 my $unix_fmt = "%-8.8s %-8.8s %5.5s %4.4s %-6.6s %-43.43s\n";
41
42 #
43 # Gather information about sockets
44 #
45 sub gather() {
46     
47     local *PIPE;                # Pipe
48     my $pid;                    # Child PID
49     my $line;                   # Input line
50     my @fields;                 # Fields
51
52     # Netstat
53     if (!defined($pid = open(PIPE, "-|"))) {
54         die("open(netstat): $!\n");
55     } elsif ($pid == 0) {
56         exec("/usr/bin/netstat", "-AanW");
57         die("exec(netstat): $!\n");
58     }
59     while ($line = <PIPE>) {
60         next unless ($line =~ m/^[0-9a-f]{8} /) || ($line =~ m/^[0-9a-f]{16} /);
61         chomp($line);
62         @fields = split(' ', $line);
63         $netstat{$fields[0]} = [ @fields ];
64     }
65     close(PIPE)
66         or die("close(netstat): $!\n");
67
68     # Fstat
69     if (!defined($pid = open(PIPE, "-|"))) {
70         die("open(fstat): $!\n");
71     } elsif ($pid == 0) {
72         exec("/usr/bin/fstat");
73         die("exec(fstat): $!\n");
74     }
75     while ($line = <PIPE>) {
76         chomp($line);
77         @fields = split(' ', $line);
78         next if ($fields[4] eq "-");
79         push(@{$fstat{$fields[4]}}, [ @fields ]);
80     }
81     close(PIPE)
82         or die("close(fstat): $!\n");
83 }
84
85 #
86 # Replace the last dot in an "address.port" string with a colon
87 #
88 sub addr($) {
89     my $addr = shift;           # Address
90
91     $addr =~ s/^(.*)\.([^\.]*)$/$1:$2/;
92     return $addr;
93 }
94
95 #
96 # Print information about Internet sockets
97 #
98 sub print_inet($$$) {
99     my $af = shift;             # Address family
100     my $conn = shift || 0;      # Show connected sockets
101     my $listen = shift || 0;    # Show listen sockets
102
103     my $fsd;                    # Fstat data
104     my $nsd;                    # Netstat data
105
106     printf($inet_fmt, "USER", "COMMAND", "PID", "FD",
107            "PROTO", "LOCAL ADDRESS", "FOREIGN ADDRESS");
108     foreach $fsd (@{$fstat{$af}}) {
109         next unless defined($fsd->[7]);
110         $nsd = $netstat{$fsd->[7]} || $unknown;
111         next if (!$conn && $nsd->[5] ne '*.*');
112         next if (!$listen && $nsd->[5] eq '*.*');
113         printf($inet_fmt, $fsd->[0], $fsd->[1], $fsd->[2],
114                substr($fsd->[3], 0, -1),
115                $nsd->[1], addr($nsd->[4]), addr($nsd->[5]));
116     }
117     print("\n");
118 }
119
120 #
121 # Print information about Unix domain sockets
122 #
123 sub print_unix($$) {
124     my $conn = shift || 0;      # Show connected sockets
125     my $listen = shift || 0;    # Show listen sockets
126
127     my %endpoint;               # Mad PCB to process/fd
128     my $fsd;                    # Fstat data
129     my $nsd;                    # Netstat data
130
131     foreach $fsd (@{$fstat{"local"}}) {
132         $endpoint{$fsd->[6]} = "$fsd->[1]\[$fsd->[2]\]:" .
133             substr($fsd->[3], 0, -1);
134     }
135     printf($unix_fmt, "USER", "COMMAND", "PID", "FD", "PROTO", "ADDRESS");
136     foreach $fsd (@{$fstat{"local"}}) {
137         next unless defined($fsd->[6]);
138         next if (!$conn && defined($fsd->[8]));
139         next if (!$listen && !defined($fsd->[8]));
140         $nsd = $netstat{$fsd->[6]} || $unknown;
141         printf($unix_fmt, $fsd->[0], $fsd->[1], $fsd->[2],
142                substr($fsd->[3], 0, -1), $fsd->[5],
143                $nsd->[8] || (($fsd->[8] && $endpoint{$fsd->[8]}) ? $endpoint{$fsd->[8]} : "(none)"));
144     }
145     print("\n");
146 }
147
148 #
149 # Print usage message and exit
150 #
151 sub usage() {
152     print(STDERR "Usage: sockstat [-46clu]\n");
153     exit(1);
154 }
155
156 MAIN:{
157     my %opts;                   # Command-line options
158
159     getopts("46clu", \%opts)
160         or usage();
161
162     gather();
163
164     if (!$opts{'4'} && !$opts{'6'} && !$opts{'u'}) {
165         $opts{'4'} = $opts{'6'} = $opts{'u'} = 1;
166     }
167     if (!$opts{'c'} && !$opts{'l'}) {
168         $opts{'c'} = $opts{'l'} = 1;
169     }
170     if ($opts{'4'}) {
171         print_inet("internet", $opts{'c'}, $opts{'l'});
172     }
173     if ($opts{'6'}) {
174         print_inet("internet6", $opts{'c'}, $opts{'l'});
175     }
176     if ($opts{'u'}) {
177         print_unix($opts{'c'}, $opts{'l'});
178     }
179     
180     exit(0);
181 }