#! /usr/bin/perl

#*********************************************************************
#
# dhcp-edit -- managing dhcpd entries made easy
#
# This script is part of FAI (Fully Automatic Installation)
# Copyright (C) 2010-2014 Thomas Lange, lange@informatik.uni-koeln.de
# Universitaet zu Koeln
#
#*********************************************************************
# This program 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 2 of the License, or
# (at your option) any later version.
#*********************************************************************

# TODO
# -q quiet: do not print error if host/mac entry not found, exit code 0

my $dhcpdconf="/etc/dhcp/dhcpd.conf";

my $modified=0; # 1 if dhcpd.conf was modified
my @dhcpd;
our ($opt_p,$opt_d,$opt_h,$opt_n,$opt_r);

use strict;
use Pod::Usage;
use Getopt::Std;

getopts('p:dhnr') || pod2usage(-msg => "edit-dhcp", -verbose => 2);
$opt_h && pod2usage(-msg => "edit-dhcp",-verbose => 1);
my ($hostname,$mac,$ip)= @ARGV;
$hostname || pod2usage(-msg => "edit-dhcp",-verbose => 1);
$ip && merror(4,"$ip is not a correct IP address") unless $ip =~ /^[.0-9]{7,15}$/i;

read_dhcpd_conf();

if ($opt_r) {

  $mac=$hostname;
  # create empty entry, remove entry

  # set flag if an entry was found. print warning if entry not found
  foreach (@dhcpd) {
    next if /^\s*#/;  # do not change comments
    do {$_="XXX ENTRY DELETED XXX\n";$modified++} if m/host\s+$hostname\b.+hardware\s+ethernet.+;/;
    do {$_="XXX ENTRY DELETED XXX\n";$modified++} if m/host\s+.+hardware\s+ethernet\s+$mac[\s+;]/i;
  }
  merror(6,"Entry $hostname can not be removed. Not found.\n") unless $modified;
  print "$modified entry/entries removed.\n" if $modified;

} else {

  $mac || merror(5,"Please specify hostname and MAC address.");
  merror(4,"$hostname is not a correct host name") unless $hostname =~ /^[.0-9a-z-]+$/i;
  merror(4,"$mac is not a correct MAC address") unless $mac =~ /^([0-9a-f]{1,2}(:|$)){6}$/i;
  # grep all lines if the entry already exists
  foreach (@dhcpd) {
    next if /^\s*#/;  # do not change comments
    merror(7,"$hostname already exists in dhcpd.conf") if m/host\s+$hostname\b/;
    merror(7,"MAC address $mac already exists in dhcpd.conf") if m/hardware\s+ethernet\s+$mac[\s+;]/i;
  }
}

# check if executed as root
merror(3,"Terminated. $0 can only be run as root.") unless ($< == 0);
add_entry($hostname,$ip) unless $opt_r;
write_dhcpd();
restart_dhcpd();

exit 0;
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub merror {

  my $error = shift;
  warn "$0 ERROR: @_\n";
  exit $error;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub read_dhcpd_conf {

  # read the whole dhcpd.conf
  open(DHCP,"$dhcpdconf") || die "Can't read $dhcpdconf. $!\n";
  @dhcpd = <DHCP>;
  close(DHCP);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub add_entry {

  my @new;

  my ($hostname,$ip) = @_;
  $ip=$hostname unless $ip;

  # if -p was not given
  unless (defined $opt_p) {
    $modified=1;
    push @dhcpd, "host $hostname {hardware ethernet $mac;fixed-address $ip;}\n";
    print "Entry added: host $hostname {hardware ethernet $mac;fixed-address $ip;}\n" if $modified;
    return;
  }

  # add new entry before line matching $opt_p
  # if $opt_p matches multiple times, also the new entry is added multiple times
  foreach (@dhcpd) {
    if ($_ =~ /$opt_p/o) {
      push @new, "host $hostname {hardware ethernet $mac;fixed-address $ip;}\n";
      print "Entry added: host $hostname {hardware ethernet $mac;fixed-address $ip;}\n";
      $modified=1;
    }
    push @new,$_;
  }
  @dhcpd = @new;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub write_dhcpd {

  if ($opt_d) {
    print "DRY RUN. Nothing changed.";
    return;
  }

  unless ($modified) {
    print "Nothing changed.";
    return;
  }

  @dhcpd = grep(!/^XXX ENTRY DELETED XXX\n$/, @dhcpd);
#  print @dhcpd;
  open(DHCP," >$dhcpdconf") || die "Can't write $dhcpdconf. $!\n";
  print DHCP @dhcpd;
  close(DHCP);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub restart_dhcpd {

  if ($opt_d) {
    print "DRY RUN. Did not restart dhcp daemon.\n";
  }
  if ($opt_n) {
    print "Did not restart dhcp daemon.\n";
  }
  unless ($modified) {
    print "No modifications.\n";
  }

  if ($opt_d || $opt_n || $modified==0) {
    print "Did not restart dhcp daemon.\n";
    return;
  }

  (-x "/etc/init.d/isc-dhcp-server") and print qx#/etc/init.d/isc-dhcp-server restart#
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__END__

=head1 NAME

dhcp-edit - add or and remove entries to/from dhcpd.conf

=head1 SYNOPSIS

dhcp-edit [OPTION] HOST MAC [IP]

=head1 DESCRIPTION

Add a new host entry to dhcpd.conf or remove an existing entry.
Additionally restart DHCP daemon.

=head1 OPTIONS

=over 8

=item B<-d>

Dry run. Do not change files.

=item B<-h>

Print help.

=item B<-n>

Do not restart DHCP daemon.

=item B<-p> PATTERN

Add new entry before line matching PATTERN

=item B<-r> HOST|MAC

Remove entry contain HOST or MAC address.

=back

=head1 EXAMPLES

dhcp-edit host mac

   Add entry using host and mac address using a fixed IP address. You
   have to define the IP address in /etc/hosts or similar service.


dhcp-edit host mac ip

   Add entry using host and mac address using the numerical IP address.


dhcp-edit -r hostname|mac

   Remove line containing this hostname or mac address.

=head1 COPYRIGHT

This program is Copyright (C) 2010-2014 by Thomas Lange <lange@informatik.uni-koeln.de>

=cut
