File Coverage

blib/lib/Net/Connection/ncnetstat.pm
Criterion Covered Total %
statement 29 197 14.7
branch 0 80 0.0
condition 0 24 0.0
subroutine 10 12 83.3
pod 2 2 100.0
total 41 315 13.0


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