File Coverage

blib/lib/App/GSD.pm
Criterion Covered Total %
statement 50 83 60.2
branch 3 24 12.5
condition 5 17 29.4
subroutine 12 15 80.0
pod 6 6 100.0
total 76 145 52.4


line stmt bran cond sub pod time code
1             package App::GSD;
2             # ABSTRACT: boost productivity by blocking distracting websites
3              
4 1     1   70665 use strict;
  1         2  
  1         34  
5 1     1   5 use warnings;
  1         3  
  1         29  
6 1     1   6997 use autodie qw(:all); # including system
  1         19909  
  1         7  
7 1     1   18739 use Carp;
  1         2  
  1         89  
8 1     1   4 use File::Slurp qw(read_file write_file);
  1         2  
  1         1190  
9              
10             my $DEFAULT_HOSTS = '/etc/hosts';
11              
12             my $START_TOKEN = '## start-gsd';
13             my $END_TOKEN = '## end-gsd';
14              
15             sub new {
16 1     1 1 688 my ($class, $config) = @_;
17 1         4 my $self = bless $config, $class;
18 1   33     22 $self->{hosts_file} ||= $DEFAULT_HOSTS;
19 1   50     7 $self->{block} ||= [];
20 1 50 33     12 if (exists $self->{network_command} && !ref $self->{network_command}) {
21             # Convert single string to arrayref
22 0         0 $self->{network_command} = [ $self->{network_command} ];
23             }
24              
25 1         4 return $self;
26             }
27              
28             sub work {
29 1     1 1 2 my $self = shift;
30 1         4 my $contents = read_file($self->hosts_file);
31 1 50 33     105 if ($contents =~ /^$START_TOKEN$/ && $contents =~ qr/^$END_TOKEN$/) {
32 0         0 croak "Work mode already set";
33             }
34              
35 1         5 open my $fh, '>>', $self->hosts_file;
36 1         1716 print {$fh} $START_TOKEN, "\n";
  1         7  
37 1         6 for my $site ($self->blocklist) {
38 4         6 print {$fh} "127.0.0.1\t", $site, "\n";
  4         10  
39             }
40 1         3 print {$fh} $END_TOKEN, "\n";
  1         4  
41              
42 1         5 $self->_flush_dns;
43             }
44              
45             sub play {
46 1     1 1 1875 my $self = shift;
47              
48 1         4 my $contents = read_file($self->hosts_file);
49 1         106 $contents =~ s/\Q$START_TOKEN\E.*\Q$END_TOKEN\E\n//s;
50 1         7 write_file($self->hosts_file, $contents);
51              
52 1         148 $self->_flush_dns;
53             }
54              
55             # Return what App::GSD thinks the hosts file is
56             sub hosts_file {
57 5     5 1 1010 my $self = shift;
58 5         39 return $self->{hosts_file};
59             }
60              
61             # Return network_command as 'undef' (if not specified)
62             # or arrayref
63             sub network_command {
64 2     2 1 4 my $self = shift;
65 2         4 return $self->{network_command};
66             }
67              
68             # List of sites to be blocked
69             sub blocklist {
70 1     1 1 2 my $self = shift;
71 1         7 my $block = $self->{block};
72 1         3 return map { ($_, "www.$_") } @$block;
  2         8  
73             }
74              
75             # Determine the best method of flushing DNS, and execute it
76             sub _flush_dns {
77 2     2   4 my $self = shift;
78 2         7 my $netcmd = $self->network_command;
79              
80 2 50 33     14 if (defined $netcmd && not @$netcmd) {
81             # Don't do any network-related stuff
82 2         33 return;
83             }
84             else {
85 0   0       $netcmd ||= $self->_platform_network_command;
86 0           system(@$netcmd);
87              
88 0 0         if ($^O eq 'linux') {
89 0           $self->_flush_nscd;
90             }
91             }
92             }
93              
94             # Return best method of flushing DNS for the target platform
95             sub _platform_network_command {
96 0     0     my $self = shift;
97 0           my $platform = $^O;
98 0           my $cmd;
99 0 0         if ($platform eq 'linux') {
    0          
100 0           $cmd = $self->_flush_dns_linux;
101             }
102             elsif ($platform eq 'darwin') {
103 0           $cmd = ['dscacheutil', '-flushcache'];
104             }
105             else {
106 0           croak "don't know how to flush DNS for platform '$platform'";
107             }
108 0           return $cmd;
109             }
110              
111             # Return DNS flush command for Linux. It needs to support a few scenarios:
112             # Ubuntu e.g. '/etc/init.d/networking restart' (or via upstart: 'restart networking')
113             # Arch using network module: '/etc/rc.d/network restart'
114             # Arch using NetworkManager or wicd: '/etc/rc.d/$foo restart'
115             sub _flush_dns_linux {
116 0     0     my $self = shift;
117 0           my $cmd;
118              
119 0 0         if (-x '/usr/sbin/rc.d') {
    0          
    0          
120             # Try to guess the user's preferred network module by looking for AUTO
121 0           my $services = `/usr/sbin/rc.d list | fgrep AUTO`;
122 0           for my $service (qw(networkmanager wicd network)) {
123 0 0         if ($services =~ /^\[STARTED\]\[AUTO\] $service$/m) {
124 0           $cmd = ['/usr/sbin/rc.d', 'restart', $service];
125 0           last;
126             }
127             }
128 0 0         if (!defined $cmd) {
129 0           croak "You appear to be using rc.d but I can't figure out which network module you are using.";
130             }
131             }
132             elsif (-x '/etc/init.d/networking') {
133 0           $cmd = ['/etc/init.d/networking', 'restart'];
134             }
135             elsif (-x '/etc/init.d/network') {
136 0           $cmd = ['/etc/init.d/network', 'restart'];
137             }
138             else {
139 0           croak "I can't figure out how to restart your network.";
140             }
141              
142 0           return $cmd;
143             }
144              
145             # Try to invalidate nscd/unscd cache if present
146             sub _flush_nscd {
147 0     0     my $self = shift;
148 0 0         return if $^O ne 'linux';
149 0           for my $nscd (qw(nscd unscd)) {
150             # Ignore errors if the daemon is installed, but not running
151 0           CORE::system("/usr/sbin/$nscd", '-i', 'hosts');
152             }
153 0           return;
154             }
155              
156             1;
157              
158              
159              
160             =pod
161              
162             =head1 NAME
163              
164             App::GSD - boost productivity by blocking distracting websites
165              
166             =head1 VERSION
167              
168             version 0.4
169              
170             =head1 SYNOPSIS
171              
172             use App::GSD;
173             my $app = App:GSD->new({ block => [qw(foo.com bar.com baz.com)] });
174             $app->work; # sites are now blocked
175             $app->play; # unblocked
176              
177             =head1 METHODS
178              
179             =head2 new ( \%args )
180              
181             The following arguments are accepted:
182              
183             =over
184              
185             =item block
186              
187             An arrayref of hostnames to block, without a 'www.' prefix (if
188             present) as these will be blocked automatically.
189              
190             =item hosts_file
191              
192             Path to the hosts file (e.g. '/etc/hosts'), overriding the
193             module's guess based on current operating system.
194              
195             =item network_command
196              
197             A reference to an array passable to C that will restart
198             the network, e.g.
199              
200             ['/etc/init.d/network', 'restart']
201              
202             =back
203              
204             =head2 work
205              
206             Set work mode - block the sites specified.
207              
208             =head2 play
209              
210             Set play mode - unblock sites.
211              
212             =head2 blocklist
213              
214             Return the blocklist, with 'www.' and non-'www.' versions included.
215              
216             =head2 network_command
217              
218             Return user-specified network command as arrayref, or undef if
219             none specified.
220              
221             =head2 hosts_file
222              
223             Return path to hosts file.
224              
225             =head1 METHODS
226              
227             =head1 AUTHOR
228              
229             Richard Harris
230              
231             =head1 COPYRIGHT AND LICENSE
232              
233             This software is copyright (c) 2012 by Richard Harris.
234              
235             This is free software; you can redistribute it and/or modify it under
236             the same terms as the Perl 5 programming language system itself.
237              
238             =cut
239              
240              
241             __END__