#!/usr/bin/perl
#
# cvmfs_talk talks to a running cvmfs instance.
# This is a simple query-response mechanism.
#
# SYNOPSIS
# cvmfs_talk [-i instance | -p socket] <command>
#
# Part of the CernVM File System.
#

use strict;
use warnings;
use Socket;
use Getopt::Long;

sub usage {
  print "Usage: $0 [-i instance | -p socket] <command>                     \n";
  print "   By default, iteratate through all instances.                   \n";
  print "\n";
  print "Example:                                                          \n";
  print "  $0 -i atlas.cern.ch pid                                         \n";
  print "\n";
  print "Commands:                                                         \n";
  print "  tracebuffer flush      flushes the trace buffer to disk         \n";
  print "  cache instance         describes the active cache manager       \n";
  print "  cache size             gets current size of file cache          \n";
  print "  cache list             gets files in cache                      \n";
  print "  cache list pinned      gets pinned file catalogs in cache       \n";
  print "  cache list catalogs    gets all file catalogs in cache          \n";
  print "  cleanup <MB>           cleans file cache until size <= <MB>     \n";
  print "  cleanup rate <period>  n.o. cleanups in the last <period> min   \n";
  print "  evict <path>           removes <path> from the cache            \n";
  print "  pin <path>             pins <path> in the cache                 \n";
  print "  mountpoint             returns the mount point                  \n";
  print "  remount [sync]         look for new catalogs                    \n";
  print "  revision               gets the repository revision             \n";
  print "  max ttl info           gets the maximum ttl                     \n";
  print "  max ttl set <minutes>  sets the maximum ttl                     \n";
  print "  nameserver set <host>  sets a DNS server                        \n";
  print "  host info              get host chain and their rtt,            \n";
  print "                         if already probed                        \n";
  print "  host probe             orders the host chain according to rtt   \n";
  print "  host probe geo         let Stratum 1s order the host chain and  \n";
  print "                         fallback proxies using the Geo-API       \n";
  print "  host switch            switches to the next host in the chain   \n";
  print "  host set <host list>   sets a new host chain                    \n";
  print "  proxy info             gets load-balance proxy groups           \n";
  print "  proxy rebalance        randomly selects a new proxy server      \n";
  print "                         from the current load-balance group      \n";
  print "  proxy group switch     switches to the next load-balance        \n";
  print "                         proxy group in the chain                 \n";
  print "  proxy set <proxy list> sets a new chain of load-balance proxy   \n";
  print "                         groups (not including fallback proxies)  \n";
  print "  proxy fallback <list>  sets a new list of fallback proxies      \n";
  print "  external host info     gets info about external host chain      \n";
  print "  external host switch   switches to the next external host       \n";
  print "  external host set                                               \n";
  print "       <host list>       sets external host chain                 \n";
  print "  external proxy info    gets info about external proxy groups    \n";
  print "  external proxy set                                              \n";
  print "       <proxy list>      sets chain of external proxy groups      \n";
  print "  timeout info           gets the network timeouts                \n";
  print "  timeout set                                                     \n";
  print "       <proxy> <direct>  sets the network timeouts in seconds     \n";
  print "  pid                    gets the pid                             \n";
  print "  pid cachemgr           gets the pid of the shared cache manager \n";
  print "  pid watchdog           gets the pid of the crash handler process\n";
  print "  parameters             dumps the effective parameters           \n";
  print "  reset error counters   resets the counter for I/O errors        \n";
  print "  hotpatch history       shows timestamps and version info of     \n";
  print "                         loaded (hotpatched) Fuse modules         \n";
  print "  version                gets cvmfs version                       \n";
  print "  version patchlevel     gets cvmfs patchlevel                    \n";
  print "  open catalogs          shows information about currently        \n";
  print "                         loaded catalogs (_not_ all cached ones)  \n";
  print "\n";

  exit 1;
}

my $instance_arg;
my $pipe_path_arg;
GetOptions("i:s" => \$instance_arg,
           "p:s" => \$pipe_path_arg);
my $command = join(' ', @ARGV) or usage;
$command .= "\0";

sub get_parameter {
  my $instance = shift;
  my $parameter = shift;

  my @config_line = grep(/^$parameter=/, `cvmfs_config showconfig $instance`);
  my @config;
  @config = split(' ', $config_line[0]) unless (!defined($config_line[0]));
  if (!defined($config[0]) || !$config[0]) {
    print "Could not figure out $parameter\n";
    return "";
  }
  (my $trash, my $value) = split(/=/, $config[0]);
  chomp($value);
  return $value;
}

sub talk {
  my $instance = shift;
  my $pipe_path;
  my $workspace;
  my $cache_dir;
  my $repository_name;

  if (!$pipe_path_arg) {
    $repository_name = get_parameter($instance, "CVMFS_REPOSITORY_NAME");
    $workspace = get_parameter($instance, "CVMFS_WORKSPACE");
    if ($workspace eq "") {
      $cache_dir = get_parameter($instance, "CVMFS_CACHE_DIR");
    } else {
      $cache_dir = $workspace
    }
    return 1 if ($repository_name eq "") || ($cache_dir eq "");
    $pipe_path = "$cache_dir/cvmfs_io.$repository_name"
  } else {
    $pipe_path = $pipe_path_arg;
    $cache_dir = "<specific pipe: $pipe_path>"
  }

  if (! -S "$pipe_path") {
    print "Seems like CernVM-FS is not running in $cache_dir (not found: $pipe_path)\n";
    return 1;
  }
  if (! -w "$pipe_path") {
    print "You don't have permissions to talk to instance in $cache_dir\n";
    return 1;
  }

  my $socket_name = sockaddr_un("$pipe_path");

  socket(SOCKET, PF_UNIX, SOCK_STREAM, 0);
  if (!connect(SOCKET, $socket_name)) {
    print "Can't connect to socket $pipe_path: $!\n";
    return 1;
  }

  send(SOCKET, $command, 0);

  my $response;
  my $length = 256;

  # read the response from cvmfs-instance (maximal $length bytes)
  while (sysread(SOCKET, $response, $length) && $response) {
    print $response;
  }

  return 0;
}

if ($instance_arg || $pipe_path_arg) {
  exit talk($instance_arg);
} else {
  my @config_line = grep(/^CVMFS_REPOSITORIES=/, `cvmfs_config showconfig`);
  if (scalar(@config_line) == 0) {
    print "No repositories specified (CVMFS_REPOSITORIES undefined)\n";
    exit 1;
  }

  my @config = split(' ', $config_line[0]);
  (my $trash, my $repo_list) = split(/=/, $config[0]);
  chomp($repo_list);
  my @repos = split(/,/, $repo_list);

  my $retval = 0;
  foreach my $i (@repos) {
    print "$i:\n";
    $retval += talk($i);
    print "\n";
  }
  exit $retval;
}
