File Coverage

blib/lib/Net/Connection/ncnetstat.pm
Criterion Covered Total %
statement 32 203 15.7
branch 0 82 0.0
condition 0 24 0.0
subroutine 11 13 84.6
pod 2 2 100.0
total 45 324 13.8


line stmt bran cond sub pod time code
1             package Net::Connection::ncnetstat;
2              
3 2     2   214007 use 5.006;
  2         19  
4 2     2   12 use strict;
  2         4  
  2         129  
5 2     2   12 use warnings;
  2         5  
  2         60  
6 2     2   1128 use Net::Connection;
  2         220865  
  2         192  
7 2     2   1285 use Net::Connection::Match;
  2         4720  
  2         100  
8 2     2   1130 use Net::Connection::Sort;
  2         905  
  2         81  
9 2     2   1376 use Term::ANSIColor;
  2         28823  
  2         344  
10 2     2   1252 use Proc::ProcessTable;
  2         13103  
  2         114  
11 2     2   2965 use Text::ANSITable;
  2         186346  
  2         130  
12              
13             # use Net::Connection::FreeBSD_sockstat if possible
14 2     2   1764 use if $^O eq 'freebsd', 'Net::Connection::FreeBSD_sockstat';
  2         30  
  2         15  
15 2     2   104 use if $^O ne 'freebsd', 'Net::Connection::lsof';
  2         5  
  2         9  
16              
17              
18             =head1 NAME
19              
20             Net::Connection::ncnetstat - The backend for ncnetstat, the colorized and enhanced netstat like tool.
21              
22             =head1 VERSION
23              
24             Version 0.6.4
25              
26             =cut
27              
28             our $VERSION = '0.6.4';
29              
30              
31             =head1 SYNOPSIS
32              
33             use Net::Connection::ncnetstat;
34            
35             # Net::Connection::Match filters
36             my @filters=(
37             {
38             type=>'States',
39             invert=>1,
40             args=>{
41             states=>['LISTEN']
42             }
43             }
44             );
45            
46             my $ncnetstat=Net::Connection::ncnetstat->new(
47             {
48             ptr=>1,
49             command=>1,
50             command_long=>0,
51             wchan=>0,
52             pct_show=>1,
53             sorter=>{
54             invert=>0,
55             type=>'host_lf',
56             },
57             match=>{
58             checks=>\@filters,
59             }
60             }
61             );
62            
63             print $ncnetstat->run;
64              
65             =head1 METHODS
66              
67             =head2 new
68              
69             This initiates the object.
70              
71             my $ncnetstat=Net::Connection::ncnetstat->new( \%args );
72              
73             =head3 args hash ref
74              
75             =head4 command
76              
77             If set to true, it will show the command for the PID.
78              
79             =head4 command_long
80              
81             If set to true, the full command is shown.
82              
83             This requires command also being true.
84              
85             =head4 match
86              
87             This is the hash to pass to L<Net::Connection::Match>.
88              
89             By default this is undef and that module won't be used.
90              
91             =head4 no_pid_user
92              
93             Don't show the PID or UID/user colomn.
94              
95             =head4 sorter
96              
97             This is what is to be passed to L<Net::Connection::Sorter>.
98              
99             The default is as below.
100              
101             {
102             type=>'host_fl',
103             invert=>0,
104             }
105              
106             =cut
107              
108             sub new {
109 0     0 1   my %args;
110 0 0         if ( defined( $_[1] ) ) {
111 0           %args = %{ $_[1] };
  0            
112             }
113              
114 0 0         if ( !defined( $args{sorter} ) ) {
115             $args{sorter} = {
116 0           type => 'host_fl',
117             invert => 0,
118             };
119             }
120              
121             my $self = {
122             invert => 0,
123 0           sorter => Net::Connection::Sort->new( $args{sorter} ),
124             ptr => 1,
125             command => 0,
126             command_long => 0,
127             wchan => 0,
128             pct => 0,
129             no_pid_user => 0,
130             };
131 0           bless $self;
132              
133 0 0         if ( defined( $args{match} ) ) {
134 0           $self->{match} = Net::Connection::Match->new( $args{match} );
135             }
136              
137 0 0         if ( defined( $args{ptr} ) ) {
138 0           $self->{ptr} = $args{ptr};
139             }
140              
141 0 0         if ( defined( $args{command} ) ) {
142 0           $self->{command} = $args{command};
143             }
144              
145 0 0         if ( defined( $args{pct} ) ) {
146 0           $self->{pct} = $args{pct};
147             }
148              
149 0 0         if ( defined( $args{wchan} ) ) {
150 0           $self->{wchan} = $args{wchan};
151             }
152              
153 0 0         if ( defined( $args{command_long} ) ) {
154 0           $self->{command_long} = $args{command_long};
155             }
156              
157 0 0         if ( defined( $args{no_pid_user} ) ) {
158 0           $self->{no_pid_user} = $args{no_pid_user};
159             }
160              
161 0           return $self;
162             }
163              
164             =head2 run
165              
166             This runs it and returns a string.
167              
168             print $ncnetstat->run;
169              
170             =cut
171              
172             sub run {
173 0     0 1   my $self = $_[0];
174              
175 0           my @objects;
176 0 0         if ( $^O !~ /freebsd/ ) {
177 0           @objects = &lsof_to_nc_objects;
178             }
179             else {
180 0           @objects = &sockstat_to_nc_objects;
181             }
182              
183 0           my @found;
184 0 0         if ( defined( $self->{match} ) ) {
185 0           foreach my $conn (@objects) {
186 0 0         if ( $self->{match}->match($conn) ) {
187 0           push( @found, $conn );
188             }
189             }
190             }
191             else {
192 0           @found = @objects;
193             }
194              
195 0           @found = $self->{sorter}->sorter( \@found );
196              
197 0           my $tb = Text::ANSITable->new;
198 0           $tb->border_style('ASCII::None');
199 0           $tb->color_theme('NoColor');
200              
201 0           my @headers;
202 0           my $header_int = 0;
203 0           push( @headers, 'Proto' );
204 0           $tb->set_column_style( $header_int, pad => 0 );
205 0           $header_int++;
206 0 0         if ( !$self->{no_pid_user} ) {
207 0           push( @headers, 'User' );
208 0           $tb->set_column_style( $header_int, pad => 1 );
209 0           $header_int++;
210 0           push( @headers, 'PID' );
211 0           $tb->set_column_style( $header_int, pad => 0 );
212 0           $header_int++;
213             }
214 0           push( @headers, 'Local Host' );
215 0           $tb->set_column_style( $header_int, pad => 1, formats => [ [ wrap => { ansi => 1, mb => 1 } ] ] );
216 0           $header_int++;
217 0           push( @headers, 'L Port' );
218 0           $tb->set_column_style( $header_int, pad => 0 );
219 0           $header_int++;
220 0           push( @headers, 'Remote Host' );
221 0           $tb->set_column_style( $header_int, pad => 1, formats => [ [ wrap => { ansi => 1, mb => 1 } ] ] );
222 0           $header_int++;
223 0           push( @headers, 'R Port' );
224 0           $tb->set_column_style( $header_int, pad => 0 );
225 0           $header_int++;
226 0           push( @headers, 'State' );
227 0           $tb->set_column_style( $header_int, pad => 1 );
228 0           $header_int++;
229              
230 0           my $padding = 0;
231 0 0         if ( $self->{wchan} ) {
232 0 0         if ( ( $header_int % 2 ) != 0 ) {
233 0           $padding = 1;
234             }
235 0           push( @headers, 'WChan' );
236 0           $tb->set_column_style( $header_int, pad => $padding );
237 0           $header_int++;
238             }
239              
240 0 0         if ( $self->{pct} ) {
241 0 0         if ( ( $header_int % 2 ) != 0 ) {
242 0           $padding = 1;
243             }
244             else {
245 0           $padding = 0;
246             }
247 0           push( @headers, 'CPU%' );
248 0           $tb->set_column_style( $header_int, pad => $padding );
249 0           $header_int++;
250 0 0         if ( ( $header_int % 2 ) != 0 ) {
251 0           $padding = 1;
252             }
253             else {
254 0           $padding = 0;
255             }
256 0           push( @headers, 'Mem%' );
257 0           $tb->set_column_style( $header_int, pad => $padding );
258 0           $header_int++;
259             }
260              
261 0 0         if ( $self->{command} ) {
262 0 0         if ( ( $header_int % 2 ) != 0 ) {
263 0           $padding = 1;
264             }
265             else {
266 0           $padding = 0;
267             }
268 0           push( @headers, 'Command' );
269 0           $tb->set_column_style( $header_int, pad => $padding, formats => [ [ wrap => { ansi => 1, mb => 1 } ] ] );
270 0           $header_int++;
271             }
272              
273 0           $tb->set_column_style( 4, pad => 0 );
274 0           $tb->set_column_style( 5, pad => 1, formats => [ [ wrap => { ansi => 1, mb => 1 } ] ] );
275 0           $tb->set_column_style( 6, pad => 0 );
276 0           $tb->set_column_style( 7, pad => 1 );
277 0           $tb->set_column_style( 8, pad => 0 );
278 0           $tb->set_column_style( 9, pad => 1 );
279 0           $tb->set_column_style( 10, pad => 0 );
280 0           $tb->set_column_style( 11, pad => 1, formats => [ [ wrap => { ansi => 1, mb => 1 } ] ] );
281 0           $tb->set_column_style( 12, pad => 0 );
282 0           $tb->set_column_style( 13, pad => 1 );
283              
284 0           $tb->columns( \@headers );
285              
286             # process table stuff if needed
287 0           my $ppt;
288             my $proctable;
289 0           my %cmd_cache;
290 0 0         if ( $self->{command} ) {
291 0           $ppt = Proc::ProcessTable->new;
292 0           $proctable = $ppt->table;
293             }
294              
295 0           my @td;
296 0           foreach my $conn (@found) {
297 0           my @new_line = ( color('bright_yellow') . $conn->proto . color('reset'), );
298              
299             # don't add the PID or user if requested to
300 0 0         if ( !$self->{no_pid_user} ) {
301              
302             # handle adding the username or UID if we have one
303 0 0         if ( defined( $conn->username ) ) {
304 0           push( @new_line, color('bright_cyan') . $conn->username . color('reset') );
305             }
306             else {
307 0 0         if ( defined( $conn->uid ) ) {
308 0           push( @new_line, color('bright_cyan') . $conn->uid . color('reset') );
309             }
310             else {
311 0           push( @new_line, '' );
312             }
313             }
314              
315             # handle adding the PID if we have one
316 0 0         if ( defined( $conn->pid ) ) {
317 0           push( @new_line, color('bright_red') . $conn->pid . color('reset') );
318 0           $conn->pid;
319             }
320             else {
321 0           push( @new_line, '' );
322             }
323             }
324              
325             # Figure out what we are using for the local host
326 0           my $local;
327 0 0 0       if ( defined( $conn->local_ptr ) && $self->{ptr} ) {
328 0           $local = $conn->local_ptr;
329             }
330             else {
331 0           $local = $conn->local_host;
332             }
333              
334             # Figure out what we are using for the foriegn host
335 0           my $foreign;
336 0 0 0       if ( defined( $conn->foreign_ptr ) && $self->{ptr} ) {
337 0           $foreign = $conn->foreign_ptr;
338             }
339             else {
340 0           $foreign = $conn->foreign_host;
341             }
342              
343             # Figure out what we are using for the local port
344 0           my $lport;
345 0 0         if ( defined( $conn->local_port_name ) ) {
346 0           $lport = $conn->local_port_name;
347             }
348             else {
349 0           $lport = $conn->local_port;
350             }
351              
352             # Figure out what we are using for the foreign port
353 0           my $fport;
354 0 0         if ( defined( $conn->foreign_port_name ) ) {
355 0           $fport = $conn->foreign_port_name;
356             }
357             else {
358 0           $fport = $conn->foreign_port;
359             }
360              
361 0           push( @new_line,
362             color('bright_green') . $local . color('reset'),
363             color('green') . $lport . color('reset'),
364             color('bright_magenta') . $foreign . color('reset'),
365             color('magenta') . $fport . color('reset'),
366             color('bright_blue') . $conn->state . color('reset'),
367             );
368              
369             # handle the wchan bit if needed
370 0 0 0       if ( $self->{wchan}
371             && defined( $conn->wchan ) )
372             {
373 0           push( @new_line, color('bright_yellow') . $conn->wchan . color('reset') );
374             }
375              
376             # handle the percent stuff if needed
377 0 0 0       if ( $self->{pct}
378             && defined( $conn->pctcpu ) )
379             {
380 0           push( @new_line, color('bright_cyan') . sprintf( '%.2f', $conn->pctcpu ) . color('reset') );
381             }
382              
383             # handle the percent stuff if needed
384 0 0 0       if ( $self->{pct}
385             && defined( $conn->pctmem ) )
386             {
387 0           push( @new_line, color('bright_green') . sprintf( '%.2f', $conn->pctmem ) . color('reset') );
388             }
389              
390             # handle the command portion if needed
391 0 0 0       if ( defined( $conn->pid )
    0 0        
392             && $self->{command} )
393             {
394              
395 0           my $loop = 1;
396 0           my $proc = 0;
397 0   0       while ( defined( $proctable->[$proc] )
398             && $loop )
399             {
400 0           my $command;
401 0 0         if ( defined( $cmd_cache{ $conn->pid } ) ) {
    0          
    0          
402 0           push( @new_line, color('bright_red') . $cmd_cache{ $conn->pid } . color('reset') );
403 0           $loop = 0;
404             }
405             elsif ( defined( $conn->proc ) ) {
406 0           my $command = $conn->proc;
407 0 0         if ( !$self->{command_long} ) {
408 0           $command =~ s/\ .*//;
409             }
410 0           $cmd_cache{ $conn->pid } = $command;
411 0           push( @new_line, color('bright_red') . $cmd_cache{ $conn->pid } . color('reset') );
412 0           $loop = 0,;
413             }
414             elsif ( $proctable->[$proc]->pid eq $conn->pid ) {
415 0 0         if ( $proctable->[$proc]->{'cmndline'} =~ /^$/ ) {
    0          
    0          
416              
417             #kernel process
418             $cmd_cache{ $conn->pid }
419 0           = color('bright_red') . '[' . $proctable->[$proc]->{'fname'} . ']' . color('reset');
420             }
421             elsif ( $self->{command_long} ) {
422             $cmd_cache{ $conn->pid }
423 0           = color('bright_red') . $proctable->[$proc]->{'cmndline'} . color('reset');
424             }
425             elsif ( $proctable->[$proc]->{'cmndline'} =~ /^\// ) {
426              
427             # something ran with a complete path
428             $cmd_cache{ $conn->pid }
429 0           = color('bright_red') . $proctable->[$proc]->{'fname'} . color('reset');
430             }
431             else {
432             # likely a thread or the like... such as dovecot/auth
433             # just trunkcat everything after the space
434 0           my $cmd = $proctable->[$proc]->{'cmndline'};
435 0           $cmd =~ s/\ +.*//g;
436 0           $cmd_cache{ $conn->pid } = color('bright_red') . $cmd . color('reset');
437             }
438              
439 0           push( @new_line, $cmd_cache{ $conn->pid } );
440 0           $loop = 0;
441             }
442              
443 0           $proc++;
444             }
445              
446             }
447             elsif ( ( !defined( $conn->pid ) )
448             && $self->{command} )
449             {
450 0           push( @new_line, '' );
451             }
452              
453 0           $tb->add_row( \@new_line );
454             }
455              
456 0           return $tb->draw;
457             }
458              
459             =head1 TODO
460              
461             * Add support for more collection methods than L<Net::Connection::lsof>
462              
463             * Support color selection and column ordering.
464              
465             =head1 AUTHOR
466              
467             Zane C. Bowers-Hadley, C<< <vvelox at vvelox.net> >>
468              
469             =head1 BUGS
470              
471             Please report any bugs or feature requests to C<bug-net-connection-ncnetstat at rt.cpan.org>, or through
472             the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-Connection-ncnetstat>. I will be notified, and then you'll
473             automatically be notified of progress on your bug as I make changes.
474              
475              
476              
477              
478             =head1 SUPPORT
479              
480             You can find documentation for this module with the perldoc command.
481              
482             perldoc Net::Connection::ncnetstat
483              
484              
485             You can also look for information at:
486              
487             =over 4
488              
489             =item * RT: CPAN's request tracker (report bugs here)
490              
491             L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-Connection-ncnetstat>
492              
493             =item * AnnoCPAN: Annotated CPAN documentation
494              
495             L<http://annocpan.org/dist/Net-Connection-ncnetstat>
496              
497             =item * CPAN Ratings
498              
499             L<https://cpanratings.perl.org/d/Net-Connection-ncnetstat>
500              
501             =item * Search CPAN
502              
503             L<https://metacpan.org/release/Net-Connection-ncnetstat>
504              
505             =item * Repository
506              
507             L<https://github.com/VVelox/Net-Connection-ncnetstat>
508              
509             =back
510              
511              
512             =head1 ACKNOWLEDGEMENTS
513              
514              
515             =head1 LICENSE AND COPYRIGHT
516              
517             This software is Copyright (c) 2019 by Zane C. Bowers-Hadley.
518              
519             This is free software, licensed under:
520              
521             The Artistic License 2.0 (GPL Compatible)
522              
523              
524             =cut
525              
526             1; # End of Net::Connection::ncnetstat