12 use Storable
qw(dclone);
18 use PVE
::AccessControl
;
26 # to enable cached operation, you need to call 'inotify_init'
27 # inotify handles are a limited resource, so use with care (only
28 # enable the cache if you really need it)
30 # Note: please close the inotify handle after you fork
33 '/etc/network/interfaces' => '/etc/network/interfaces.new',
38 return ('127.0.0.1', 83);
50 open (TMP
, "</usr/share/zoneinfo/zone.tab");
52 while ($line = <TMP
>) {
54 if (!($line =~ m
|^[A-Z
][A-Z
]\s
+\S
+\s
+(\S
+)/(\S
+).*|)) {
59 push @zoneinfo, "$1/$2";
65 @zoneinfo = sort (@zoneinfo);
70 my $bond_modes = { 'balance-rr' => 0,
87 foreach my $iface (split (/;/, $data)) {
89 foreach my $pv (split (/,/, $iface)) {
90 if ($pv =~ m/^(ifname|mac|bridge|host_ifname|host_mac)=(.+)$/) {
96 $res->{$d->{ifname
}} = $d;
98 die "unable to parse --netif value";
106 my ($filename, $fh, $update) = @_;
112 while (my $rec = <$fh>) {
119 if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) {
120 $res->{headline
} = $1;
122 $long =~ s/\n\s+/ /g;
125 $res->{description
} = $long;
126 } elsif ($rec =~ s/^Version:\s*(.*\S)\s*\n//i) {
128 if ($version =~ m/^(\d[a-zA-Z0-9\.\+\-\:\~]*)-(\d+)$/) {
129 $res->{version
} = $version;
131 my $msg = "unable to parse appliance record: version = '$version'";
132 $update ?
die "$msg\n" : syslog
('err', $msg);
134 } elsif ($rec =~ s/^Type:\s*(.*\S)\s*\n//i) {
136 if ($type =~ m/^(openvz)$/) {
137 $res->{type
} = $type;
139 my $msg = "unable to parse appliance record: unknown type '$type'";
140 $update ?
die "$msg\n" : syslog
('err', $msg);
142 } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) {
145 my $msg = "unable to parse appliance record: $rec";
146 $update ?
die "$msg\n" : syslog
('err', $msg);
152 if ($res->{'package'} eq 'pve-web-news' && $res->{description
}) {
153 $list->{'all'}->{$res->{'package'}} = $res;
157 $res->{section
} = 'unknown' if !$res->{section
};
159 if ($res->{'package'} && $res->{type
} && $res->{os
} && $res->{version
} &&
162 if ($res->{location
}) {
163 $template = $res->{location
};
164 $template =~ s
|.*/([^/]+.tar
.gz
)|$1|;
166 $template = "$res->{os}-$res->{package}_$res->{version}_i386.tar.gz";
167 $template =~ s/$res->{os}-$res->{os}-/$res->{os}-/;
169 $res->{template
} = $template;
170 $list->{$res->{section
}}->{$template} = $res;
171 $list->{'all'}->{$template} = $res;
173 my $msg = "found incomplete appliance records";
174 $update ?
die "$msg\n" : syslog
('err', $msg);
181 # we write /etc/host at startup - see pvesetup (we do not
182 # dynamically update this file).
183 # we use an host alias 'pvelocalhost' to mark the line we write/update.
184 sub update_etc_hosts
{
186 my $hostname = PVE
::Config
::read_file
("hostname");
187 my $rconf = PVE
::Config
::read_file
('resolvconf');
188 my $ifaces = PVE
::Config
::read_file
("interfaces");
189 my $localip = $ifaces->{vmbr0
}->{address
} || $ifaces->{eth0
}->{address
};
191 my $domain = $rconf->{search
};
193 my $filename = "/etc/hosts";
194 my $fh = IO
::File-
>new($filename, "r") ||
195 die "unable to open file '$filename' - $! :ERROR";
197 my $outfh = PVE
::AtomicFile-
>open($filename, "w") ||
198 die "unable to open file '$filename' for writing - $! :ERROR";
202 while (defined ($line = <$fh>)) {
204 if ($line =~ m/^\s*127.0.0.1\s/) {
205 print $outfh "$line\n";
206 print $outfh "$localip $hostname.$domain $hostname pvelocalhost\n";
210 if ($line =~ m/^\s*(\d+\.\d+\.\d+\.\d+\s+.*\S)\s*/) {
212 foreach my $n (split (/\s+/, $1)) {
214 if ($e eq lc ($hostname) ||
215 $e eq lc ($localip) ||
216 $e eq 'pvelocalhost') {
224 print $outfh "$line\n";
240 return read_file
("/etc/qemu-server/$vmid.conf");
246 return read_file
("/etc/vz/conf/$veid.conf");
250 my ($filename, $fh) = @_;
252 $filename =~ m
|/(\d
+)\
.conf
$|
253 || die "got strange filename '$filename'";
255 my $storecfg = read_file
("storagecfg");
257 return PVE
::QemuServer
::parse_config
($filename, $fh, $storecfg);
261 my ($filename, $fh) = @_;
263 $filename =~ m
|/(\d
+)\
.conf
$|
264 || die "got strange filename '$filename'";
267 while (defined (my $line = <$fh>)) {
268 next if $line =~ m/^#/;
269 next if $line =~ m/^\s*$/;
271 if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
275 if ($text =~ m/^(\d+):(\d+)$/) {
279 $data->{$name}->{bar
} = $bar;
280 $data->{$name}->{lim
} = $lim;
282 $data->{$name}->{value
} = $text;
285 die "unable to parse config line: $line\n";
293 my ($filename, $fh) = @_;
297 1 while (defined ($line = <$fh>) && ($line !~ m/^.*ssh-rsa\s/));
299 my $rsapubkey = $line;
301 $rsapubkey =~ s/^.*ssh-rsa\s+//i;
302 $rsapubkey =~ s/\s+root\@\S+\s*$//i;
303 $rsapubkey =~ s/\s+$//;
305 die "strange key format - not base64 encoded"
306 if $rsapubkey !~ m/^[A-Za-z0-9\+\/]+={0,2}$/;
308 die "unable to read '$filename'" if !$rsapubkey;
314 my ($filename, $fh) = @_;
320 while (defined ($line = <$fh>)) {
322 next if $line =~ m/^\s*$/;
324 if ($line =~ m/^counter:(\S+):(\d+):$/) {
327 syslog
('err', "warning: unable to parse file '$filename'");
335 my ($filename, $fh, $data) = @_;
337 foreach my $cn (keys %$data) {
338 print $fh "counter:$cn:$data->{$cn}:\n";
344 sub update_pcounter
{
345 my ($filename, $data, $counter) = @_;
352 sub read_var_lib_vmops
{
353 my ($filename, $fh) = @_;
359 while (defined ($line = <$fh>)) {
364 if (($upid_hash = PVE
::Utils
::upid_decode
($line)) &&
365 $upid_hash->{type
} eq 'vmops') {
366 my $cid = $upid_hash->{cid
};
367 my $veid = $upid_hash->{veid
};
368 $res->{"CID_$cid"}->{"VEID_$veid"} = $upid_hash;
375 sub write_var_lib_vmops
{
376 my ($filename, $fh, $data) = @_;
378 foreach my $ckey (sort keys %$data) {
379 next if $ckey !~ m/^CID_(\d+)$/;
381 my $vzl = $data->{$ckey};
383 foreach my $vekey (sort keys %$vzl) {
384 next if $vekey !~ m/^VEID_(\d+)$/;
387 my $upid = PVE
::Utils
::upid_encode
($vzl->{$vekey});
395 sub update_var_lib_vmops
{
396 my ($filename, $vmops, $upid) = @_;
398 if (my $upid_hash = PVE
::Utils
::upid_decode
($upid)) {
399 my $cid = $upid_hash->{cid
};
400 my $veid = $upid_hash->{veid
};
401 $vmops->{"CID_$cid"}->{"VEID_$veid"} = $upid_hash;
407 sub read_var_lib_vzlist
{
408 my ($filename, $fh) = @_;
416 while (defined ($line = <$fh>)) {
419 next if $line =~ m/^\#/; # skip comments
420 next if $line =~ m/^\s*$/; # skip empty lines
422 if ($line =~ m/^CID:(\d+):(\d+):(\d+):\S*$/) {
424 $res->{"CID_$cid"}->{lasttime
} = $2;
425 $res->{"CID_$cid"}->{version
} = $3;
429 if (!defined ($cid)) {
430 warn "unable to parse line - undefined cluster ID: $line";
433 if ($line =~ m/^(\d+):([a-z]+):(\d+):(\S+):(\S+):(\S+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):$/) {
448 $res->{"CID_$cid"}->{"VEID_$1"} = $d;
450 warn "unable to parse line: $line";
457 sub write_var_lib_vzlist
{
458 my ($filename, $fh, $data) = @_;
460 print $fh "# Cluster wide VZ status\n\n";
462 foreach my $ckey (sort keys %$data) {
463 next if $ckey !~ m/^CID_(\d+)$/;
465 my $vzl = $data->{$ckey};
467 print $fh "CID:$cid:$vzl->{lasttime}:$vzl->{version}:\n";
469 foreach my $vekey (sort keys %$vzl) {
470 my $d = $vzl->{$vekey};
471 next if $vekey !~ m/^VEID_(\d+)$/;
474 print $fh "$veid:$d->{type}:$d->{nproc}:$d->{status}:$d->{ip}:$d->{name}:" .
475 "$d->{mem}:$d->{maxmem}:$d->{disk}:$d->{maxdisk}:$d->{pctcpu}:$d->{uptime}:$d->{relcpu}:\n";
484 sub update_var_lib_vzlist
{
485 my ($filename, $vzlist, $data, $cid) = @_;
487 my $old = $vzlist->{"CID_$cid"};
491 # only update if record is newer:
492 # record is considered newer if either version or lastime is newer
493 # (skip update when version is older and lastime ins older)
495 if (($old->{version
} > $data->{version
}) &&
496 ($old->{lasttime
} >= $data->{lasttime
})) {
501 my $ckey = "CID_$cid";
503 if (!$data->{qemu
}) {
504 # record does not contain info about qemu, so copy them
505 my $vzl = $vzlist->{$ckey};
506 foreach my $vekey (keys %$vzl) {
507 my $d = $vzl->{$vekey};
508 next if $vekey !~ m/^VEID_(\d+)$/;
509 next if $d->{type
} ne 'qemu';
510 next if defined ($data->{$vekey}); # already defined ?
511 $data->{$vekey} = $d;
515 if (!$data->{openvz
}) {
516 # record does not contain info about openvz, so copy them
517 my $vzl = $vzlist->{$ckey};
518 foreach my $vekey (keys %$vzl) {
519 my $d = $vzl->{$vekey};
520 next if $vekey !~ m/^VEID_(\d+)$/;
521 next if $d->{type
} ne 'openvz';
522 next if defined ($data->{$vekey}); # already defined ?
523 $data->{$vekey} = $d;
527 $vzlist->{$ckey} = $data;
529 # remove non-existing cluster nodes
530 my $ccfg = read_file
("clustercfg");
531 PVE
::Utils
::foreach_cid
($vzlist, sub {
532 my ($cid, undef, $ckey) = @_;
534 delete $vzlist->{$ckey} if !defined ($ccfg->{$ckey});
536 delete $vzlist->{$ckey} if $cid != 0;
544 sub read_var_lib_syncstatus
{
545 my ($filename, $fh) = @_;
551 while (defined ($line = <$fh>)) {
554 next if $line =~ m/^\#/; # skip comments
555 next if $line =~ m/^\s*$/; # skip empty lines
557 if ($line =~ m/^(\d+):(\d+):$/) {
558 $res->{$1}->{lastsync
} = $2;
565 sub write_var_lib_syncstatus
{
566 my ($filename, $fh, $data) = @_;
568 print $fh "# Cluster sync status (CID:TIME:)\n\n";
570 foreach my $cid (keys %$data) {
571 my $stime = $data->{$cid}->{lastsync
};
572 print $fh "$cid:$stime:\n";
578 sub read_etc_hostname
{
579 my ($filename, $fd) = @_;
581 my $hostname = <$fd>;
588 sub write_etc_hostname
{
589 my ($filename, $fh, $hostname) = @_;
591 print $fh "$hostname\n";
596 sub read_root_dotforward
{
597 my ($filename, $fh) = @_;
600 while (defined ($line = <$fh>)) {
602 next if $line =~ m/^\s*$/;
609 sub write_root_dotforward
{
610 my ($filename, $fh, $mailto) = @_;
612 print $fh "$mailto\n";
617 sub read_etc_pve_cfg
{
618 my ($filename, $fh) = @_;
624 while (defined ($line = <$fh>)) {
626 next if $line =~ m/^\#/; # skip comments
627 next if $line =~ m/^\s*$/; # skip empty lines
629 if ($line =~ m/^([^\s:]+):\s*(.*\S)\s*$/) {
638 sub write_etc_pve_cfg
{
639 my ($filename, $fh, $data) = @_;
643 foreach my $k (keys %$data) {
644 print $fh "$k: $data->{$k}\n";
650 sub read_etc_pve_storagecfg
{
651 my ($filename, $fh) = @_;
653 return PVE
::Storage
::parse_config
($filename, $fh);
656 sub read_etc_pve_qemu_server_cfg
{
657 my ($filename, $fh) = @_;
663 while (defined ($line = <$fh>)) {
665 next if $line =~ m/^\#/; # skip comments
666 next if $line =~ m/^\s*$/; # skip empty lines
668 if ($line =~ m/^([^\s:]+):\s*(.*\S)\s*$/) {
677 sub write_etc_pve_qemu_server_cfg
{
678 my ($filename, $fh, $data) = @_;
682 foreach my $k (keys %$data) {
683 print $fh "$k: $data->{$k}\n";
689 sub read_etc_timezone
{
690 my ($filename, $fd) = @_;
692 my $timezone = <$fd>;
699 sub write_etc_timezone
{
700 my ($filename, $fh, $timezone) = @_;
702 print $fh "$timezone\n";
704 unlink ("/etc/localtime");
705 symlink ("/usr/share/zoneinfo/$timezone", "/etc/localtime");
710 sub __dowhash_to_dow
{
714 push @da, $num ?
1 : 'mon' if $d->{mon
};
715 push @da, $num ?
2 : 'tue' if $d->{tue
};
716 push @da, $num ?
3 : 'wed' if $d->{wed
};
717 push @da, $num ?
4 : 'thu' if $d->{thu
};
718 push @da, $num ?
5 : 'fri' if $d->{fri
};
719 push @da, $num ?
6 : 'sat' if $d->{sat
};
720 push @da, $num ?
7 : 'sun' if $d->{sun
};
722 return join ',', @da;
725 sub update_etc_crond_vzdump
{
726 my ($filename, $jobs, $data) = @_;
728 my $digest = $data->{digest
};
731 foreach my $jid (keys %$data) {
732 next if $jid !~ m/^JOB\d+$/;
733 $verify = 1 if defined ($jobs->{$jid});
734 my $d = $data->{$jid};
736 delete $jobs->{$jid};
741 if ($verify && (!$digest || ($digest ne $jobs->{digest
}))) {
742 die "unable to update a modified file '$filename'\n";
748 sub read_etc_crond_vzdump
{
749 my ($filename, $fh) = @_;
753 my $jid = 1; # we start at 1
756 my $sha1 = Digest
::SHA1-
>new;
760 my $dowmap = {mon
=> 1, tue
=> 2, wed
=> 3, thu
=> 4,
761 fri
=> 5, sat
=> 6, sun
=> 7};
762 my $rdowmap = { '1' => 'mon', '2' => 'tue', '3' => 'wed', '4' => 'thu',
763 '5' => 'fri', '6' => 'sat', '7' => 'sun', '0' => 'sun'};
765 while (defined ($line = <$fh>)) {
766 $sha1->add ($line); # compute digest
768 next if $line =~ m/^\s*$/;
769 next if $line =~ m/^\#/;
770 next if $line =~ m/^PATH\s*=/; # we always overwrite path
775 if ($line =~ m
|^(\d
+)\s
+(\d
+)\s
+\
*\s
+\
*\s
+(\S
+)\s
+root\s
+(/\S+/)?vzdump
(\s
+(.*))?
$|) {
783 # convenient startime can be used to sort jobs
784 $d->{starttime
} = sprintf ("%02d:%02d", $d->{hour
}, $d->{minute
});
786 $dow = '1,2,3,4,5,6,7' if $dow eq '*';
788 foreach my $day (split (/,/, $dow)) {
789 if ($day =~ m/^(mon|tue|wed|thu|fri|sat|sun)-(mon|tue|wed|thu|fri|sat|sun)$/i) {
790 for (my $i = $dowmap->{lc($1)}; $i <= $dowmap->{lc($2)}; $i++) {
791 my $r = $rdowmap->{$i};
795 } elsif ($day =~ m/^(mon|tue|wed|thu|fri|sat|sun|[0-7])$/i) {
796 $day = $rdowmap->{$day} if $day =~ m/\d/;
799 die "unable to parse day of week '$dow' in '$filename'\n";
805 my $opt_exclude_path;
806 my $opt_compress = 0;
816 local @ARGV = split /\s+/, $param;
817 if (!GetOptions
('all' => \
$opt_all,
818 'compress' => \
$opt_compress,
819 'mailto=s@' => \
$opt_mailto,
821 'suspend' =>\
$opt_suspend,
822 'snapshot' =>\
$opt_snap,
823 'quiet' =>\
$opt_quiet,
824 'node=i' =>\
$opt_node,
825 'storage=s' =>\
$opt_storage,
826 'dumpdir=s' => \
$opt_dumpdir)) {
828 die "unable to parse vzdump options in '$filename'\n";
831 $d->{mode
} = 'snapshot';
832 } elsif ($opt_suspend) {
833 $d->{mode
} = 'suspend';
834 } elsif ($opt_stop) {
838 $d->{compress
} = $opt_compress;
839 $d->{dumpdir
} = $opt_dumpdir;
840 $d->{storage
} = $opt_storage;
841 $d->{includeall
} = $opt_all;
842 $d->{mailto
} = $opt_mailto ?
join (' ', @$opt_mailto) : '';
843 $d->{node
} = $opt_node || 0;
846 foreach my $vmid (@ARGV) {
847 if ($vmid =~ m/^\d+$/) {
848 $vmlist .= $vmlist ?
" $vmid" : $vmid;
850 die "unable to parse vzdump options in '$filename'\n";
855 $d->{vmlist
} = $vmlist;
858 $d->{param
} = $param;
860 $d->{dow
} = __dowhash_to_dow
($d);
862 $res->{"JOB$jid"} = $d;
869 syslog
('err', "warning: $err");
871 $res->{"EJOB$ejid"} = { line
=> $line };
877 } elsif ($line =~ m
|^\S
+\s
+(\S
+)\s
+\S
+\s
+\S
+\s
+\S
+\s
+\S
+\s
+(\S
.*)$|) {
878 syslog
('err', "warning: malformed line in '$filename'");
879 $res->{"EJOB$ejid"} = { line
=> $line };
882 syslog
('err', "ignoring malformed line in '$filename'");
886 $res->{digest
} = $sha1->hexdigest;
891 sub write_etc_crond_vzdump
{
892 my ($filename, $fh, $data) = @_;
894 print $fh "# Atomatically generated file - do not edit\n\n";
896 print $fh "PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"\n\n";
900 foreach my $jid (keys %$data) {
901 next if $jid !~ m/^JOB\d+$/;
902 my $d = $data->{$jid};
905 $d->{starttime
} = sprintf ("%02d:%02d", $d->{hour
}, $d->{minute
}); # used to sort
908 if ($d->{mon
} && $d->{tue
} && $d->{wed
} && $d->{thu
} &&
909 $d->{fri
} && $d->{sat
} && $d->{sun
}) {
912 $dow = __dowhash_to_dow
($d, 1);
919 my $param = '--quiet';
920 $param .= " --node $d->{node}" if $d->{node
};
921 $param .= " --$d->{mode}" if $d->{mode
};
922 $param .= " --compress" if $d->{compress
};
923 $param .= " --dumpdir $d->{dumpdir}" if $d->{dumpdir
};
924 $param .= " --storage $d->{storage}" if $d->{storage
};
926 if (my $mailto = $d->{mailto
}) {
927 $mailto =~ s/[,;]/ /g;
928 foreach my $ma (split (/\s+/, $mailto)) {
929 $param .= " --mailto $ma";
933 $param .= " --all" if $d->{includeall
};
934 $param .= " $d->{vmlist}" if $d->{vmlist
};
936 $d->{param
} = $param;
940 foreach my $jid (sort { ($data->{$a}->{node
} <=> $data->{$b}->{node
}) ||
941 ($data->{$a}->{starttime
} cmp $data->{$b}->{starttime
}) ||
942 ($data->{$a}->{dow
} cmp $data->{$b}->{dow
}) ||
943 ($data->{$a}->{param
} cmp $data->{$b}->{param
})} @jids) {
945 my $d = $data->{$jid};
947 printf $fh "$d->{minute} $d->{hour} * * %-11s root vzdump $d->{param}\n", $d->{dow
};
951 print $fh "\n" if $found;
954 foreach my $jid (keys %$data) {
955 next if $jid !~ m/^EJOB\d+$/;
956 my $d = $data->{$jid};
957 next if !$d || !$d->{line
};
958 print $fh "$d->{line}\n";
962 print $fh "\n" if $found;
967 sub read_etc_network_interfaces
{
968 my ($filename, $fh) = @_;
976 if ($fd2 = IO
::File-
>new ("/proc/net/dev", "r")) {
977 while (defined ($line = <$fd2>)) {
979 if ($line =~ m/^\s*(eth[0-9]):.*/) {
980 $ifaces->{$1}->{exists} = 1;
986 # always add the vmbr0 bridge device
987 $ifaces->{vmbr0
}->{exists} = 1;
989 if ($fd2 = IO
::File-
>new ("/proc/net/if_inet6", "r")) {
990 while (defined ($line = <$fd2>)) {
992 if ($line =~ m/^[a-f0-9]{32}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+(eth\d+|vmbr\d+|bond\d+)$/) {
993 $ifaces->{$1}->{active
} = 1;
1001 while (defined ($line = <$fh>)) {
1003 next if $line =~ m/^#/;
1005 if ($line =~ m/^auto\s+(.*)$/) {
1006 my @aa = split (/\s+/, $1);
1008 foreach my $a (@aa) {
1009 $ifaces->{$a}->{autostart
} = 1;
1012 } elsif ($line =~ m/^iface\s+(\S+)\s+inet\s+(\S+)\s*$/) {
1014 $ifaces->{$i}->{type
} = $2;
1015 while (defined ($line = <$fh>) && ($line =~ m/^\s+((\S+)\s+(.+))$/)) {
1017 my ($id, $value) = ($2, $3);
1018 if (($id eq 'address') || ($id eq 'netmask') || ($id eq 'broadcast')) {
1019 $ifaces->{$i}->{$id} = $value;
1020 } elsif ($id eq 'gateway') {
1021 $ifaces->{$i}->{$id} = $value;
1023 } elsif ($id eq 'slaves') {
1024 foreach my $p (split (/\s+/, $value)) {
1025 next if $p eq 'none';
1026 $ifaces->{$i}->{$id}->{$p} = 1;
1028 } elsif ($id eq 'bridge_ports') {
1029 foreach my $p (split (/\s+/, $value)) {
1030 next if $p eq 'none';
1031 $ifaces->{$i}->{$id}->{$p} = 1;
1033 } elsif ($id eq 'bridge_stp') {
1034 if ($value =~ m/^\s*(on|yes)\s*$/i) {
1035 $ifaces->{$i}->{$id} = 'on';
1037 $ifaces->{$i}->{$id} = 'off';
1039 } elsif ($id eq 'bridge_fd') {
1040 $ifaces->{$i}->{$id} = $value;
1041 } elsif ($id eq 'bond_miimon') {
1042 $ifaces->{$i}->{$id} = $value;
1043 } elsif ($id eq 'bond_mode') {
1045 foreach my $bm (keys %$bond_modes) {
1046 my $id = $bond_modes->{$bm};
1047 if ($id eq $value) {
1052 $ifaces->{$i}->{$id} = $value;
1054 push @{$ifaces->{$i}->{options
}}, $option;
1061 $ifaces->{vmbr0
}->{gateway
} = '';
1064 if (!$ifaces->{lo
}) {
1065 $ifaces->{lo
}->{type
} = 'loopback';
1066 $ifaces->{lo
}->{autostart
} = 1;
1069 foreach my $iface (keys %$ifaces) {
1070 if ($iface =~ m/^bond\d+$/) {
1072 } elsif ($iface =~ m/^vmbr\d+$/) {
1073 if (!defined ($ifaces->{$iface}->{bridge_fd
})) {
1074 $ifaces->{$iface}->{bridge_fd
} = 0;
1076 if (!defined ($ifaces->{$iface}->{bridge_stp
})) {
1077 $ifaces->{$iface}->{bridge_stp
} = 'off';
1079 } elsif ($iface =~ m/^(\S+):\d+$/) {
1080 if (defined ($ifaces->{$1})) {
1081 $ifaces->{$iface}->{exists} = $ifaces->{$1}->{exists};
1083 $ifaces->{$1}->{exists} = 0;
1084 $ifaces->{$iface}->{exists} = 0;
1088 $ifaces->{$iface}->{type
} = 'manual' if !$ifaces->{$iface}->{type
};
1094 sub __print_interface
{
1095 my ($fh, $ifaces, $iface) = @_;
1097 return if !$ifaces->{$iface}->{type
};
1099 if ($ifaces->{$iface}->{autostart
}) {
1100 print $fh "auto $iface\n";
1102 print $fh "iface $iface inet $ifaces->{$iface}->{type}\n";
1103 print $fh "\taddress $ifaces->{$iface}->{address}\n" if $ifaces->{$iface}->{address
};
1104 print $fh "\tnetmask $ifaces->{$iface}->{netmask}\n" if $ifaces->{$iface}->{netmask
};
1105 print $fh "\tgateway $ifaces->{$iface}->{gateway}\n" if $ifaces->{$iface}->{gateway
};
1106 print $fh "\tbroadcast $ifaces->{$iface}->{broadcast}\n" if $ifaces->{$iface}->{broadcast
};
1108 if ($ifaces->{$iface}->{bridge_ports
} || ($iface =~ m/^vmbr\d+$/)) {
1110 if ($ifaces->{$iface}->{bridge_ports
}) {
1111 $ports = join (' ', sort keys %{$ifaces->{$iface}->{bridge_ports
}});
1113 $ports = 'none' if !$ports;
1114 print $fh "\tbridge_ports $ports\n";
1117 if ($ifaces->{$iface}->{bridge_stp
} || ($iface =~ m/^vmbr\d+$/)) {
1118 my $v = $ifaces->{$iface}->{bridge_stp
};
1119 $v = defined ($v) ?
$v : 'off';
1120 print $fh "\tbridge_stp $v\n";
1123 if (defined ($ifaces->{$iface}->{bridge_fd
}) || ($iface =~ m/^vmbr\d+$/)) {
1124 my $v = $ifaces->{$iface}->{bridge_fd
};
1125 $v = defined ($v) ?
$v : 0;
1126 print $fh "\tbridge_fd $v\n";
1129 if ($ifaces->{$iface}->{slaves
} || ($iface =~ m/^bond\d+$/)) {
1131 if ($ifaces->{$iface}->{slaves
}) {
1132 $slaves = join (' ', sort keys %{$ifaces->{$iface}->{slaves
}});
1134 $slaves = 'none' if !$slaves;
1135 print $fh "\tslaves $slaves\n";
1138 if (defined ($ifaces->{$iface}->{'bond_miimon'}) || ($iface =~ m/^bond\d+$/)) {
1139 my $v = $ifaces->{$iface}->{'bond_miimon'};
1140 $v = defined ($v) ?
$v : 100;
1141 print $fh "\tbond_miimon $v\n";
1144 if (defined ($ifaces->{$iface}->{'bond_mode'}) || ($iface =~ m/^bond\d+$/)) {
1145 my $v = $ifaces->{$iface}->{'bond_mode'};
1146 $v = defined ($v) ?
$v : 'balance-rr';
1147 print $fh "\tbond_mode $v\n";
1150 foreach my $option (@{$ifaces->{$iface}->{options
}}) {
1151 print $fh "\t$option\n";
1157 sub write_etc_network_interfaces
{
1158 my ($filename, $fh, $ifaces) = @_;
1160 print $fh "# network interface settings\n";
1162 foreach my $iface (keys %$ifaces) {
1163 delete ($ifaces->{$iface}->{printed
});
1166 foreach my $t (('lo', 'eth', '')) {
1167 foreach my $iface (sort keys %$ifaces) {
1168 next if $ifaces->{$iface}->{printed
};
1169 next if $iface !~ m/^$t/;
1170 $ifaces->{$iface}->{printed
} = 1;
1171 __print_interface
($fh, $ifaces, $iface);
1178 sub read_etc_resolv_conf
{
1179 my ($filename, $fh) = @_;
1183 while (my $line = <$fh>) {
1185 if ($line =~ m/^search\s+(\S+)\s*/) {
1186 $res->{search
} = $1;
1187 } elsif ($line =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*/) {
1188 push @{$res->{nameservers
}}, $1;
1195 sub write_etc_resolv_conf
{
1196 my ($filename, $fh, $resolv) = @_;
1198 print $fh "search $resolv->{search}\n";
1203 foreach my $ns (@{$resolv->{nameservers
}}) {
1204 if ($ns ne '0.0.0.0' && !$written->{$ns}) {
1205 $written->{$ns} = 1;
1206 print $fh "nameserver $ns\n";
1211 $resolv->{nameservers
} = $nslist;
1215 sub ccache_default_writer
{
1216 my ($filename, $data) = @_;
1218 die "undefined config writer for '$filename' :ERROR";
1221 sub ccache_default_parser
{
1222 my ($filename, $srcfd) = @_;
1224 die "undefined config reader for '$filename' :ERROR";
1227 sub ccache_compute_diff
{
1228 my ($filename, $shadow) = @_;
1232 open (TMP
, "diff -b -N -u '$filename' '$shadow'|");
1234 while (my $line = <TMP
>) {
1240 $diff = undef if !$diff;
1246 my ($filename, $data, $full) = @_;
1248 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1250 die "file '$filename' not added :ERROR" if !defined ($ccache->{$filename});
1252 my $writer = $ccache->{$filename}->{writer
};
1254 my $realname = $filename;
1257 if ($shadow = $shadowfiles->{$filename}) {
1258 $realname = $shadow;
1261 my $fh = PVE
::AtomicFile-
>open($realname, "w") ||
1262 die "unable to open file '$realname' for writing - $! :ERROR";
1267 $res = &$writer ($filename, $fh, $data);
1270 $ccache->{$filename}->{version
} = undef;
1273 $fh->detach() if $err;
1279 if ($shadow && $full) {
1280 $diff = ccache_compute_diff
($filename, $shadow);
1284 return { data
=> $res, changes
=> $diff };
1291 my ($filename, $data, @args) = @_;
1293 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1295 my $update = $ccache->{$filename}->{update
};
1297 die "unable to update/merge data" if !$update;
1299 my $lkfn = "$filename.lock";
1301 if (!open (FLCK
, ">>$lkfn")) {
1302 die "unable to open lock file '$lkfn' - $?";
1305 if (!flock (FLCK
, LOCK_EX
)) {
1307 die "unable to aquire lock for file '$lkfn' - $?";
1314 my $olddata = read_file
($filename);
1317 my $res = &$update ($filename, $olddata, $data, @args);
1318 if (defined ($res)) {
1319 $newdata = write_file
($filename, $res);
1321 $newdata = $olddata;
1324 $newdata = $olddata;
1337 sub discard_changes
{
1338 my ($filename, $full) = @_;
1340 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1342 die "file '$filename' not added :ERROR" if !defined ($ccache->{$filename});
1344 if (my $copy = $shadowfiles->{$filename}) {
1348 return read_file
($filename, $full);
1352 my ($filename, $full) = @_;
1356 if ($filename =~ m
|^/etc/qemu-server
/\d
+\
.conf
$|) {
1357 $parser = \
&read_qmconfig
;
1358 } elsif ($filename =~ m
|^/etc/vz
/conf/\d
+\
.conf
$|) {
1359 $parser = \
&read_vzconfig
;
1361 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1363 die "file '$filename' not added :ERROR" if !defined ($ccache->{$filename});
1365 $parser = $ccache->{$filename}->{parser
};
1371 poll
() if $inotify; # read new inotify events
1373 $versions->{$filename} = 0 if !defined ($versions->{$filename});
1375 my $cver = $versions->{$filename};
1377 if (my $copy = $shadowfiles->{$filename}) {
1378 if ($fd = IO
::File-
>new ($copy, "r")) {
1381 $fd = IO
::File-
>new ($filename, "r");
1384 $fd = IO
::File-
>new ($filename, "r");
1387 my $acp = $ccache->{$filename}->{always_call_parser
};
1390 $ccache->{$filename}->{version
} = undef;
1391 $ccache->{$filename}->{data
} = undef;
1392 $ccache->{$filename}->{diff
} = undef;
1393 return undef if !$acp;
1396 my $noclone = $ccache->{$filename}->{noclone
};
1399 if (!$ccache->{$filename}->{nocache
} &&
1400 $inotify && $versions->{$filename} &&
1401 defined ($ccache->{$filename}->{data
}) &&
1402 defined ($ccache->{$filename}->{version
}) &&
1403 ($ccache->{$filename}->{readonce
} ||
1404 ($ccache->{$filename}->{version
} == $versions->{$filename}))) {
1407 if (!$noclone && ref ($ccache->{$filename}->{data
})) {
1408 $ret->{data
} = dclone
($ccache->{$filename}->{data
});
1410 $ret->{data
} = $ccache->{$filename}->{data
};
1412 $ret->{changes
} = $ccache->{$filename}->{diff
};
1414 return $full ?
$ret : $ret->{data
};
1420 $diff = ccache_compute_diff
($filename, $shadow);
1423 my $res = &$parser ($filename, $fd);
1425 if (!$ccache->{$filename}->{nocache
}) {
1426 $ccache->{$filename}->{version
} = $cver;
1429 # we cache data with references, so we always need to
1430 # dclone this data. Else the original data may get
1432 $ccache->{$filename}->{data
} = $res;
1435 $ccache->{$filename}->{diff
} = $diff;
1438 if (!$noclone && ref ($ccache->{$filename}->{data
})) {
1439 $ret->{data
} = dclone
($ccache->{$filename}->{data
});
1441 $ret->{data
} = $ccache->{$filename}->{data
};
1443 $ret->{changes
} = $ccache->{$filename}->{diff
};
1445 return $full ?
$ret : $ret->{data
};
1449 my ($id, $filename, $parser, $writer, $update, %options) = @_;
1451 die "file '$filename' already added :ERROR" if defined ($ccache->{$filename});
1452 die "ID '$id' already used :ERROR" if defined ($ccachemap->{$id});
1454 $ccachemap->{$id} = $filename;
1455 $ccache->{$filename}->{id
} = $id;
1457 $ccache->{$filename}->{parser
} = $parser || \
&ccache_default_parser
;
1458 $ccache->{$filename}->{writer
} = $writer || \
&ccache_default_writer
;
1459 $ccache->{$filename}->{update
} = $update;
1461 foreach my $opt (keys %options) {
1462 my $v = $options{$opt};
1463 if ($opt eq 'readonce') {
1464 $ccache->{$filename}->{$opt} = $v;
1465 } elsif ($opt eq 'nocache') {
1466 $ccache->{$filename}->{$opt} = $v;
1467 } elsif ($opt eq 'noclone') {
1468 # noclone flag for large read-only data chunks like aplinfo
1469 $ccache->{$filename}->{$opt} = $v;
1470 } elsif ($opt eq 'always_call_parser') {
1471 # when set, we call parser even when the file does not exists.
1472 # this allows the parser to return some default
1473 $ccache->{$filename}->{$opt} = $v;
1475 die "internal error - unsupported option '$opt'";
1483 return if !$inotify;
1485 if ($inotify_pid != $$) {
1486 syslog
('err', "got inotify poll request in wrong process - disabling inotify");
1489 1 while $inotify && $inotify->poll;
1494 foreach my $filename (keys %$ccache) {
1495 $ccache->{$filename}->{version
} = undef;
1496 $ccache->{$filename}->{data
} = undef;
1497 $ccache->{$filename}->{diff
} = undef;
1507 die "only one inotify instance allowed" if $inotify;
1509 $inotify = Linux
::Inotify2-
>new()
1510 || die "Unable to create new inotify object: $!";
1512 $inotify->blocking (0);
1517 foreach my $fn (keys %$ccache) {
1518 my $dir = dirname
($fn);
1519 my $base = basename
($fn);
1521 $dirhash->{$dir}->{$base} = $fn;
1523 if (my $sf = $shadowfiles->{$fn}) {
1524 $base = basename
($sf);
1525 $dir = dirname
($sf);
1526 $dirhash->{$dir}->{$base} = $fn; # change version of original file!
1530 # also get versions of qemu and openvz config files
1531 $dirhash->{"/etc/qemu-server"}->{_regex
} = '\d+\.conf';
1532 $dirhash->{"/etc/vz/conf"}->{_regex
} = '\d+\.conf';
1536 foreach my $dir (keys %$dirhash) {
1538 my $evlist = IN_MODIFY
|IN_ATTRIB
|IN_MOVED_FROM
|IN_MOVED_TO
|IN_DELETE
|IN_CREATE
;
1539 $inotify->watch ($dir, $evlist, sub {
1541 my $name = $e->name;
1543 if ($inotify_pid != $$) {
1544 syslog
('err', "got inotify event in wrong process");
1547 if ($e->IN_ISDIR || !$name) {
1551 if ($e->IN_Q_OVERFLOW) {
1552 syslog
('info', "got inotify overflow - flushing cache");
1557 if ($e->IN_UNMOUNT) {
1558 syslog
('err', "got 'unmount' event on '$name' - disabling inotify");
1561 if ($e->IN_IGNORED) {
1562 syslog
('err', "got 'ignored' event on '$name' - disabling inotify");
1566 my $re = $dirhash->{$dir}->{_regex
};
1567 if ($re && ($name =~ m
|^$re$|)) {
1569 my $fn = "$dir/$name";
1571 #print "VERSION:$fn:$versions->{$fn}\n";
1573 } elsif (my $fn = $dirhash->{$dir}->{$name}) {
1576 #print "VERSION:$fn:$versions->{$fn}\n";
1581 foreach my $dir (keys %$dirhash) {
1582 foreach my $name (keys %{$dirhash->{$dir}}) {
1583 if ($name eq '_regex') {
1584 my $re = $dirhash->{$dir}->{_regex
};
1585 if (my $fd = IO
::Dir-
>new ($dir)) {
1586 while (defined(my $de = $fd->read)) {
1587 if ($de =~ m/^$re$/) {
1588 my $fn = "$dir/$de";
1589 $versions->{$fn}++; # init with version
1590 #print "init:$fn:$versions->{$fn}\n";
1595 my $fn = $dirhash->{$dir}->{$name};
1596 $versions->{$fn}++; # init with version
1597 #print "init:$fn:$versions->{$fn}\n";
1604 add_file
('hostname', "/etc/hostname",
1605 \
&read_etc_hostname
,
1606 \
&write_etc_hostname
);
1608 add_file
('syncstatus', "/var/lib/pve-manager/syncstatus",
1609 \
&read_var_lib_syncstatus
,
1610 \
&write_var_lib_syncstatus
);
1612 add_file
('vzlist', "/var/lib/pve-manager/vzlist",
1613 \
&read_var_lib_vzlist
,
1614 \
&write_var_lib_vzlist
,
1615 \
&update_var_lib_vzlist
);
1617 add_file
('vmops', "/var/lib/pve-manager/vmops",
1618 \
&read_var_lib_vmops
,
1619 \
&write_var_lib_vmops
,
1620 \
&update_var_lib_vmops
);
1622 add_file
('interfaces', "/etc/network/interfaces",
1623 \
&read_etc_network_interfaces
,
1624 \
&write_etc_network_interfaces
);
1626 add_file
('resolvconf', "/etc/resolv.conf",
1627 \
&read_etc_resolv_conf
,
1628 \
&write_etc_resolv_conf
);
1630 add_file
('timezone', "/etc/timezone",
1631 \
&read_etc_timezone
,
1632 \
&write_etc_timezone
);
1634 add_file
('pvecfg', "/etc/pve/pve.cfg",
1636 \
&write_etc_pve_cfg
);
1638 add_file
('storagecfg', "/etc/pve/storage.cfg",
1639 \
&read_etc_pve_storagecfg
, undef, undef,
1640 always_call_parser
=> 1);
1642 add_file
('rootrsapubkey', "/root/.ssh/id_rsa.pub",
1645 add_file
('hostrsapubkey', "/etc/ssh/ssh_host_rsa_key.pub",
1648 add_file
('clustercfg', "/etc/pve/cluster.cfg",
1649 \
&PVE
::Storage
::read_cluster_config
);
1651 add_file
('newclustercfg', "/etc/pve/master/cluster.cfg",
1652 \
&PVE
::Storage
::read_cluster_config
);
1654 add_file
('qemuservercfg', "/etc/pve/qemu-server.cfg",
1655 \
&read_etc_pve_qemu_server_cfg
,
1656 \
&write_etc_pve_qemu_server_cfg
);
1658 add_file
('vzdump', "/etc/cron.d/vzdump",
1659 \
&read_etc_crond_vzdump
,
1660 \
&write_etc_crond_vzdump
,
1661 \
&update_etc_crond_vzdump
);
1663 add_file
('aplinfo', "/var/lib/pve-manager/apl-available",
1664 \
&read_aplinfo
, undef, undef,
1667 add_file
('dotforward', "/root/.forward",
1668 \
&read_root_dotforward
,
1669 \
&write_root_dotforward
);
1671 add_file
('usercfg', "/etc/pve/user.cfg",
1672 \
&PVE
::AccessControl
::parse_config
);
1674 # persistent counter implementation
1675 add_file
('pcounter', "/var/lib/pve-manager/pcounter",