]> git.refcnt.org Git - distdns.git/blobdiff - client.pl
Mention all copyright years
[distdns.git] / client.pl
index 917eec30f1e90608d17e25fc11f9e9e9407fd4af..2a73f4e6de5f0f9218a98c69758224f40aed0841 100755 (executable)
--- a/client.pl
+++ b/client.pl
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 #
-# Copyright (c) 2013 Michel Ketterle, Steven Schubiger
+# Copyright (c) 2013, 2015 Michel Ketterle, Steven Schubiger
 #
 # This file is part of distdns.
 #
@@ -21,64 +21,102 @@ use strict;
 use warnings;
 use constant false => 0;
 
-use Config::Tiny;
-use Digest::MD5 qw(md5_hex);
-use Fcntl ':flock';
-use File::Spec::Functions qw(rel2abs);
-use Getopt::Long qw(:config no_auto_abbrev no_ignore_case);
-use JSON qw(decode_json);
-use LWP::UserAgent;
-use Sys::Hostname qw(hostname);
-use Tie::File;
-
-my $VERSION = '0.04';
+BEGIN
+{
+    my @modules = (
+        [ 'Config::Tiny',          [                                           ] ],
+        [ 'Digest::MD5',           [ qw(md5_hex)                               ] ],
+        [ 'Fcntl',                 [ qw(:flock)                                ] ],
+        [ 'File::Spec::Functions', [ qw(catfile rel2abs)                       ] ],
+        [ 'FindBin',               [ qw($Bin)                                  ] ],
+        [ 'Getopt::Long',          [ qw(:config no_auto_abbrev no_ignore_case) ] ],
+        [ 'JSON',                  [ qw(decode_json)                           ] ],
+        [ 'LWP::UserAgent',        [                                           ] ],
+        [ 'POSIX',                 [ qw(strftime)                              ] ],
+        [ 'Sys::Hostname',         [ qw(hostname)                              ] ],
+        [ 'Tie::File',             [                                           ] ],
+    );
+    my (@missing, @import);
+    foreach my $module (@modules) {
+        unless (eval "require $module->[0]; 1") {
+            push @missing, $module->[0];
+            next;
+        }
+        unless (eval { $module->[0]->import(@{$module->[1]}); 1 }) {
+            push @import, $module->[0];
+        }
+    }
+    if (@missing || @import) {
+        warn <<"EOT";
+Modules missing: @missing
+Import failures: @import
+EOT
+        exit 1;
+    }
+}
 
-#-----------------------
-# Start of configuration
-#-----------------------
+my $VERSION = '0.07';
 
-my $config_file  = 'dynuser.conf';
-my $hosts_file   = 'hosts';
-my $session_file = 'session.dat';
-my $server_url   = 'http://refcnt.org/~sts/cgi-bin/ketterle/server.cgi';
+my $conf_file = catfile($Bin, 'client.conf');
 
-#---------------------
-# End of configuration
-#---------------------
+sub _die { die "$0: [client] $_[0]" }
 
 sub usage
 {
+    warn "$_[0]\n" if defined $_[0];
     print <<"USAGE";
-Usage: $0
+Usage: $0 [options]
     -d, --debug    server debugging
     -h, --help     this help screen
     -i, --init     initialize session data
+    -l, --list     list remote entries
 USAGE
     exit;
 }
 
 my %opts;
-GetOptions(\%opts, qw(d|debug h|help i|init)) or usage();
+GetOptions(\%opts, qw(d|debug h|help i|init l|list)) or usage();
 usage() if $opts{h};
 
-$config_file  = rel2abs($config_file);
-$hosts_file   = rel2abs($hosts_file);
-$session_file = rel2abs($session_file);
+usage('Cannot combine --init and --list') if $opts{i} && $opts{l};
+
+my $config = Config::Tiny->new;
+   $config = Config::Tiny->read($conf_file);
+
+my $get_config_opts = sub
+{
+    my ($section, $options) = @_;
+
+    _die "Section '$section' missing in $conf_file\n" unless exists $config->{$section};
+
+    my %options;
+    @options{@$options} = @{$config->{$section}}{@$options};
+
+    foreach my $option (@$options) {
+        _die "Option '$option' not set in $conf_file\n" unless defined $options{$option} && length $options{$option};
+    }
+
+    return @options{@$options};
+};
+
+my ($hosts_file, $session_file) = map rel2abs($_, $Bin), $get_config_opts->('path', [ qw(hosts_file session_file) ]);
+
+my ($server_url)  = $get_config_opts->('url',  [ qw(server_url) ]);
+my ($netz, $name) = $get_config_opts->('data', [ qw(netz name)  ]);
 
 my $save_session = sub
 {
     my ($session) = @_;
 
-    open(my $fh, '>', $session_file) or die "Cannot open client-side $session_file for writing: $!\n";
+    open(my $fh, '>', $session_file) or _die "Cannot open $session_file for writing: $!\n";
     print {$fh} "$session\n";
     close($fh);
 };
 
 my $get_session = sub
 {
-    open(my $fh, '<', $session_file) or die "Cannot open client-side $session_file for reading: $!\nPerhaps try running --init\n";
-    my $session = do { local $/; <$fh> };
-    chomp $session;
+    open(my $fh, '<', $session_file) or _die "Cannot open $session_file for reading: $!\nPerhaps try running --init\n";
+    chomp(my $session = <$fh>);
     close($fh);
 
     return $session;
@@ -86,19 +124,13 @@ my $get_session = sub
 
 my $session = $opts{i} ? substr(md5_hex(md5_hex(time() . {} . rand() . $$)), 0, 32) : $get_session->();
 
-my $config = Config::Tiny->new;
-   $config = Config::Tiny->read($config_file);
-
-my ($netz, $name) = @{$config->{data}}{qw(netz name)};
-
-die "$0: Network and/or name not set in $config_file\n" unless defined $netz && defined $name;
-
 my %params = (
     netz    => $netz,
     pc      => hostname(),
     name    => $name,
     debug   => $opts{d} || false,
     init    => $opts{i} || false,
+    list    => $opts{l} || false,
     session => $session,
 );
 
@@ -113,38 +145,55 @@ if ($response->is_success) {
         $data = decode_json($response->decoded_content);
     } or exit;
 
-    die "$0: $data->{error}" if defined $data->{error};
-
-    $save_session->($session) if $opts{i};
+    die "$0: [server] $data->{error}" if defined $data->{error};
 
-    my %list;
-    foreach my $entry (@{$data->{entries}}) {
-        my $host = "$entry->{ip}\t" . join '.', @$entry{qw(name pc netz)};
-        push @{$list{$entry->{netz}}}, $host;
+    if ($opts{i}) {
+        $save_session->($session);
     }
+    elsif ($opts{l}) {
+        format STDOUT_TOP =
+IP                 Name               PC                      Netz               Aktualisiert
+====================================================================================================
+.
+        foreach my $entry (sort { $a->{netz} cmp $b->{netz} } @{$data->{entries}}) {
+            my $updated = strftime '%Y-%m-%d %H:%M:%S', localtime $entry->{time};
+            format STDOUT =
+@<<<<<<<<<<<<<<    @<<<<<<<<<<<<<<    @<<<<<<<<<<<<<<<<<<<    @<<<<<<<<<<<<<<    @<<<<<<<<<<<<<<<<<<
+@$entry{qw(ip name pc netz)}, $updated
+.
+            write;
+        }
+    }
+    else {
+        my %list;
+        foreach my $entry (@{$data->{entries}}) {
+            my $host = "$entry->{ip}\t" . join '.', @$entry{qw(name pc netz)};
+            push @{$list{$entry->{netz}}}, $host;
+        }
 
-    my $o = tie my @hosts, 'Tie::File', $hosts_file or die "$0: Cannot tie $hosts_file: $!\n";
-    $o->flock(LOCK_EX);
-
-    foreach my $network (keys %list) {
-        my %indexes;
-        for (my $i = 0; $i < @hosts; $i++) {
-            if ($hosts[$i] =~ /^\#$network\#$/i) {
-                $indexes{start} = $i;
-            }
-            elsif (exists $indexes{start} && $hosts[$i] =~ /^\#\/$network\#$/i) {
-                $indexes{end} = $i;
-                my $count = ($indexes{end} - $indexes{start} > 1)
-                  ? $indexes{end} - $indexes{start} - 1
-                  : 0;
-                splice @hosts, $indexes{start} + 1, $count, @{$list{$network}};
-                last;
+        my $o = tie my @hosts, 'Tie::File', $hosts_file or _die "Cannot tie $hosts_file: $!\n";
+        $o->flock(LOCK_EX);
+
+        foreach my $network (keys %list) {
+            my %indexes;
+            for (my $i = 0; $i < @hosts; $i++) {
+                if ($hosts[$i] =~ /^\#$network\#$/i) {
+                    $indexes{start} = $i;
+                }
+                elsif (exists $indexes{start} && $hosts[$i] =~ /^\#\/$network\#$/i) {
+                    $indexes{end} = $i;
+                    my $count = ($indexes{end} - $indexes{start} > 1)
+                      ? $indexes{end} - $indexes{start} - 1
+                      : 0;
+                    splice @hosts, $indexes{start} + 1, $count, @{$list{$network}};
+                    last;
+                }
             }
         }
-    }
 
-    undef $o;
-    untie @hosts;
+        undef $o;
+        untie @hosts;
+    }
 }
 else {
     warn $response->status_line, "\n";