#!/usr/bin/perl

# This program reads Captrap's configuration and generates suitable MySQL
# commands for creating a pmacct database that Captrap can use.

# Copyright 2009 Corey Hickey


# This file is part of Captrap.
#
# Captrap is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Captrap is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Captrap.  If not, see <http://www.gnu.org/licenses/>.


=head1 NAME

captrap_mkdb - a program for helping set up a pmacct database that Captrap can
use

=head1 SYNOPSIS

captrap_mkdb -print | mysql

=head1 DESCRIPTION

This is a script for printing MySQL statements that create a pmacct database
and tables suitable for Captrap. Specifically, the statements printed: 

=over

=item 1. Create the database (unless it already exists).

=item 2. Create "acct" and "macs" tables.

=item 3. Grant the pmacctd user necessary mysql privileges.

=item 4. Grant the captrap user necessary mysql privileges.

=back

The database name, table names, and user/password information are read from
Captrap's configuration files, which should be set up prior to running this
script.

=head1 OPTIONS

=over

=item -help

Print brief usage text.

=item -print

Print MySQL statements.

=back

=head1 EXIT STATUS

=over

=item 0Z<>

Everything is ok.

=item 1Z<>

No arguments given; usage information was shown.

=item 2Z<>

Invalid argument.

=item 3Z<>

There was a problem executing an action.

=back

=head1 FILES

=over

=item /etc/captrap/captrap.conf

The main Captrap configuration file.

=item /etc/captrap/priv.conf

The privileged-user Captrap configuration file.

=back

=head1 EXAMPLES

=over

=item Print out statements:

captrap_mkdb -print

=item Execute the statements:

captrap_mkdb -print | mysql

=item Use this if you need to enter your MySQL password manually:

captrap_mkdb -print | mysql -u root -p

=back

=head1 AUTHOR

Corey Hickey <bugfood-c@fatooh.org>

This program is free software; you may redistribute and/or modify it under the
terms of the GNU General Public License, version 3. See the source file for the
usual GPL preamble and the COPYING file for a copy of the GPL.

=head1 SEE ALSO

captrap_mkconfig, captrap_mac, mysql

The documentation included with the Captrap source code has more information on
setup and general usage.

=cut


use strict;
use warnings FATAL => 'all';

use DBI;

# for development using a different Captrap module
use lib "lib";
use Captrap qw(:misc :actions :config :db);


# -----------------------------------------------------------------------------
# printing
# -----------------------------------------------------------------------------

# print main help info
sub usage {
  my $common = shift; # unused
  my $actions = mk_actions();
  my $actions_text = describe_actions($actions);
  print "
This is a script for printing MySQL statements that create a pmacct database
and tables suitable for Captrap. For full usage information, see the man page
and/or documentation provided in the Captrap source archive.

captrap_mkdb [ACTION] [[ACTION-PARAMETERS]] ...

ACTIONS

$actions_text
"
}


# make a list of statements to execute
sub mk_statements {
  my $common = shift;
  my $dbh = $common->{dbh};
  my $config = $common->{config};
  my $config_priv = parse_config_priv($config, 1);
  my ($priv_user, $priv_pass) =
      @{$config_priv}{'db_user_priv', 'db_password_priv'};
  my $states = $config->{states};
  $states = [ map { quote_sql($dbh, $_) } @$states ]; # quote them
  $states = join(", ", @$states); # collapse to scalar, joined with commas
  # some things should not be quoted, so they just get string-interpolated
  return [
    {
      stmt => "CREATE DATABASE IF NOT EXISTS $config->{db_database}",
      args => [],
    },
    {
      stmt => "USE $config->{db_database}",
      args => [ ],
    },
    {
      stmt =>
"# make the main pmacct table
CREATE TABLE IF NOT EXISTS $config->{db_table_acct} (
  mac_dst CHAR(17) NOT NULL,
  packets INT UNSIGNED NOT NULL,
  bytes BIGINT UNSIGNED NOT NULL,
  stamp_inserted DATETIME NOT NULL,
  stamp_updated DATETIME,
  PRIMARY KEY (mac_dst, stamp_inserted)
)",
      args => [ ],
    },
    {
      stmt =>
"# make the MAC address table
CREATE TABLE IF NOT EXISTS $config->{db_table_macs} (
  mac CHAR(17) UNIQUE KEY NOT NULL,
  state ENUM($states) NOT NULL,
  comment TEXT(40)
)",
      args => [],
    },
    {
      stmt =>
"# create a read-only Captrap user
GRANT
  SELECT
  ON $config->{db_database}.*
  TO ?@?
  IDENTIFIED BY ?",
      args => [
        $config->{db_user},
        $config->{captrap_host},
        $config->{db_password},
      ],
    },
    {
      stmt =>
"# create a read/write pmacctd user
GRANT
  SELECT, INSERT, UPDATE, DELETE, LOCK TABLES
  ON $config->{db_database}.*
  TO ?@?
  IDENTIFIED BY ?",
      args => [
        $priv_user,
        $config->{captrap_host},
        $priv_pass
      ],
    },
  ];
}


# print out statements for making a database
sub mkdb {
  my $common = shift;
  my $dbh = $common->{dbh};
  my $config = $common->{config};
  my $states = $config->{states};
  $states = [ map { "'$_'" } @$states ]; # add single-quotes
  $states = join(", ", @$states); # collapse to scalar, joined with commas
  foreach my $stmt (@{mk_statements($common)}) {
    print quote_sql_stmt($dbh, $stmt->{stmt}, $stmt->{args}), ";\n\n";
  }
}

# -----------------------------------------------------------------------------
# actions info
# -----------------------------------------------------------------------------

# return a hash of action info
sub mk_actions {
  my $actions = mk_ixhash();
  %$actions = (
    "-help" => {
      func => \&usage,
      args => [],
      desc => "
          Print this usage information.
      ",
    },
    "-print" => {
      func => \&mkdb,
      args => [],
      desc => "
          Print MySQL statements.
      ",
    },
  );
  return $actions;
}


# -----------------------------------------------------------------------------


# parse the arguments and take actions
if (! @ARGV) {
  usage();
  exit(1);
}
my $actions = mk_actions();
check_args(\@ARGV, $actions);

my $config = parse_config();
my $common = {
  config => $config,
};

do_args($common, \@ARGV, $actions);

exit(0);
