]> git.proxmox.com Git - pve-zsync.git/blobdiff - pve-zsync
fix 'use of unitialized value' warning for help command
[pve-zsync.git] / pve-zsync
index a8434de4292251c6ae11d49a7a51290a8cc8d7bf..2503117235bbc5bb3540ba69147aa33c77faf63d 100644 (file)
--- a/pve-zsync
+++ b/pve-zsync
 #!/usr/bin/perl
 
 #!/usr/bin/perl
 
-my $PROGNAME = "pve-zsync";
-my $CONFIG_PATH = '/var/lib/'.$PROGNAME.'/';
-my $CONFIG = "$PROGNAME.cfg";
-my $CRONJOBS = '/etc/cron.d/'.$PROGNAME;
-my $VMCONFIG = '/var/lib/'.$PROGNAME.'/';
-my $PATH = "/usr/sbin/";
-my $QEMU_CONF = '/etc/pve/local/qemu-server/';
-my $DEBUG = 0;
-
 use strict;
 use warnings;
 use Data::Dumper qw(Dumper);
 use Fcntl qw(:flock SEEK_END);
 use strict;
 use warnings;
 use Data::Dumper qw(Dumper);
 use Fcntl qw(:flock SEEK_END);
-use Getopt::Long;
-use Switch;
+use Getopt::Long qw(GetOptionsFromArray);
+use File::Copy qw(move);
+use File::Path qw(make_path);
+use JSON;
+use IO::File;
+use String::ShellQuote 'shell_quote';
+
+my $PROGNAME = "pve-zsync";
+my $CONFIG_PATH = "/var/lib/${PROGNAME}";
+my $STATE = "${CONFIG_PATH}/sync_state";
+my $CRONJOBS = "/etc/cron.d/$PROGNAME";
+my $PATH = "/usr/sbin";
+my $PVE_DIR = "/etc/pve/local";
+my $QEMU_CONF = "${PVE_DIR}/qemu-server";
+my $LXC_CONF = "${PVE_DIR}/lxc";
+my $LOCKFILE = "$CONFIG_PATH/${PROGNAME}.lock";
+my $PROG_PATH = "$PATH/${PROGNAME}";
+my $INTERVAL = 15;
+my $DEBUG = 0;
+
+my $IPV4OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
+my $IPV4RE = "(?:(?:$IPV4OCTET\\.){3}$IPV4OCTET)";
+my $IPV6H16 = "(?:[0-9a-fA-F]{1,4})";
+my $IPV6LS32 = "(?:(?:$IPV4RE|$IPV6H16:$IPV6H16))";
+
+my $IPV6RE = "(?:" .
+    "(?:(?:" .                             "(?:$IPV6H16:){6})$IPV6LS32)|" .
+    "(?:(?:" .                           "::(?:$IPV6H16:){5})$IPV6LS32)|" .
+    "(?:(?:(?:" .              "$IPV6H16)?::(?:$IPV6H16:){4})$IPV6LS32)|" .
+    "(?:(?:(?:(?:$IPV6H16:){0,1}$IPV6H16)?::(?:$IPV6H16:){3})$IPV6LS32)|" .
+    "(?:(?:(?:(?:$IPV6H16:){0,2}$IPV6H16)?::(?:$IPV6H16:){2})$IPV6LS32)|" .
+    "(?:(?:(?:(?:$IPV6H16:){0,3}$IPV6H16)?::(?:$IPV6H16:){1})$IPV6LS32)|" .
+    "(?:(?:(?:(?:$IPV6H16:){0,4}$IPV6H16)?::" .           ")$IPV6LS32)|" .
+    "(?:(?:(?:(?:$IPV6H16:){0,5}$IPV6H16)?::" .            ")$IPV6H16)|" .
+    "(?:(?:(?:(?:$IPV6H16:){0,6}$IPV6H16)?::" .                    ")))";
+
+my $HOSTv4RE0 = "(?:[\\w\\.\\-_]+|$IPV4RE)";       # hostname or ipv4 address
+my $HOSTv4RE1 = "(?:$HOSTv4RE0|\\[$HOSTv4RE0\\])"; # these may be in brackets, too
+my $HOSTRE = "(?:$HOSTv4RE1|\\[$IPV6RE\\])";       # ipv6 must always be in brackets
+# targets are either a VMID, or a 'host:zpool/path' with 'host:' being optional
+my $TARGETRE = qr!^(?:($HOSTRE):)?(\d+|(?:[\w\-_]+)(/.+)?)$!;
 
 check_bin ('cstream');
 check_bin ('zfs');
 check_bin ('ssh');
 check_bin ('scp');
 
 
 check_bin ('cstream');
 check_bin ('zfs');
 check_bin ('ssh');
 check_bin ('scp');
 
+$SIG{TERM} = $SIG{QUIT} = $SIG{PIPE} = $SIG{HUP} = $SIG{KILL} = $SIG{INT} =
+    sub {
+       die "Signal aborting sync\n";
+    };
+
 sub check_bin {
     my ($bin)  = @_;
 
     foreach my $p (split (/:/, $ENV{PATH})) {
 sub check_bin {
     my ($bin)  = @_;
 
     foreach my $p (split (/:/, $ENV{PATH})) {
-        my $fn = "$p/$bin";
-        if (-x $fn) {
-            return $fn;
+       my $fn = "$p/$bin";
+       if (-x $fn) {
+           return $fn;
        }
     }
 
        }
     }
 
-    warn "unable to find command '$bin'\n";
+    die "unable to find command '$bin'\n";
 }
 
 }
 
-sub cut_to_width {
-    my ($text, $max) = @_;
+sub cut_target_width {
+    my ($path, $maxlen) = @_;
+    $path =~ s@/+@/@g;
 
 
-    return  $text if (length($text) <= $max);
-    my @spl = split('/', $text);
+    return $path if length($path) <= $maxlen;
 
 
-    my $count = length($spl[@spl-1]);
-    return "..\/".substr($spl[@spl-1],($count-$max)+3 ,$count) if $count > $max;
+    return '..'.substr($path, -$maxlen+2) if $path !~ m@/@;
 
 
-    $count +=  length($spl[0]) if @spl > 1;
-    return substr($spl[0], 0, $max-4-length($spl[@spl-1]))."\/..\/".$spl[@spl-1] if $count > $max;
+    $path =~ s@/([^/]+/?)$@@;
+    my $tail = $1;
+
+    if (length($tail)+3 == $maxlen) {
+       return "../$tail";
+    } elsif (length($tail)+2 >= $maxlen) {
+       return '..'.substr($tail, -$maxlen+2)
+    }
 
 
-    my $rest = 1 ;
-    $rest = $max-$count if ($max-$count > 0);
+    $path =~ s@(/[^/]+)(?:/|$)@@;
+    my $head = $1;
+    my $both = length($head) + length($tail);
+    my $remaining = $maxlen-$both-4; # -4 for "/../"
 
 
-    return "$spl[0]".substr($text, length($spl[0]), $rest)."..\/".$spl[@spl-1];
+    if ($remaining < 0) {
+       return substr($head, 0, $maxlen - length($tail) - 3) . "../$tail"; # -3 for "../"
+    }
+
+    substr($path, ($remaining/2), (length($path)-$remaining), '..');
+    return "$head/" . $path . "/$tail";
 }
 
 sub lock {
     my ($fh) = @_;
 }
 
 sub lock {
     my ($fh) = @_;
-    flock($fh, LOCK_EX) or die "Cannot lock config - $!\n";
-
-    seek($fh, 0, SEEK_END) or die "Cannot seek - $!\n";
+    flock($fh, LOCK_EX) || die "Can't lock config - $!\n";
 }
 
 sub unlock {
     my ($fh) = @_;
 }
 
 sub unlock {
     my ($fh) = @_;
-    flock($fh, LOCK_UN) or die "Cannot unlock config- $!\n";
+    flock($fh, LOCK_UN) || die "Can't unlock config- $!\n";
 }
 
 }
 
-sub check_config {
-    my ($source, $name, $cfg) = @_;
+sub get_status {
+    my ($source, $name, $status) = @_;
 
 
-    my $id = $source->{vmid} ? $source->{vmid} : $source->{abs_path};
-    my $status = $cfg->{$id}->{$name}->{status};
-
-    if ($cfg->{$id}->{$name}->{status}){
+    if ($status->{$source->{all}}->{$name}->{status}) {
        return $status;
     }
 
     return undef;
 }
 
        return $status;
     }
 
     return undef;
 }
 
+sub check_pool_exists {
+    my ($target) = @_;
 
 
-sub check_pool_exsits {
-    my ($pool, $ip) = @_;
+    my $cmd = [];
 
 
-    my $cmd = '';
-    $cmd = "ssh root\@$ip " if $ip;
-    $cmd .= "zfs list $pool -H";
+    if ($target->{ip}) {
+       push @$cmd, 'ssh', "root\@$target->{ip}", '--';
+    }
+    push @$cmd, 'zfs', 'list', '-H', '--', $target->{all};
     eval {
        run_cmd($cmd);
     };
 
     eval {
        run_cmd($cmd);
     };
 
-    if($@){
-       return 1;
+    if ($@) {
+       return 0;
     }
     }
-    return undef;
+    return 1;
 }
 
 }
 
-sub write_to_config {
-    my ($cfg) = @_;
+sub parse_target {
+    my ($text) = @_;
 
 
-    open(my $fh, ">", "$CONFIG_PATH$CONFIG")
-       or die "cannot open >$CONFIG_PATH$CONFIG: $!\n";
+    my $errstr = "$text : is not a valid input! Use [IP:]<VMID> or [IP:]<ZFSPool>[/Path]";
+    my $target = {};
 
 
-    my $text = decode_config($cfg);
+    if ($text !~ $TARGETRE) {
+       die "$errstr\n";
+    }
+    $target->{all} = $2;
+    $target->{ip} = $1 if $1;
+    my @parts = split('/', $2);
 
 
-    print($fh $text);
+    $target->{ip} =~ s/^\[(.*)\]$/$1/ if $target->{ip};
 
 
-    close($fh);
+    my $pool = $target->{pool} = shift(@parts);
+    die "$errstr\n" if !$pool;
+
+    if ($pool =~ m/^\d+$/) {
+       $target->{vmid} = $pool;
+       delete $target->{pool};
+    }
+
+    return $target if (@parts == 0);
+    $target->{last_part} = pop(@parts);
+
+    if ($target->{ip}) {
+       pop(@parts);
+    }
+    if (@parts > 0) {
+       $target->{path} = join('/', @parts);
+    }
+
+    return $target;
 }
 
 }
 
-sub read_from_config {
+sub read_cron {
 
 
-    unless(-e "$CONFIG_PATH$CONFIG") {
+    #This is for the first use to init file;
+    if (!-e $CRONJOBS) {
+       my $new_fh = IO::File->new("> $CRONJOBS");
+       die "Could not create $CRONJOBS: $!\n" if !$new_fh;
+       close($new_fh);
        return undef;
     }
 
        return undef;
     }
 
-    open(my $fh, "<", "$CONFIG_PATH$CONFIG")
-       or die "cannot open > $CONFIG_PATH$CONFIG: $!\n";
+    my $fh = IO::File->new("< $CRONJOBS");
+    die "Could not open file $CRONJOBS: $!\n" if !$fh;
 
 
-    $/ = undef;
-
-    my $text = <$fh>;
+    my @text = <$fh>;
 
     close($fh);
 
 
     close($fh);
 
-    my $cfg = encode_config($text);
+    return encode_cron(@text);
+}
 
 
-    return $cfg;
+sub parse_argv {
+    my (@arg) = @_;
+
+    my $param = {};
+    $param->{dest} = undef;
+    $param->{source} = undef;
+    $param->{verbose} = undef;
+    $param->{limit} = undef;
+    $param->{maxsnap} = undef;
+    $param->{name} = undef;
+    $param->{skip} = undef;
+    $param->{method} = undef;
+
+    my ($ret, $ar) = GetOptionsFromArray(\@arg,
+                                        'dest=s' => \$param->{dest},
+                                        'source=s' => \$param->{source},
+                                        'verbose' => \$param->{verbose},
+                                        'limit=i' => \$param->{limit},
+                                        'maxsnap=i' => \$param->{maxsnap},
+                                        'name=s' => \$param->{name},
+                                        'skip' => \$param->{skip},
+                                        'method=s' => \$param->{method});
+
+    if ($ret == 0) {
+       die "can't parse options\n";
+    }
+
+    $param->{name} = "default" if !$param->{name};
+    $param->{maxsnap} = 1 if !$param->{maxsnap};
+    $param->{method} = "ssh" if !$param->{method};
+
+    return $param;
 }
 
 }
 
-sub decode_config {
-    my ($cfg) = @_;
-    my $raw = '';
+sub add_state_to_job {
+    my ($job) = @_;
 
 
-    foreach my $source (sort keys%{$cfg}){
-       foreach my $sync_name (sort keys%{$cfg->{$source}}){
-           $raw .= "$source: $sync_name\n";
-               foreach my $parameter (sort keys%{$cfg->{$source}->{$sync_name}}){
-                   $raw .= "\t$parameter: $cfg->{$source}->{$sync_name}->{$parameter}\n";
-               }
-       }
+    my $states = read_state();
+    my $state = $states->{$job->{source}}->{$job->{name}};
+
+    $job->{state} = $state->{state};
+    $job->{lsync} = $state->{lsync};
+    $job->{vm_type} = $state->{vm_type};
+
+    for (my $i = 0; $state->{"snap$i"}; $i++) {
+       $job->{"snap$i"} = $state->{"snap$i"};
     }
     }
-    return $raw;
+
+    return $job;
 }
 
 }
 
-sub encode_config {
-    my ($raw) = @_;
+sub encode_cron {
+    my (@text) = @_;
+
     my $cfg = {};
     my $cfg = {};
-    my $source;
-    my $check = 0;
-    my $sync_name;
-    while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
-       my $line = $1;
 
 
-       next if $line =~ m/^\#/;
-       next if $line =~ m/^\s*$/;
-
-       if ($line =~ m/^(\t| )(\w+): (.+)/){
-           my $par = $2;
-           my $value = $3;
-
-           if ($par eq 'source_pool') {
-               $cfg->{$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: SourcePool value doubled\n" if ($check & 1);
-               $check += 1;
-           } elsif ($par eq 'source_ip') {
-               $cfg->{$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: SourceIP value doubled\n" if ($check & 2);
-               $check += 2;
-           } elsif ($par eq 'status') {
-               $cfg->{$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: Status value doubled\n" if ($check & 4);
-               $check += 4;
-           } elsif ($par eq 'method') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: Method value doubled\n" if ($check & 8);
-               $check += 8;
-           } elsif ($par eq 'interval') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: Iterval value doubled\n" if ($check & 16);
-               $check += 16;
-           } elsif ($par eq 'limit') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: Limit value doubled\n" if ($check & 32);
-               $check += 32;
-           } elsif ($par eq 'dest_pool') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: DestPool value doubled\n" if ($check & 64);
-               $check += 64;
-           } elsif ($par eq 'dest_ip') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: DestIp value doubled\n" if ($check & 128);
-               $check += 128;
-           } elsif ($par eq 'dest_path') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: DestPath value doubled\n" if ($check & 256);
-               $check += 256;
-           } elsif ($par eq 'source_path') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: SourcePath value doubled\n" if ($check & 512);
-               $check += 512;
-           } elsif ($par eq 'vmid') {
-               $cfg -> {$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: Vmid value doubled\n" if ($check & 1024);
-               $check += 1024;
-           } elsif ($par =~ 'lsync') {
-               $cfg->{$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: lsync value doubled\n" if ($check & 2048);
-               $check += 2048;
-           } elsif ($par =~ 'maxsnap') {
-               $cfg->{$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: maxsnap value doubled\n" if ($check & 4096);
-               $check += 4096;
-           } else {
-               die "error in Config\n";
-           }
-       } elsif ($line =~ m/^((\d+.\d+.\d+.\d+):)?([\w\-\_\/]+): (.+){0,1}/){
-           $source = $3;
-           $sync_name = $4 ? $4 : 'default' ;
-           $cfg->{$source}->{$sync_name} = undef;
-           $cfg->{$source}->{$sync_name}->{source_ip} = $2 if $2;
-           $check = 0;
+    while (my $line = shift(@text)) {
+
+       my @arg = split('\s', $line);
+       my $param = parse_argv(@arg);
+
+       if ($param->{source} && $param->{dest}) {
+           $cfg->{$param->{source}}->{$param->{name}}->{dest} = $param->{dest};
+           $cfg->{$param->{source}}->{$param->{name}}->{verbose} = $param->{verbose};
+           $cfg->{$param->{source}}->{$param->{name}}->{limit} = $param->{limit};
+           $cfg->{$param->{source}}->{$param->{name}}->{maxsnap} = $param->{maxsnap};
+           $cfg->{$param->{source}}->{$param->{name}}->{skip} = $param->{skip};
+           $cfg->{$param->{source}}->{$param->{name}}->{method} = $param->{method};
        }
     }
        }
     }
+
     return $cfg;
 }
 
     return $cfg;
 }
 
-sub parse_target {
-    my ($text) = @_;
+sub param_to_job {
+    my ($param) = @_;
+
+    my $job = {};
+
+    my $source = parse_target($param->{source});
+    my $dest = parse_target($param->{dest}) if $param->{dest};
+
+    $job->{name} = !$param->{name} ? "default" : $param->{name};
+    $job->{dest} = $param->{dest} if $param->{dest};
+    $job->{method} = "local" if !$dest->{ip} && !$source->{ip};
+    $job->{method} = "ssh" if !$job->{method};
+    $job->{limit} = $param->{limit};
+    $job->{maxsnap} = $param->{maxsnap} if $param->{maxsnap};
+    $job->{source} = $param->{source};
+
+    return $job;
+}
+
+sub read_state {
+
+    if (!-e $STATE) {
+       make_path $CONFIG_PATH;
+       my $new_fh = IO::File->new("> $STATE");
+       die "Could not create $STATE: $!\n" if !$new_fh;
+       print $new_fh "{}";
+       close($new_fh);
+       return undef;
+    }
+
+    my $fh = IO::File->new("< $STATE");
+    die "Could not open file $STATE: $!\n" if !$fh;
+
+    my $text = <$fh>;
+    my $states = decode_json($text);
 
 
-    if ($text =~ m/^((\d+.\d+.\d+.\d+):)?((\w+)\/?)([\w\/\-\_]*)?$/) {
+    close($fh);
 
 
-       die "Input not valid\n" if !$3;
-       my $tmp = $3;
-       my $target = {};
+    return $states;
+}
+
+sub update_state {
+    my ($job) = @_;
+    my $text;
+    my $in_fh;
+
+    eval {
+
+       $in_fh = IO::File->new("< $STATE");
+       die "Could not open file $STATE: $!\n" if !$in_fh;
+       lock($in_fh);
+       $text = <$in_fh>;
+    };
 
 
-       if ($2) {
-           $target->{ip} = $2 ;
+    my $out_fh = IO::File->new("> $STATE.new");
+    die "Could not open file ${STATE}.new: $!\n" if !$out_fh;
+
+    my $states = {};
+    my $state = {};
+    if ($text){
+       $states = decode_json($text);
+       $state = $states->{$job->{source}}->{$job->{name}};
+    }
+
+    if ($job->{state} ne "del") {
+       $state->{state} = $job->{state};
+       $state->{lsync} = $job->{lsync};
+       $state->{vm_type} = $job->{vm_type};
+
+       for (my $i = 0; $job->{"snap$i"} ; $i++) {
+           $state->{"snap$i"} = $job->{"snap$i"};
        }
        }
+       $states->{$job->{source}}->{$job->{name}} = $state;
+    } else {
 
 
-       if ($tmp =~ m/^(\d\d\d+)$/){
-           $target->{vmid} = $tmp;
+       delete $states->{$job->{source}}->{$job->{name}};
+       delete $states->{$job->{source}} if !keys %{$states->{$job->{source}}};
+    }
+
+    $text = encode_json($states);
+    print $out_fh $text;
+
+    close($out_fh);
+    move("$STATE.new", $STATE);
+    eval {
+       close($in_fh);
+    };
+
+    return $states;
+}
+
+sub update_cron {
+    my ($job) = @_;
+
+    my $updated;
+    my $has_header;
+    my $line_no = 0;
+    my $text = "";
+    my $header = "SHELL=/bin/sh\n";
+    $header .= "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n\n";
+
+    my $fh = IO::File->new("< $CRONJOBS");
+    die "Could not open file $CRONJOBS: $!\n" if !$fh;
+    lock($fh);
+
+    my @test = <$fh>;
+
+    while (my $line = shift(@test)) {
+       chomp($line);
+       if ($line =~ m/source $job->{source} .*name $job->{name} /) {
+           $updated = 1;
+           next if $job->{state} eq "del";
+           $text .= format_job($job, $line);
        } else {
        } else {
-           $target->{pool} = $4;
-           my $abs_path = $4;
-           if ($5) {
-               $target->{path} = "\/$5";
-               $abs_path .= "\/$5";
+           if (($line_no < 3) && ($line =~ /^(PATH|SHELL)/ )) {
+               $has_header = 1;
            }
            }
-           $target->{abs_path} = $abs_path;
+           $text .= "$line\n";
        }
        }
+       $line_no++;
+    }
+
+    if (!$has_header) {
+       $text = "$header$text";
+    }
 
 
-       return $target;
+    if (!$updated) {
+       $text .= format_job($job);
     }
     }
-    die "Input not valid\n";
+    my $new_fh = IO::File->new("> ${CRONJOBS}.new");
+    die "Could not open file ${CRONJOBS}.new: $!\n" if !$new_fh;
+
+    die "can't write to $CRONJOBS.new\n" if !print($new_fh $text);
+    close ($new_fh);
+
+    die "can't move $CRONJOBS.new: $!\n" if !move("${CRONJOBS}.new", "$CRONJOBS");
+    close ($fh);
 }
 
 }
 
-sub list {
+sub format_job {
+    my ($job, $line) = @_;
+    my $text = "";
 
 
-    my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
+    if ($job->{state} eq "stopped") {
+       $text = "#";
+    }
+    if ($line) {
+       $line =~ /^#*(.+) root/;
+       $text .= $1;
+    } else {
+       $text .= "*/$INTERVAL * * * *";
+    }
+    $text .= " root";
+    $text .= " $PROGNAME sync --source $job->{source} --dest $job->{dest}";
+    $text .= " --name $job->{name} --maxsnap $job->{maxsnap}";
+    $text .= " --limit $job->{limit}" if $job->{limit};
+    $text .= " --method $job->{method}";
+    $text .= " --verbose" if $job->{verbose};
+    $text .= "\n";
+
+    return $text;
+}
 
 
-    my $list = sprintf("%-25s%-15s%-7s%-20s%-10s%-5s\n" , "SOURCE", "NAME", "ACTIVE", "LAST SYNC", "INTERVAL", "TYPE");
+sub list {
 
 
-    foreach my $source (sort keys%{$cfg}){
-       foreach my $sync_name (sort keys%{$cfg->{$source}}){
-           my $source_name = $source;
-           $source_name = $cfg->{$source}->{$sync_name}->{source_ip}.":".$source if $cfg->{$source}->{$sync_name}->{source_ip};
-           $list .= sprintf("%-25s%-15s", cut_to_width($source_name,25), cut_to_width($sync_name,15));
+    my $cfg = read_cron();
 
 
-           my $active = "";
-           if($cfg->{$source}->{$sync_name}->{status} eq 'syncing'){
-               $active = "yes";
-           } else {
-               $active = "no";
-           }
+    my $list = sprintf("%-25s%-25s%-10s%-20s%-6s%-5s\n" , "SOURCE", "NAME", "STATE", "LAST SYNC", "TYPE", "CON");
 
 
-           $list .= sprintf("%-7s", $active);
-           $list .= sprintf("%-20s",$cfg->{$source}->{$sync_name}->{lsync});
-           $list .= sprintf("%-10s",$cfg->{$source}->{$sync_name}->{interval});
-           $list .= sprintf("%-5s\n",$cfg->{$source}->{$sync_name}->{method});
+    my $states = read_state();
+    foreach my $source (sort keys%{$cfg}) {
+       foreach my $name (sort keys%{$cfg->{$source}}) {
+           $list .= sprintf("%-25s", cut_target_width($source, 25));
+           $list .= sprintf("%-25s", cut_target_width($name, 25));
+           $list .= sprintf("%-10s", $states->{$source}->{$name}->{state});
+           $list .= sprintf("%-20s", $states->{$source}->{$name}->{lsync});
+           $list .= sprintf("%-6s", defined($states->{$source}->{$name}->{vm_type}) ? $states->{$source}->{$name}->{vm_type} : "undef");
+           $list .= sprintf("%-5s\n", $cfg->{$source}->{$name}->{method});
        }
     }
 
        }
     }
 
@@ -286,287 +455,291 @@ sub list {
 sub vm_exists {
     my ($target) = @_;
 
 sub vm_exists {
     my ($target) = @_;
 
-    my $cmd = "";
-    $cmd = "ssh root\@$target->{ip} " if ($target->{ip});
-    $cmd .= "qm status $target->{vmid}";
+    my @cmd = ('ssh', "root\@$target->{ip}", '--') if $target->{ip};
 
 
-    my $res = run_cmd($cmd);
+    my $res = undef;
+
+    return undef if !defined($target->{vmid});
+
+    eval { $res = run_cmd([@cmd, 'ls',  "$QEMU_CONF/$target->{vmid}.conf"]) };
+
+    return "qemu" if $res;
+
+    eval { $res = run_cmd([@cmd, 'ls',  "$LXC_CONF/$target->{vmid}.conf"]) };
+
+    return "lxc" if $res;
 
 
-    return 1 if ($res =~ m/^status.*$/);
     return undef;
 }
 
 sub init {
     my ($param) = @_;
 
     return undef;
 }
 
 sub init {
     my ($param) = @_;
 
-    my $cfg = read_from_config;
+    my $cfg = read_cron();
 
 
-    my $vm = {};
+    my $job = param_to_job($param);
 
 
-    my $name =  $param->{name} ? $param->{name} : "default";
-    my $interval = $param->{interval} ? $param->{interval} : 15;
+    $job->{state} = "ok";
+    $job->{lsync} = 0;
 
     my $source = parse_target($param->{source});
     my $dest = parse_target($param->{dest});
 
 
     my $source = parse_target($param->{source});
     my $dest = parse_target($param->{dest});
 
-    $vm->{$name}->{dest_pool} = $dest->{pool};
-    $vm->{$name}->{dest_ip} = $dest->{ip} if $dest->{ip};
-    $vm->{$name}->{dest_path} = $dest->{path} if $dest->{path};
-
-    $param->{method} = "local" if !$dest->{ip} && !$source->{ip};
-    $vm->{$name}->{status} = "ok";
-    $vm->{$name}->{interval} = $interval;
-    $vm->{$name}->{method} = $param->{method} ? $param->{method} : "ssh";
-    $vm->{$name}->{limit} = $param->{limit} if $param->{limit};
-    $vm->{$name}->{maxsnap} = $param->{maxsnap} if $param->{maxsnap};
-
-    if ( my $ip =  $vm->{$name}->{dest_ip} ) {
-       run_cmd("ssh-copy-id -i /root/.ssh/id_rsa.pub root\@$ip");
+    if (my $ip =  $dest->{ip}) {
+       run_cmd(['ssh-copy-id', '-i', '/root/.ssh/id_rsa.pub', "root\@$ip"]);
     }
 
     }
 
-    if ( my $ip =  $source->{ip} ) {
-       run_cmd("ssh-copy-id -i /root/.ssh/id_rsa.pub root\@$ip");
+    if (my $ip =  $source->{ip}) {
+       run_cmd(['ssh-copy-id', '-i', '/root/.ssh/id_rsa.pub', "root\@$ip"]);
     }
 
     }
 
-    die "Pool $dest->{abs_path} does not exists\n" if check_pool_exsits($dest->{abs_path}, $dest->{ip});
+    die "Pool $dest->{all} does not exists\n" if !check_pool_exists($dest);
 
 
-    my $check = check_pool_exsits($source->{abs_path}, $source->{ip}) if !$source->{vmid} && $source->{abs_path};
+    if (!defined($source->{vmid})) {
+       die "Pool $source->{all} does not exists\n" if !check_pool_exists($source);
+    }
 
 
-    die "Pool $source->{abs_path} does not exists\n" if undef($check);
+    my $vm_type = vm_exists($source);
+    $job->{vm_type} = $vm_type;
+    $source->{vm_type} = $vm_type;
 
 
-    my $add_job = sub {
-       my ($vm, $name) = @_;
-       my $source = "";
-       if ($vm->{$name}->{vmid}) {
-           $source .= $vm->{$name}->{vmid};
-       } else {
-           $source .= $vm->{$name}->{source_pool};
-           $source .= $vm->{$name}->{source_path} if $vm->{$name}->{source_path};
-       }
-       die "Config already exists\n" if $cfg->{$source}->{$name};
+    die "VM $source->{vmid} doesn't exist\n" if $source->{vmid} && !$vm_type;
 
 
-       $cfg->{$source}->{$name} = $vm->{$name};
+    die "Config already exists\n" if $cfg->{$job->{source}}->{$job->{name}};
 
 
-       write_to_cron($cfg);
+    #check if vm has zfs disks if not die;
+    get_disks($source) if $source->{vmid};
 
 
-       write_to_config($cfg);
-    };
+    update_cron($job);
+    update_state($job);
 
 
-    if ($source->{vmid}) {
-       die "VM $source->{vmid} doesn't exist\n" if !vm_exists($source);
-       my $disks = get_disks($source);
-       $vm->{$name}->{vmid} = $source->{vmid};
-       $vm->{$name}->{lsync} = 0;
-       $vm->{$name}->{source_ip} = $source->{ip} if $source->{ip};
+    eval {
+       sync($param) if !$param->{skip};
+    };
+    if(my $err = $@) {
+       destroy_job($param);
+       print $err;
+    }
+}
 
 
-       &$add_job($vm, $name);
+sub get_job {
+    my ($param) = @_;
 
 
-    } else {
-       $vm->{$name}->{source_pool} = $source->{pool};
-        $vm->{$name}->{source_ip} = $source->{ip} if $source->{ip};
-       $vm->{$name}->{source_path} = $source->{path} if $source->{path};
-       $vm->{$name}->{lsync} = 0;
+    my $cfg = read_cron();
 
 
-       &$add_job($vm, $name);
+    if (!$cfg->{$param->{source}}->{$param->{name}}) {
+       die "Job  with source $param->{source} and name $param->{name} does not exist\n" ;
     }
     }
+    my $job = $cfg->{$param->{source}}->{$param->{name}};
+    $job->{name} = $param->{name};
+    $job->{source} = $param->{source};
+    $job = add_state_to_job($job);
 
 
-    eval {sync($param) if !$param->{skip};};
-    if(my $err = $@) {
-       destroy($param);
-       print $err;
-    }
+    return $job;
 }
 
 }
 
-sub destroy {
+sub destroy_job {
     my ($param) = @_;
 
     my ($param) = @_;
 
-    modify_configs($param->{name}, $param->{source},1);
+    my $job = get_job($param);
+    $job->{state} = "del";
+
+    update_cron($job);
+    update_state($job);
 }
 
 sub sync {
     my ($param) = @_;
 
 }
 
 sub sync {
     my ($param) = @_;
 
-    my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
+    my $lock_fh = IO::File->new("> $LOCKFILE");
+    die "Can't open Lock File: $LOCKFILE $!\n" if !$lock_fh;
+    lock($lock_fh);
 
 
-    my $name =  $param->{name} ? $param->{name} : "default";
-    my $max_snap = $param->{maxsnap} ? $param->{maxsnap} : 1;
-    my $method = $param->{method} ? $param->{method} : "ssh";
+    my $date = get_date();
+    my $job;
+    eval {
+       $job = get_job($param);
+    };
+
+    if ($job && $job->{state} eq "syncing") {
+       die "Job --source $param->{source} --name $param->{name} is syncing at the moment";
+    }
 
     my $dest = parse_target($param->{dest});
     my $source = parse_target($param->{source});
 
     my $sync_path = sub {
 
     my $dest = parse_target($param->{dest});
     my $source = parse_target($param->{source});
 
     my $sync_path = sub {
-       my ($source, $name, $cfg, $max_snap, $dest, $method) = @_;
+       my ($source, $dest, $job, $param, $date) = @_;
 
 
-       ($source->{old_snap},$source->{last_snap}) = snapshot_get($source, $dest, $max_snap, $name);
+       ($source->{old_snap}, $source->{last_snap}) = snapshot_get($source, $dest, $param->{maxsnap}, $param->{name});
 
 
-       my $job_status = check_config($source, $name, $cfg) if $cfg;
-       die "VM Status: $job_status syncing will not done!\n" if ($job_status && !($job_status eq "ok" || $job_status eq "stoped"));
-
-       if ($job_status) {
-           my $conf_name = $source->{abs_path};
-           $conf_name = $source->{vmid} if $source->{vmid};
-           $cfg->{$conf_name}->{$name}->{status} = "syncing";
-           write_to_config($cfg);
-       }
+       snapshot_add($source, $dest, $param->{name}, $date);
 
 
-       my $date = undef;
+       send_image($source, $dest, $param);
 
 
-       eval{
-           $date = snapshot_add($source, $dest, $name);
+       snapshot_destroy($source, $dest, $param->{method}, $source->{old_snap}) if ($source->{destroy} && $source->{old_snap});
 
 
-           send_image($source, $dest, $method, $param->{verbose}, $param->{limit});
-
-           snapshot_destroy($source, $dest, $method, $source->{old_snap}) if ($source->{destroy} && $source->{old_snap});
-       };
-       if(my $err = $@){
-           if ($job_status){
-               my $conf_name = $source->{abs_path};
-               $conf_name = $source->{vmid} if $source->{vmid};
-               $cfg->{$conf_name}->{$name}->{status} = "error";
-               write_to_config($cfg);
-
-               my $source_target = $source->{ip} ? $source->{ip}.":" : '';
-               $source_target .= $source->{vmid} ? $source->{vmid} : $source->{abs_path};
-               write_to_cron($cfg);
-           }
-           die "$err\n";
-       }
-
-       if ($job_status) {
-           my $conf_name = $source->{abs_path};
-           $conf_name = $source->{vmid} if $source->{vmid};
-           $cfg->{$conf_name}->{$name}->{status} = "ok";
-           $cfg->{$conf_name}->{$name}->{lsync} = $date;
-
-           write_to_config($cfg);
-       }
     };
 
     };
 
-    $param->{method}  = "ssh" if  !$param->{method};
-
-    if ($source->{vmid}) {
-        die "VM $source->{vmid} doesn't exist\n" if !vm_exists($source);
-       my $disks = get_disks($source);
+    my $vm_type = vm_exists($source);
+    $source->{vm_type} = $vm_type;
 
 
-        foreach my $disk (sort keys %{$disks}) {
-           $source->{abs_path} = $disks->{$disk}->{pool};
-           $source->{abs_path} .= "\/$disks->{$disk}->{path}" if $disks->{$disk}->{path};
-
-           $source->{pool} = $disks->{$disk}->{pool};
-           $source->{path} = "\/$disks->{$disk}->{path}";
+    if ($job) {
+       $job->{state} = "syncing";
+       $job->{vm_type} = $vm_type if !$job->{vm_type};
+       update_state($job);
+    }
 
 
-           &$sync_path($source, $name, $cfg, $max_snap, $dest, $method);
+    eval{
+       if ($source->{vmid}) {
+           die "VM $source->{vmid} doesn't exist\n" if !$vm_type;
+           my $disks = get_disks($source);
+
+           foreach my $disk (sort keys %{$disks}) {
+               $source->{all} = $disks->{$disk}->{all};
+               $source->{pool} = $disks->{$disk}->{pool};
+               $source->{path} = $disks->{$disk}->{path} if $disks->{$disk}->{path};
+               $source->{last_part} = $disks->{$disk}->{last_part};
+               &$sync_path($source, $dest, $job, $param, $date);
+           }
+           if ($param->{method} eq "ssh" && ($source->{ip} || $dest->{ip})) {
+               send_config($source, $dest,'ssh');
+           } else {
+               send_config($source, $dest,'local');
+           }
+       } else {
+           &$sync_path($source, $dest, $job, $param, $date);
        }
        }
-       if ($method eq "ssh") {
-           send_config($source, $dest,'ssh');
+    };
+    if(my $err = $@) {
+       if ($job) {
+           $job->{state} = "error";
+           update_state($job);
+           unlock($lock_fh);
+           close($lock_fh);
+           print "Job --source $param->{source} --name $param->{name} got an ERROR!!!\nERROR Message:\n";
        }
        }
-    } else {
-       &$sync_path($source, $name, $cfg, $max_snap, $dest, $method);
+       die "$err\n";
+    }
+
+    if ($job) {
+       $job->{state} = "ok";
+       $job->{lsync} = $date;
+       update_state($job);
     }
     }
+
+    unlock($lock_fh);
+    close($lock_fh);
 }
 
 sub snapshot_get{
     my ($source, $dest, $max_snap, $name) = @_;
 
 }
 
 sub snapshot_get{
     my ($source, $dest, $max_snap, $name) = @_;
 
-    my $cmd = "zfs list -r -t snapshot -Ho name, -S creation ";
-
-    $cmd .= $source->{abs_path};
-    $cmd = "ssh root\@$source->{ip} ".$cmd if $source->{ip};
+    my $cmd = [];
+    push @$cmd, 'ssh', "root\@$source->{ip}", '--', if $source->{ip};
+    push @$cmd, 'zfs', 'list', '-r', '-t', 'snapshot', '-Ho', 'name', '-S', 'creation';
+    push @$cmd, $source->{all};
 
     my $raw = run_cmd($cmd);
 
     my $raw = run_cmd($cmd);
-    my $index = 1;
+    my $index = 0;
     my $line = "";
     my $last_snap = undef;
     my $line = "";
     my $last_snap = undef;
+    my $old_snap;
 
     while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
        $line = $1;
 
     while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
        $line = $1;
-       $last_snap = $line if $index == 1;
-       if ($index == $max_snap) {
-           $source->{destroy} = 1;
-           last;
-       };
-       $index++;
+       if ($line =~ m/(rep_\Q${name}\E_\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2})$/) {
+
+           $last_snap = $1 if (!$last_snap);
+           $old_snap = $1;
+           $index++;
+           if ($index == $max_snap) {
+               $source->{destroy} = 1;
+               last;
+           };
+       }
     }
 
     }
 
-    $line =~ m/^(.+)\@(rep_$name\_.+)(\n|$)/;
-    return ($2, $last_snap) if $2;
+    return ($old_snap, $last_snap) if $last_snap;
 
     return undef;
 }
 
 sub snapshot_add {
 
     return undef;
 }
 
 sub snapshot_add {
-    my ($source, $dest, $name) = @_;
-
-    my $date = get_date();
+    my ($source, $dest, $name, $date) = @_;
 
     my $snap_name = "rep_$name\_".$date;
 
     $source->{new_snap} = $snap_name;
 
 
     my $snap_name = "rep_$name\_".$date;
 
     $source->{new_snap} = $snap_name;
 
-    my $path = $source->{abs_path}."\@".$snap_name;
-
-    my $cmd = "zfs snapshot $path";
-    $cmd = "ssh root\@$source->{ip}  ".$cmd if $source->{ip};
+    my $path = "$source->{all}\@$snap_name";
 
 
+    my $cmd = [];
+    push @$cmd, 'ssh', "root\@$source->{ip}", '--', if $source->{ip};
+    push @$cmd, 'zfs', 'snapshot', $path;
     eval{
        run_cmd($cmd);
     };
 
     eval{
        run_cmd($cmd);
     };
 
-    if (my $err = $@){
+    if (my $err = $@) {
        snapshot_destroy($source, $dest, 'ssh', $snap_name);
        die "$err\n";
     }
        snapshot_destroy($source, $dest, 'ssh', $snap_name);
        die "$err\n";
     }
-    return $date;
 }
 
 }
 
-sub write_to_cron {
+sub write_cron {
     my ($cfg) = @_;
 
     my ($cfg) = @_;
 
-    my $text = 'SHELL=/bin/sh'."\n";
-    $text .= 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin'."\n";
+    my $text = "SHELL=/bin/sh\n";
+    $text .= "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n";
 
 
-    open(my $fh, '>', "$CRONJOBS")
-       or die "Could not open file: $!\n";
+    my $fh = IO::File->new("> $CRONJOBS");
+    die "Could not open file: $!\n" if !$fh;
 
 
-    foreach my $source (sort keys%{$cfg}){
-       foreach my $sync_name (sort keys%{$cfg->{$source}}){
+    foreach my $source (sort keys%{$cfg}) {
+       foreach my $sync_name (sort keys%{$cfg->{$source}}) {
            next if $cfg->{$source}->{$sync_name}->{status} ne 'ok';
            next if $cfg->{$source}->{$sync_name}->{status} ne 'ok';
-       $text .= "*/$cfg->{$source}->{$sync_name}->{interval} * * * * root ";
-       $text .= "$PATH$PROGNAME sync";
-       $text .= " -source  ";
-       if ($cfg->{$source}->{$sync_name}->{vmid}) {
-           $text .= "$cfg->{$source}->{$sync_name}->{source_ip}:" if $cfg->{$source}->{$sync_name}->{source_ip};
-           $text .= "$cfg->{$source}->{$sync_name}->{vmid} ";
-       } else {
-           $text .= "$cfg->{$source}->{$sync_name}->{source_ip}:" if $cfg->{$source}->{$sync_name}->{source_ip};
-           $text .= "$cfg->{$source}->{$sync_name}->{source_pool}";
-           $text .= "$cfg->{$source}->{$sync_name}->{source_path}" if $cfg->{$source}->{$sync_name}->{source_path};
-       }
-       $text .= " -dest  ";
-       $text .= "$cfg->{$source}->{$sync_name}->{dest_ip}:" if $cfg->{$source}->{$sync_name}->{dest_ip};
-       $text .= "$cfg->{$source}->{$sync_name}->{dest_pool}";
-       $text .= "$cfg->{$source}->{$sync_name}->{dest_path}" if $cfg->{$source}->{$sync_name}->{dest_path};
-       $text .= " -name $sync_name ";
-       $text .= " -limit $cfg->{$source}->{$sync_name}->{limit}" if $cfg->{$source}->{$sync_name}->{limit};
-       $text .= " -maxsnap $cfg->{$source}->{$sync_name}->{maxsnap}" if $cfg->{$source}->{$sync_name}->{maxsnap};
-       $text .= "\n";
+           $text .= "$PROG_PATH sync";
+           $text .= " -source  ";
+           if ($cfg->{$source}->{$sync_name}->{vmid}) {
+               $text .= "$cfg->{$source}->{$sync_name}->{source_ip}:" if $cfg->{$source}->{$sync_name}->{source_ip};
+               $text .= "$cfg->{$source}->{$sync_name}->{vmid} ";
+           } else {
+               $text .= "$cfg->{$source}->{$sync_name}->{source_ip}:" if $cfg->{$source}->{$sync_name}->{source_ip};
+               $text .= "$cfg->{$source}->{$sync_name}->{source_pool}";
+               $text .= "$cfg->{$source}->{$sync_name}->{source_path}" if $cfg->{$source}->{$sync_name}->{source_path};
+           }
+           $text .= " -dest  ";
+           $text .= "$cfg->{$source}->{$sync_name}->{dest_ip}:" if $cfg->{$source}->{$sync_name}->{dest_ip};
+           $text .= "$cfg->{$source}->{$sync_name}->{dest_pool}";
+           $text .= "$cfg->{$source}->{$sync_name}->{dest_path}" if $cfg->{$source}->{$sync_name}->{dest_path};
+           $text .= " -name $sync_name ";
+           $text .= " -limit $cfg->{$source}->{$sync_name}->{limit}" if $cfg->{$source}->{$sync_name}->{limit};
+           $text .= " -maxsnap $cfg->{$source}->{$sync_name}->{maxsnap}" if $cfg->{$source}->{$sync_name}->{maxsnap};
+           $text .= "\n";
        }
     }
        }
     }
-    print($fh $text);
+    die "Can't write to cron\n" if (!print($fh $text));
     close($fh);
 }
 
 sub get_disks {
     my ($target) = @_;
 
     close($fh);
 }
 
 sub get_disks {
     my ($target) = @_;
 
-    my $cmd = "";
-    $cmd = "ssh root\@$target->{ip} " if $target->{ip};
-    $cmd .= "qm config $target->{vmid}";
+    my $cmd = [];
+    push @$cmd, 'ssh', "root\@$target->{ip}", '--', if $target->{ip};
+
+    if ($target->{vm_type} eq 'qemu') {
+       push @$cmd, 'qm', 'config', $target->{vmid};
+    } elsif ($target->{vm_type} eq 'lxc') {
+       push @$cmd, 'pct', 'config', $target->{vmid};
+    } else {
+       die "VM Type unknown\n";
+    }
 
     my $res = run_cmd($cmd);
 
 
     my $res = run_cmd($cmd);
 
-    my $disks = parse_disks($res, $target->{ip});
+    my $disks = parse_disks($res, $target->{ip}, $target->{vm_type});
 
     return $disks;
 }
 
     return $disks;
 }
@@ -575,9 +748,12 @@ sub run_cmd {
     my ($cmd) = @_;
     print "Start CMD\n" if $DEBUG;
     print Dumper $cmd if $DEBUG;
     my ($cmd) = @_;
     print "Start CMD\n" if $DEBUG;
     print Dumper $cmd if $DEBUG;
+    if (ref($cmd) eq 'ARRAY') {
+       $cmd = join(' ', map { ref($_) ? $$_ : shell_quote($_) } @$cmd);
+    }
     my $output = `$cmd 2>&1`;
 
     my $output = `$cmd 2>&1`;
 
-    die $output if 0 != $?;
+    die "COMMAND:\n\t$cmd\nGET ERROR:\n\t$output" if 0 != $?;
 
     chomp($output);
     print Dumper $output if $DEBUG;
 
     chomp($output);
     print Dumper $output if $DEBUG;
@@ -586,81 +762,108 @@ sub run_cmd {
 }
 
 sub parse_disks {
 }
 
 sub parse_disks {
-    my ($text, $ip) = @_;
+    my ($text, $ip, $vm_type) = @_;
 
     my $disks;
 
     my $num = 0;
 
     my $disks;
 
     my $num = 0;
-    my $cmd = "";
-    $cmd .= "ssh root\@$ip " if $ip;
-    $cmd .= "pvesm zfsscan";
-    my $zfs_pools = run_cmd($cmd);
     while ($text && $text =~ s/^(.*?)(\n|$)//) {
        my $line = $1;
     while ($text && $text =~ s/^(.*?)(\n|$)//) {
        my $line = $1;
+
+       next if $line =~ /media=cdrom/;
+       next if $line !~ m/^(?:((?:virtio|ide|scsi|sata|mp)\d+)|rootfs): /;
+
+       #QEMU if backup is not set include in  sync
+       next if $vm_type eq 'qemu' && ($line =~ m/backup=(?i:0|no|off|false)/);
+
+       #LXC if backup is not set do no in sync
+       next if $vm_type eq 'lxc' && ($line =~ m/^mp\d:/) && ($line !~ m/backup=(?i:1|yes|on|true)/);
+
        my $disk = undef;
        my $stor = undef;
        my $disk = undef;
        my $stor = undef;
-       my $is_disk = $line =~ m/^(virtio|ide|scsi|sata){1}\d+: /;
-       if($line =~ m/^(virtio\d+: )(.+:)([A-Za-z0-9\-]+),(.*)$/) {
-           $disk = $3;
-           $stor = $2;
-       } elsif($line =~ m/^(ide\d+: )(.+:)([A-Za-z0-9\-]+),(.*)$/) {
-           $disk = $3;
-           $stor = $2;
-       } elsif($line =~ m/^(scsi\d+: )(.+:)([A-Za-z0-9\-]+),(.*)$/) {
-           $disk = $3;
-           $stor = $2;
-       } elsif($line =~ m/^(sata\d+: )(.+:)([A-Za-z0-9\-]+),(.*)$/) {
-           $disk = $3;
-           $stor = $2;
+       if($line =~ m/^(?:(?:(?:virtio|ide|scsi|sata|mp)\d+)|rootfs): (.*)$/) {
+           my @parameter = split(/,/,$1);
+
+           foreach my $opt (@parameter) {
+               if ($opt =~ m/^(?:file=|volume=)?([^:]+:)([A-Za-z0-9\-]+)$/){
+                   $disk = $2;
+                   $stor = $1;
+                   last;
+               }
+           }
+       }
+       if (!defined($disk) || !defined($stor)) {
+           print "Disk: \"$line\" has no valid zfs dataset format and will be skipped\n";
+           next;
        }
 
        }
 
-       die "disk is not on ZFS Storage\n" if $is_disk && !$disk && $line !~ m/cdrom/;
-
-       if($disk && $line !~ m/none/ && $line !~ m/cdrom/ ) {
-           my $cmd = "";
-           $cmd .= "ssh root\@$ip " if $ip;
-           $cmd .= "pvesm path $stor$disk";
-           my $path = run_cmd($cmd);
+       my $cmd = [];
+       push @$cmd, 'ssh', "root\@$ip", '--' if $ip;
+       push @$cmd, 'pvesm', 'path', "$stor$disk";
+       my $path = run_cmd($cmd);
+
+       die "Get no path from pvesm path $stor$disk\n" if !$path;
+       if ($vm_type eq 'qemu' && $path =~ m/^\/dev\/zvol\/(\w+.*)(\/$disk)$/) {
+
+           my @array = split('/', $1);
+           $disks->{$num}->{pool} = shift(@array);
+           $disks->{$num}->{all} = $disks->{$num}->{pool};
+           if (0 < @array) {
+               $disks->{$num}->{path} = join('/', @array);
+               $disks->{$num}->{all} .= "\/$disks->{$num}->{path}";
+           }
+           $disks->{$num}->{last_part} = $disk;
+           $disks->{$num}->{all} .= "\/$disk";
 
 
-           if ($path =~ m/^\/dev\/zvol\/(\w+).*(\/$disk)$/){
+           $num++;
+       } elsif ($vm_type eq 'lxc' && $path =~ m/^\/(\w+.+)(\/(\w+.*))*(\/$disk)$/) {
 
 
-               $disks->{$num}->{pool} = $1;
-               $disks->{$num}->{path} = $disk;
-               $num++;
+           $disks->{$num}->{pool} = $1;
+           $disks->{$num}->{all} = $disks->{$num}->{pool};
 
 
-           } else {
-               die "ERROR: in path\n";
+           if ($2) {
+               $disks->{$num}->{path} = $3;
+               $disks->{$num}->{all} .= "\/$disks->{$num}->{path}";
            }
            }
+
+           $disks->{$num}->{last_part} = $disk;
+           $disks->{$num}->{all} .= "\/$disk";
+
+           $num++;
+
+       } else {
+           die "ERROR: in path\n";
        }
     }
        }
     }
+
+    die "Vm include no disk on zfs.\n" if !$disks->{0};
     return $disks;
 }
 
 sub snapshot_destroy {
     my ($source, $dest, $method, $snap) = @_;
 
     return $disks;
 }
 
 sub snapshot_destroy {
     my ($source, $dest, $method, $snap) = @_;
 
-    my $zfscmd = "zfs destroy ";
-    my $name = "$source->{path}\@$snap";
+    my @zfscmd = ('zfs', 'destroy');
+    my $snapshot = "$source->{all}\@$snap";
 
     eval {
        if($source->{ip} && $method eq 'ssh'){
 
     eval {
        if($source->{ip} && $method eq 'ssh'){
-           run_cmd("ssh root\@$source->{ip} $zfscmd $source->{pool}$name");
+           run_cmd(['ssh', "root\@$source->{ip}", '--', @zfscmd, $snapshot]);
        } else {
        } else {
-           run_cmd("$zfscmd $source->{pool}$name");
+           run_cmd([@zfscmd, $snapshot]);
        }
     };
     if (my $erro = $@) {
        warn "WARN: $erro";
     }
        }
     };
     if (my $erro = $@) {
        warn "WARN: $erro";
     }
-    if ($dest){
-       my $ssh =  $dest->{ip} ? "ssh root\@$dest->{ip}" : "";
+    if ($dest) {
+       my @ssh = $dest->{ip} ? ('ssh', "root\@$dest->{ip}", '--') : ();
 
 
-       my $path = "";
-       $path ="$dest->{path}" if $dest->{path};
+       my $path = "$dest->{all}\/$source->{last_part}";
 
 
-       my @dir = split(/\//, $source->{path});
        eval {
        eval {
-           run_cmd("$ssh $zfscmd $dest->{pool}$path\/$dir[@dir-1]\@$snap ");
+           run_cmd([@ssh, @zfscmd, "$path\@$snap"]);
        };
        if (my $erro = $@) {
            warn "WARN: $erro";
        };
        if (my $erro = $@) {
            warn "WARN: $erro";
@@ -669,61 +872,58 @@ sub snapshot_destroy {
 }
 
 sub snapshot_exist {
 }
 
 sub snapshot_exist {
-    my ($source ,$dest, $method) = @_;
+    my ($source , $dest, $method) = @_;
 
 
-    my $cmd = "";
-    $cmd = "ssh root\@$dest->{ip} " if $dest->{ip};
-    $cmd .= "zfs list -rt snapshot -Ho name $dest->{pool}";
-    $cmd .= "$dest->{path}" if $dest->{path};
-    my @dir = split(/\//, $source->{path});
-    $cmd .= "\/$dir[@dir-1]\@$source->{old_snap}";
+    my $cmd = [];
+    push @$cmd, 'ssh', "root\@$dest->{ip}", '--' if $dest->{ip};
+    push @$cmd, 'zfs', 'list', '-rt', 'snapshot', '-Ho', 'name';
+    push @$cmd, "$dest->{all}/$source->{last_part}\@$source->{old_snap}";
 
     my $text = "";
     eval {$text =run_cmd($cmd);};
 
     my $text = "";
     eval {$text =run_cmd($cmd);};
-    if (my $erro = $@) {
+    if (my $erro =$@) {
        warn "WARN: $erro";
        return undef;
     }
 
     while ($text && $text =~ s/^(.*?)(\n|$)//) {
        warn "WARN: $erro";
        return undef;
     }
 
     while ($text && $text =~ s/^(.*?)(\n|$)//) {
-       my $line = $1;
+       my $line =$1;
        return 1 if $line =~ m/^.*$source->{old_snap}$/;
     }
 }
 
 sub send_image {
        return 1 if $line =~ m/^.*$source->{old_snap}$/;
     }
 }
 
 sub send_image {
-    my ($source, $dest, $method, $verbose, $limit) = @_;
+    my ($source, $dest, $param) = @_;
 
 
-    my $cmd = "";
+    my $cmd = [];
 
 
-    $cmd .= "ssh root\@$source->{ip} " if $source->{ip};
-    $cmd .= "zfs send ";
-    $cmd .= "-v " if $verbose;
+    push @$cmd, 'ssh', '-o', 'BatchMode=yes', "root\@$source->{ip}", '--' if $source->{ip};
+    push @$cmd, 'zfs', 'send';
+    push @$cmd, '-v' if $param->{verbose};
 
 
-    if($source->{last_snap} && snapshot_exist($source ,$dest, $method)) {
-       $cmd .= "-i $source->{abs_path}\@$source->{old_snap} $source->{abs_path}\@$source->{new_snap} ";
-    } else {
-       $cmd .= "$source->{abs_path}\@$source->{new_snap} ";
+    if($source->{last_snap} && snapshot_exist($source , $dest, $param->{method})) {
+       push @$cmd, '-i', "$source->{all}\@$source->{last_snap}";
     }
     }
+    push @$cmd, '--', "$source->{all}\@$source->{new_snap}";
 
 
-    if ($limit){
-       my $bwl = $limit*1024;
-       $cmd .= "| cstream  -t $bwl";
+    if ($param->{limit}){
+       my $bwl = $param->{limit}*1024;
+       push @$cmd, \'|', 'cstream', '-t', $bwl;
     }
     }
-    $cmd .= "| ";
-    $cmd .= "ssh root\@$dest->{ip} " if $dest->{ip};
-    $cmd .= "zfs recv $dest->{pool}";
-    $cmd .= "$dest->{path}" if $dest->{path};
+    my $target = "$dest->{all}/$source->{last_part}";
+    $target =~ s!/+!/!g;
 
 
-    my @dir = split(/\//,$source->{path});
-    $cmd .= "\/$dir[@dir-1]\@$source->{new_snap}";
+    push @$cmd, \'|';
+    push @$cmd, 'ssh', '-o', 'BatchMode=yes', "root\@$dest->{ip}", '--' if $dest->{ip};
+    push @$cmd, 'zfs', 'recv', '-F', '--';
+    push @$cmd, "$target";
 
     eval {
        run_cmd($cmd)
     };
 
     if (my $erro = $@) {
 
     eval {
        run_cmd($cmd)
     };
 
     if (my $erro = $@) {
-       snapshot_destroy($source, undef, $method, $source->{new_snap});
+       snapshot_destroy($source, undef, $param->{method}, $source->{new_snap});
        die $erro;
     };
 }
        die $erro;
     };
 }
@@ -732,346 +932,317 @@ sub send_image {
 sub send_config{
     my ($source, $dest, $method) = @_;
 
 sub send_config{
     my ($source, $dest, $method) = @_;
 
+    my $source_target = $source->{vm_type} eq 'qemu' ? "$QEMU_CONF/$source->{vmid}.conf": "$LXC_CONF/$source->{vmid}.conf";
+    my $dest_target_new ="$source->{vmid}.conf.$source->{vm_type}.$source->{new_snap}";
+
+    my $config_dir = $dest->{last_part} ?  "${CONFIG_PATH}/$dest->{last_part}" : $CONFIG_PATH;
+
+    $dest_target_new = $config_dir.'/'.$dest_target_new;
+
     if ($method eq 'ssh'){
        if ($dest->{ip} && $source->{ip}) {
     if ($method eq 'ssh'){
        if ($dest->{ip} && $source->{ip}) {
-           run_cmd("ssh root\@$dest->{ip} mkdir $VMCONFIG -p");
-           run_cmd("scp root\@$source->{ip}:$QEMU_CONF$source->{vmid}.conf root\@$dest->{ip}:$VMCONFIG$source->{vmid}.conf.$source->{new_snap}");
+           run_cmd(['ssh', "root\@$dest->{ip}", '--', 'mkdir', '-p', '--', $config_dir]);
+           run_cmd(['scp', '--', "root\@[$source->{ip}]:$source_target", "root\@[$dest->{ip}]:$dest_target_new"]);
        } elsif ($dest->{ip}) {
        } elsif ($dest->{ip}) {
-           run_cmd("ssh root\@$dest->{ip} mkdir $VMCONFIG -p");
-           run_cmd("scp $QEMU_CONF$source->{vmid}.conf root\@$dest->{ip}:$VMCONFIG$source->{vmid}.conf.$source->{new_snap}");
+           run_cmd(['ssh', "root\@$dest->{ip}", '--', 'mkdir', '-p', '--', $config_dir]);
+           run_cmd(['scp', '--', $source_target, "root\@[$dest->{ip}]:$dest_target_new"]);
        } elsif ($source->{ip}) {
        } elsif ($source->{ip}) {
-           run_cmd("mkdir $VMCONFIG -p");
-           run_cmd("scp root\@$source->{ip}:$QEMU_CONF$source->{vmid}.conf $VMCONFIG$source->{vmid}.conf.$source->{new_snap}");
+           run_cmd(['mkdir', '-p', '--', $config_dir]);
+           run_cmd(['scp', '--', "root\@[$source->{ip}]:$source_target", $dest_target_new]);
        }
 
        if ($source->{destroy}){
        }
 
        if ($source->{destroy}){
+           my $dest_target_old ="${config_dir}/$source->{vmid}.conf.$source->{vm_type}.$source->{old_snap}";
            if($dest->{ip}){
            if($dest->{ip}){
-               run_cmd("ssh root\@$dest->{ip} rm -f $VMCONFIG$source->{vmid}.conf.$source->{old_snap}");
+               run_cmd(['ssh', "root\@$dest->{ip}", '--', 'rm', '-f', '--', $dest_target_old]);
            } else {
            } else {
-               run_cmd("rm -f $VMCONFIG$source->{vmid}.conf.$source->{old_snap}");
+               run_cmd(['rm', '-f', '--', $dest_target_old]);
            }
        }
            }
        }
+    } elsif ($method eq 'local') {
+       run_cmd(['mkdir', '-p', '--', $config_dir]);
+       run_cmd(['cp', $source_target, $dest_target_new]);
     }
 }
 
 sub get_date {
     }
 }
 
 sub get_date {
-    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
-    my $datestamp = sprintf ( "%04d-%02d-%02d_%02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec);
+    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
+    my $datestamp = sprintf ("%04d-%02d-%02d_%02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
 
     return $datestamp;
 }
 
 sub status {
 
     return $datestamp;
 }
 
 sub status {
-    my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
+    my $cfg = read_cron();
 
 
-    my $status_list = sprintf("%-25s%-15s%-10s\n","SOURCE","NAME","STATUS");
+    my $status_list = sprintf("%-25s%-25s%-10s\n", "SOURCE", "NAME", "STATUS");
 
 
-    foreach my $source (sort keys%{$cfg}){
-       foreach my $sync_name (sort keys%{$cfg->{$source}}){
-         my $status;
+    my $states = read_state();
 
 
-         my $source_name = $source;
-
-         $source_name = $cfg->{$source}->{$sync_name}->{source_ip}.":".$source if $cfg->{$source}->{$sync_name}->{source_ip};
-
-         $status = sprintf("%-10s",$cfg->{$source}->{$sync_name}->{status});
-
-         $status_list .= sprintf("%-25s%-15s", cut_to_width($source_name,25), cut_to_width($sync_name,15));
-         $status_list .= "$status\n";
+    foreach my $source (sort keys%{$cfg}) {
+       foreach my $sync_name (sort keys%{$cfg->{$source}}) {
+           $status_list .= sprintf("%-25s", cut_target_width($source, 25));
+           $status_list .= sprintf("%-25s", cut_target_width($sync_name, 25));
+           $status_list .= "$states->{$source}->{$sync_name}->{state}\n";
        }
     }
 
     return $status_list;
 }
 
        }
     }
 
     return $status_list;
 }
 
-sub enable{
+sub enable_job {
     my ($param) = @_;
 
     my ($param) = @_;
 
-    modify_configs($param->{name}, $param->{source},4);
+    my $job = get_job($param);
+    $job->{state} = "ok";
+    update_state($job);
+    update_cron($job);
 }
 
 }
 
-sub disable{
+sub disable_job {
     my ($param) = @_;
 
     my ($param) = @_;
 
-    modify_configs($param->{name}, $param->{source},2);
+    my $job = get_job($param);
+    $job->{state} = "stopped";
+    update_state($job);
+    update_cron($job);
 }
 
 }
 
-sub modify_configs{
-    my ($name, $sou, $op) = @_;
+my $command = $ARGV[0];
 
 
-    $name =  $name ? $name : "default";
+my $commands = {'destroy' => 1,
+               'create' => 1,
+               'sync' => 1,
+               'list' => 1,
+               'status' => 1,
+               'help' => 1,
+               'enable' => 1,
+               'disable' => 1};
 
 
-    my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
+if (!$command || !$commands->{$command}) {
+    usage();
+    die "\n";
+}
 
 
-    my $source = parse_target($sou);
+my $help_sync = <<EOF;
+$PROGNAME sync -dest <string> -source <string> [OPTIONS]\n
 
 
-    my $change_configs = sub {
-       my ($path, $name, $cfg, $op) = @_;
+       will sync one time
 
 
-       die "Source does not exist!\n" unless $cfg->{$path} ;
+        -dest      string
 
 
-       die "Sync Name does not exist!\n" unless $cfg->{$path}->{$name};
+               the destination target is like [IP:]<Pool>[/Path]
 
 
-       my $source = $cfg->{$path}->{$name}->{source_ip} ? "$cfg->{$path}->{$name}->{source_ip}:"  : '';
+       -limit     integer
 
 
-       $source .= $cfg->{$path}->{$name}->{source_pool} if $cfg->{$path}->{$name}->{source_pool};
-       $source .= $cfg->{$path}->{$name}->{source_path} ? $cfg->{$path}->{$name}->{source_path}  :'';
+               max sync speed in kBytes/s, default unlimited
 
 
-       my $dest = $cfg->{$path}->{$name}->{dest_ip} ? $cfg->{$path}->{$name}->{dest_ip}  :"";
-       $dest .= $cfg->{$path}->{$name}->{dest_pool} if $cfg->{$path}->{$name}->{dest_pool};
-       $dest .= $cfg->{$path}->{$name}->{dest_path} ? $cfg->{$path}->{$name}->{dest_path}  :'';
+        -maxsnap   integer
 
 
-       if($op == 1){
-           delete $cfg->{$path}->{$name};
+               how much snapshots will be kept before get erased, default 1
 
 
-           delete $cfg->{$path} if keys%{$cfg->{$path}} == 0;
+        -name      string
 
 
-           write_to_config($cfg);
-       }
-       if($op == 1 || $op == 2) {
+               name of the sync job, if not set it is default.
+               It is only necessary if scheduler allready contains this source.
 
 
-           if ($op == 2) {
+        -source    string
 
 
-               $cfg->{$path}->{$name}->{status} = "stoped";
+               the source can be an <VMID> or [IP:]<ZFSPool>[/Path]
 
 
-               write_to_config($cfg);
-           }
-           write_to_cron($cfg);
-       } elsif($op == 4) {
-           my $job = {};
+       -verbose   boolean
 
 
-           $cfg->{$path}->{$name}->{status} = "ok";
+               print out the sync progress.
+EOF
 
 
-           write_to_config($cfg);
+my $help_create = <<EOF;
+$PROGNAME create -dest <string> -source <string> [OPTIONS]
 
 
-           write_to_cron($cfg);
-       }
-    };
+        Create a sync Job
 
 
+        -dest      string
 
 
-    if ($source->{vmid}) {
-       my $path = $source->{vmid};
+               the destination target is like [IP]:<Pool>[/Path]
 
 
-        &$change_configs($path, $name, $cfg, $op)
-    } else {
-       my $path = $source->{pool};
-       $path .= $source->{path} if $source->{path};
+        -limit     integer
 
 
-       &$change_configs($path, $name, $cfg, $op);
-    }
-}
+               max sync speed in kBytes/s, default unlimited
 
 
+        -maxsnap   string
 
 
-my $command = $ARGV[0];
+               how much snapshots will be kept before get erased, default 1
 
 
-my $commands = {'destroy' => 1,
-              'create' => 1,
-              'sync' => 1,
-              'list' => 1,
-              'status' => 1,
-              'help' => 1,
-              'enable' => 1,
-              'disable' => 1};
+        -name      string
 
 
-if (!$command || !$commands->{$command}) {
-    usage();
-    die "\n";
-}
+               name of the sync job, if not set it is default
+
+        -skip      boolean
+
+               if this flag is set it will skip the first sync
+
+        -source    string
+
+               the source can be an <VMID> or [IP:]<ZFSPool>[/Path]
+EOF
+
+my $help_destroy = <<EOF;
+$PROGNAME destroy -source <string> [OPTIONS]
+
+        remove a sync Job from the scheduler
+
+        -name      string
+
+               name of the sync job, if not set it is default
+
+        -source    string
+
+                the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
+EOF
+
+my $help_help = <<EOF;
+$PROGNAME help <cmd> [OPTIONS]
+
+       Get help about specified command.
+
+        <cmd>      string
 
 
-my $dest = '';
-my $source = '';
-my $verbose = '';
-my $interval = '';
-my $limit = '';
-my $maxsnap = '';
-my $name = '';
-my $skip = '';
-
-my $help_sync = "$PROGNAME sync -dest <string> -source <string> [OPTIONS]\n
-\twill sync one time\n
-\t-dest\tstring\n
-\t\tthe destination target is like [IP:]<Pool>[/Path]\n
-\t-limit\tinteger\n
-\t\tmax sync speed in kBytes/s, default unlimited\n
-\t-maxsnap\tinteger\n
-\t\thow much snapshots will be kept before get erased, default 1/n
-\t-name\tstring\n
-\t\tname of the sync job, if not set it is default.
-\tIt is only necessary if scheduler allready contains this source.\n
-\t-source\tstring\n
-\t\tthe source can be an <VMID> or [IP:]<ZFSPool>[/Path]\n";
-
-my $help_create = "$PROGNAME create -dest <string> -source <string> [OPTIONS]/n
-\tCreate a sync Job\n
-\t-dest\tstringn\n
-\t\tthe destination target is like [IP]:<Pool>[/Path]\n
-\t-interval\tinteger\n
-\t\tthe interval in min in witch the zfs will sync,
-\t\tdefault is 15 min\n
-\t-limit\tinteger\n
-\t\tmax sync speed in kBytes/s, default unlimited\n
-\t-maxsnap\tstring\n
-\t\thow much snapshots will be kept before get erased, default 1\n
-\t-name\tstring\n
-\t\tname of the sync job, if not set it is default\n
-\t-skip\tboolean\n
-\t\tif this flag is set it will skip the first sync\n
-\t-source\tstring\n
-\t\tthe source can be an <VMID> or [IP:]<ZFSPool>[/Path]\n";
-
-my $help_destroy = "$PROGNAME destroy -source <string> [OPTIONS]\n
-\tremove a sync Job from the scheduler\n
-\t-name\tstring\n
-\t\tname of the sync job, if not set it is default\n
-\t-source\tstring\n
-\t\tthe source can be an  <VMID> or [IP:]<ZFSPool>[/Path]\n";
-
-my $help_help = "$PROGNAME help <cmd> [OPTIONS]\n
-\tGet help about specified command.\n
-\t<cmd>\tstring\n
-\t\tCommand name\n
-\t-verbose\tboolean\n
-\t\tVerbose output format.\n";
-
-my $help_list = "$PROGNAME list\n
-\tGet a List of all scheduled Sync Jobs\n";
-
-my $help_status = "$PROGNAME status\n
-\tGet the status of all scheduled Sync Jobs\n";
-
-my $help_enable = "$PROGNAME enable -source <string> [OPTIONS]\n
-\tenable a syncjob and reset error\n
-\t-name\tstring\n
-\t\tname of the sync job, if not set it is default\n
-\t-source\tstring\n
-\t\tthe source can be an  <VMID> or [IP:]<ZFSPool>[/Path]\n";
-
-my $help_disable = "$PROGNAME disable -source <string> [OPTIONS]\n
-\tpause a syncjob\n
-\t-name\tstring\n
-\t\tname of the sync job, if not set it is default\n
-\t-source\tstring\n
-\t\tthe source can be an  <VMID> or [IP:]<ZFSPool>[/Path]\n";
-
-sub help{
+               Command name
+
+       -verbose   boolean
+
+               Verbose output format.
+EOF
+
+my $help_list = <<EOF;
+$PROGNAME list
+
+       Get a List of all scheduled Sync Jobs
+EOF
+
+my $help_status = <<EOF;
+$PROGNAME status
+
+       Get the status of all scheduled Sync Jobs
+EOF
+
+my $help_enable = <<EOF;
+$PROGNAME enable -source <string> [OPTIONS]
+
+        enable a syncjob and reset error
+
+        -name      string
+
+               name of the sync job, if not set it is default
+
+        -source    string
+
+                the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
+EOF
+
+my $help_disable = <<EOF;
+$PROGNAME disable -source <string> [OPTIONS]
+
+        pause a sync job
+
+        -name      string
+
+               name of the sync job, if not set it is default
+
+        -source    string
+
+                the source can be an  <VMID> or [IP:]<ZFSPool>[/Path] 
+EOF
+
+sub help {
     my ($command) = @_;
 
     my ($command) = @_;
 
-    switch($command){
-       case 'help'
-       {
-           die "$help_help\n";
-       }
-       case 'sync'
-       {
-           die "$help_sync\n";
-       }
-       case 'destroy'
-       {
-           die "$help_destroy\n";
-       }
-       case 'create'
-       {
-           die "$help_create\n";
-       }
-       case 'list'
-       {
-           die "$help_list\n";
-       }
-       case 'status'
-       {
-           die "$help_status\n";
-       }
-       case 'enable'
-       {
-            die "$help_enable\n";
-       }
-       case 'disable'
-       {
-           die "$help_enable\n";
-       }
+    if ($command eq 'help') {
+       die "$help_help\n";
+
+    } elsif ($command eq 'sync') {
+       die "$help_sync\n";
+
+    } elsif ($command eq 'destroy') {
+       die "$help_destroy\n";
+
+    } elsif ($command eq 'create') {
+       die "$help_create\n";
+
+    } elsif ($command eq 'list') {
+       die "$help_list\n";
+
+    } elsif ($command eq 'status') {
+       die "$help_status\n";
+
+    } elsif ($command eq 'enable') {
+       die "$help_enable\n";
+
+    } elsif ($command eq 'disable') {
+       die "$help_disable\n";
+
     }
 
 }
 
     }
 
 }
 
-my $err = GetOptions ('dest=s' => \$dest,
-          'source=s' => \$source,
-          'verbose' => \$verbose,
-          'interval=i' => \$interval,
-          'limit=i' => \$limit,
-          'maxsnap=i' => \$maxsnap,
-          'name=s' => \$name,
-          'skip' => \$skip);
-
-if ($err == 0) {
-    die "can't parse options\n";
-}
+my @arg = @ARGV;
+my $param = parse_argv(@arg);
+
+if ($command eq 'destroy') {
+    die "$help_destroy\n" if !$param->{source};
+
+    check_target($param->{source});
+    destroy_job($param);
+
+} elsif ($command eq 'sync') {
+    die "$help_sync\n" if !$param->{source} || !$param->{dest};
+
+    check_target($param->{source});
+    check_target($param->{dest});
+    sync($param);
+
+} elsif ($command eq 'create') {
+    die "$help_create\n" if !$param->{source} || !$param->{dest};
+
+    check_target($param->{source});
+    check_target($param->{dest});
+    init($param);
+
+} elsif ($command eq 'status') {
+    print status();
+
+} elsif ($command eq 'list') {
+    print list();
+
+} elsif ($command eq 'help') {
+    my $help_command = $ARGV[1];
+
+    if ($help_command && $commands->{$help_command}) {
+       print help($help_command);
 
 
-my $param;
-$param->{dest} = $dest;
-$param->{source} = $source;
-$param->{verbose} = $verbose;
-$param->{interval} = $interval;
-$param->{limit} = $limit;
-$param->{maxsnap} = $maxsnap;
-$param->{name} = $name;
-$param->{skip} = $skip;
-
-switch($command){
-    case "destroy"
-    {
-       die "$help_destroy\n" if !$source;
-       check_target($source);
-       destroy($param);
-    }
-    case "sync"
-    {
-       die "$help_sync\n" if !$source || !$dest;
-       check_target($source);
-       check_target($dest);
-       sync($param);
-    }
-    case "create"
-    {
-       die "$help_create\n" if !$source || !$dest;
-       check_target($source);
-       check_target($dest);
-       init($param);
-    }
-    case "status"
-    {
-       print status();
-    }
-    case "list"
-    {
-       print list();
-    }
-    case "help"
-    {
-       my $help_command = $ARGV[1];
-       if ($help_command && $commands->{$help_command}) {
-           print help($help_command);
-       }
-       if ($verbose){
-           exec("man $PROGNAME");
-       } else {
-           usage(1);
-       }
-    }
-    case "enable"
-    {
-       die "$help_enable\n" if !$source;
-       check_target($source);
-       enable($param);
     }
     }
-    case "disable"
-    {
-       die "$help_disable\n" if !$source;
-       check_target($source);
-       disable($param);
+    if ($param->{verbose}) {
+       exec("man $PROGNAME");
+
+    } else {
+       usage(1);
+
     }
     }
+
+} elsif ($command eq 'enable') {
+    die "$help_enable\n" if !$param->{source};
+
+    check_target($param->{source});
+    enable_job($param);
+
+} elsif ($command eq 'disable') {
+    die "$help_disable\n" if !$param->{source};
+
+    check_target($param->{source});
+    disable_job($param);
+
 }
 
 }
 
-sub usage{
+sub usage {
     my ($help) = @_;
 
     print("ERROR:\tno command specified\n") if !$help;
     my ($help) = @_;
 
     print("ERROR:\tno command specified\n") if !$help;
@@ -1086,16 +1257,9 @@ sub usage{
     print("\t$PROGNAME sync -dest <string> -source <string> [OPTIONS]\n");
 }
 
     print("\t$PROGNAME sync -dest <string> -source <string> [OPTIONS]\n");
 }
 
-sub check_target{
+sub check_target {
     my ($target) = @_;
     my ($target) = @_;
-
-    chomp($target);
-
-    if($target !~ m/(\d+.\d+.\d+.\d+:)?([\w\-\_\/]+)(\/.+)?/){
-       print("ERROR:\t$target is not valid.\n\tUse [IP:]<ZFSPool>[/Path]!\n");
-       return 1;
-    }
-    return undef;
+    parse_target($target);
 }
 
 __END__
 }
 
 __END__
@@ -1106,9 +1270,9 @@ pve-zsync - PVE ZFS Replication Manager
 
 =head1 SYNOPSIS
 
 
 =head1 SYNOPSIS
 
-zfs-zsync <COMMAND> [ARGS] [OPTIONS]
+pve-zsync <COMMAND> [ARGS] [OPTIONS]
 
 
-zfs-zsync help <cmd> [OPTIONS]
+pve-zsync help <cmd> [OPTIONS]
 
        Get help about specified command.
 
 
        Get help about specified command.
 
@@ -1120,82 +1284,78 @@ zfs-zsync help <cmd> [OPTIONS]
 
                Verbose output format.
 
 
                Verbose output format.
 
-zfs-zsync create -dest <string> -source <string> [OPTIONS]
+pve-zsync create -dest <string> -source <string> [OPTIONS]
 
 
-          Create a sync Job
+        Create a sync Job
 
 
-          -dest      string
+        -dest      string
 
                the destination target is like [IP]:<Pool>[/Path]
 
 
                the destination target is like [IP]:<Pool>[/Path]
 
-          -interval  integer
-
-               the interval in min in witch the zfs will sync, default is 15 min
-
-          -limit     integer
+        -limit     integer
 
                max sync speed in kBytes/s, default unlimited
 
 
                max sync speed in kBytes/s, default unlimited
 
-          -maxsnap   string
+        -maxsnap   string
 
                how much snapshots will be kept before get erased, default 1
 
 
                how much snapshots will be kept before get erased, default 1
 
-          -name      string
+        -name      string
 
                name of the sync job, if not set it is default
 
 
                name of the sync job, if not set it is default
 
-          -skip      boolean
+        -skip      boolean
 
                if this flag is set it will skip the first sync
 
 
                if this flag is set it will skip the first sync
 
-          -source    string
+        -source    string
 
                the source can be an <VMID> or [IP:]<ZFSPool>[/Path]
 
 
                the source can be an <VMID> or [IP:]<ZFSPool>[/Path]
 
-zfs-zsync destroy -source <string> [OPTIONS]
+pve-zsync destroy -source <string> [OPTIONS]
 
 
-          remove a sync Job from the scheduler
+        remove a sync Job from the scheduler
 
 
-          -name      string
+        -name      string
 
                name of the sync job, if not set it is default
 
 
                name of the sync job, if not set it is default
 
-          -source    string
+        -source    string
 
                 the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
 
 
                 the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
 
-zfs-zsync disable -source <string> [OPTIONS]
+pve-zsync disable -source <string> [OPTIONS]
 
 
-          pause a sync job
+        pause a sync job
 
 
-          -name      string
+        -name      string
 
                name of the sync job, if not set it is default
 
 
                name of the sync job, if not set it is default
 
-          -source    string
+        -source    string
 
                 the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
 
 
                 the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
 
-zfs-zsync enable -source <string> [OPTIONS]
+pve-zsync enable -source <string> [OPTIONS]
 
 
-          enable a syncjob and reset error
+        enable a syncjob and reset error
 
 
-          -name      string
+        -name      string
 
                name of the sync job, if not set it is default
 
 
                name of the sync job, if not set it is default
 
-          -source    string
+        -source    string
 
                 the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
 
                 the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
-zfs-zsync list
+pve-zsync list
 
        Get a List of all scheduled Sync Jobs
 
 
        Get a List of all scheduled Sync Jobs
 
-zfs-zsync status
+pve-zsync status
 
        Get the status of all scheduled Sync Jobs
 
 
        Get the status of all scheduled Sync Jobs
 
-zfs-zsync sync -dest <string> -source <string> [OPTIONS]
+pve-zsync sync -dest <string> -source <string> [OPTIONS]
 
        will sync one time
 
 
        will sync one time
 
@@ -1207,23 +1367,29 @@ zfs-zsync sync -dest <string> -source <string> [OPTIONS]
 
                max sync speed in kBytes/s, default unlimited
 
 
                max sync speed in kBytes/s, default unlimited
 
-         -maxsnap   integer
+        -maxsnap   integer
 
                how much snapshots will be kept before get erased, default 1
 
 
                how much snapshots will be kept before get erased, default 1
 
-         -name      string
+        -name      string
 
                name of the sync job, if not set it is default.
                It is only necessary if scheduler allready contains this source.
 
 
                name of the sync job, if not set it is default.
                It is only necessary if scheduler allready contains this source.
 
-          -source    string
+        -source    string
 
                the source can be an <VMID> or [IP:]<ZFSPool>[/Path]
 
 
                the source can be an <VMID> or [IP:]<ZFSPool>[/Path]
 
+       -verbose   boolean
+
+               print out the sync progress.
+
 =head1 DESCRIPTION
 
 This Tool helps you to sync your VM or directory which stored on ZFS between 2 servers.
 This tool also has the capability to add jobs to cron so the sync will be automatically done.
 =head1 DESCRIPTION
 
 This Tool helps you to sync your VM or directory which stored on ZFS between 2 servers.
 This tool also has the capability to add jobs to cron so the sync will be automatically done.
+The default syncing interval is set to 15 min, if you want to change this value you can do this in /etc/cron.d/pve-zsync.
+To config cron see man crontab.
 
 =head2 PVE ZFS Storage sync Tool
 
 
 =head2 PVE ZFS Storage sync Tool
 
@@ -1232,13 +1398,15 @@ This Tool can get remote pool on other PVE or send Pool to others ZFS machines
 =head1 EXAMPLES
 
 add sync job from local VM to remote ZFS Server
 =head1 EXAMPLES
 
 add sync job from local VM to remote ZFS Server
-zfs-zsync -source=100 -dest=192.168.1.2:zfspool
+pve-zsync create -source=100 -dest=192.168.1.2:zfspool
 
 =head1 IMPORTANT FILES
 
 
 =head1 IMPORTANT FILES
 
-Where the cron jobs are stored                            /etc/cron.d/pve-zsync
-Where the VM config get copied on the destination machine /var/pve-zsync
-Where the config is stored                                /var/pve-zsync
+Cron jobs and config are stored at                      /etc/cron.d/pve-zsync
+
+The VM config get copied on the destination machine to  /var/lib/pve-zsync/
+
+=head1 COPYRIGHT AND DISCLAIMER
 
 Copyright (C) 2007-2015 Proxmox Server Solutions GmbH
 
 
 Copyright (C) 2007-2015 Proxmox Server Solutions GmbH