]> git.proxmox.com Git - pve-manager.git/blame - lib/PVE.old/Config.pm
imported from svn 'pve-manager/pve2'
[pve-manager.git] / lib / PVE.old / Config.pm
CommitLineData
aff192e6
DM
1package PVE::Config;
2
3use strict;
4use IO::File;
5use IO::Dir;
6use PVE::AtomicFile;
7use File::stat;
8use File::Basename;
9use PVE::Utils;
10use Fcntl ':flock';
11use PVE::SafeSyslog;
12use Storable qw(dclone);
13use Getopt::Long;
14use Digest::SHA1;
15use Linux::Inotify2;
16use PVE::QemuServer;
17use PVE::Storage;
18use PVE::AccessControl;
19
20my $ccache;
21my $ccachemap;
22my $inotify;
23my $inotify_pid = 0;
24my $versions;
25
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)
29
30# Note: please close the inotify handle after you fork
31
32my $shadowfiles = {
33 '/etc/network/interfaces' => '/etc/network/interfaces.new',
34};
35
36
37sub soap_host_port {
38 return ('127.0.0.1', 83);
39}
40
41my @zoneinfo;
42
43sub zoneinfo {
44 if (@zoneinfo) {
45 return @zoneinfo;
46 }
47
48 my $line;
49
50 open (TMP, "</usr/share/zoneinfo/zone.tab");
51
52 while ($line = <TMP>) {
53 chomp $line;
54 if (!($line =~ m|^[A-Z][A-Z]\s+\S+\s+(\S+)/(\S+).*|)) {
55 next;
56 }
57
58 if ($1 && $2) {
59 push @zoneinfo, "$1/$2";
60 }
61 }
62
63 close (TMP);
64
65 @zoneinfo = sort (@zoneinfo);
66
67 return @zoneinfo;
68}
69
70my $bond_modes = { 'balance-rr' => 0,
71 'active-backup' => 1,
72 'balance-xor' => 2,
73 'broadcast' => 3,
74 '802.3ad' => 4,
75 'balance-tlb' => 5,
76 'balance-alb' => 6,
77 };
78
79sub get_bond_modes {
80 return $bond_modes;
81}
82
83sub parse_netif {
84 my $data = shift;
85
86 my $res = {};
87 foreach my $iface (split (/;/, $data)) {
88 my $d = {};
89 foreach my $pv (split (/,/, $iface)) {
90 if ($pv =~ m/^(ifname|mac|bridge|host_ifname|host_mac)=(.+)$/) {
91 $d->{$1} = $2;
92 }
93 }
94 if ($d->{ifname}) {
95 $d->{raw} = $data;
96 $res->{$d->{ifname}} = $d;
97 } else {
98 die "unable to parse --netif value";
99 }
100 }
101
102 return $res;
103}
104
105sub read_aplinfo {
106 my ($filename, $fh, $update) = @_;
107
108 local $/ = "";
109
110 my $list = {};
111
112 while (my $rec = <$fh>) {
113 chomp $rec;
114
115 my $res = {};
116
117 while ($rec) {
118
119 if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) {
120 $res->{headline} = $1;
121 my $long = $2;
122 $long =~ s/\n\s+/ /g;
123 $long =~ s/^\s+//g;
124 $long =~ s/\s+$//g;
125 $res->{description} = $long;
126 } elsif ($rec =~ s/^Version:\s*(.*\S)\s*\n//i) {
127 my $version = $1;
128 if ($version =~ m/^(\d[a-zA-Z0-9\.\+\-\:\~]*)-(\d+)$/) {
129 $res->{version} = $version;
130 } else {
131 my $msg = "unable to parse appliance record: version = '$version'";
132 $update ? die "$msg\n" : syslog ('err', $msg);
133 }
134 } elsif ($rec =~ s/^Type:\s*(.*\S)\s*\n//i) {
135 my $type = $1;
136 if ($type =~ m/^(openvz)$/) {
137 $res->{type} = $type;
138 } else {
139 my $msg = "unable to parse appliance record: unknown type '$type'";
140 $update ? die "$msg\n" : syslog ('err', $msg);
141 }
142 } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) {
143 $res->{lc $1} = $2;
144 } else {
145 my $msg = "unable to parse appliance record: $rec";
146 $update ? die "$msg\n" : syslog ('err', $msg);
147 $res = {};
148 last;
149 }
150 }
151
152 if ($res->{'package'} eq 'pve-web-news' && $res->{description}) {
153 $list->{'all'}->{$res->{'package'}} = $res;
154 next;
155 }
156
157 $res->{section} = 'unknown' if !$res->{section};
158
159 if ($res->{'package'} && $res->{type} && $res->{os} && $res->{version} &&
160 $res->{infopage}) {
161 my $template;
162 if ($res->{location}) {
163 $template = $res->{location};
164 $template =~ s|.*/([^/]+.tar.gz)|$1|;
165 } else {
166 $template = "$res->{os}-$res->{package}_$res->{version}_i386.tar.gz";
167 $template =~ s/$res->{os}-$res->{os}-/$res->{os}-/;
168 }
169 $res->{template} = $template;
170 $list->{$res->{section}}->{$template} = $res;
171 $list->{'all'}->{$template} = $res;
172 } else {
173 my $msg = "found incomplete appliance records";
174 $update ? die "$msg\n" : syslog ('err', $msg);
175 }
176 }
177
178 return $list;
179}
180
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.
184sub update_etc_hosts {
185
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};
190
191 my $domain = $rconf->{search};
192
193 my $filename = "/etc/hosts";
194 my $fh = IO::File->new($filename, "r") ||
195 die "unable to open file '$filename' - $! :ERROR";
196
197 my $outfh = PVE::AtomicFile->open($filename, "w") ||
198 die "unable to open file '$filename' for writing - $! :ERROR";
199
200 eval {
201 my $line;
202 while (defined ($line = <$fh>)) {
203 chomp $line;
204 if ($line =~ m/^\s*127.0.0.1\s/) {
205 print $outfh "$line\n";
206 print $outfh "$localip $hostname.$domain $hostname pvelocalhost\n";
207 next;
208 }
209
210 if ($line =~ m/^\s*(\d+\.\d+\.\d+\.\d+\s+.*\S)\s*/) {
211 my $found = 0;
212 foreach my $n (split (/\s+/, $1)) {
213 my $e = lc ($n);
214 if ($e eq lc ($hostname) ||
215 $e eq lc ($localip) ||
216 $e eq 'pvelocalhost') {
217 $found = 1;
218 last;
219 }
220 }
221 next if $found;
222 }
223
224 print $outfh "$line\n";
225 }
226
227 $fh->close();
228
229 $outfh->close(1);
230 };
231
232 my $err = $@;
233
234 die $err if $err;
235}
236
237sub get_qmconfig {
238 my $vmid = shift;
239
240 return read_file ("/etc/qemu-server/$vmid.conf");
241}
242
243sub get_veconfig {
244 my $veid = shift;
245
246 return read_file ("/etc/vz/conf/$veid.conf");
247}
248
249sub read_qmconfig {
250 my ($filename, $fh) = @_;
251
252 $filename =~ m|/(\d+)\.conf$|
253 || die "got strange filename '$filename'";
254
255 my $storecfg = read_file ("storagecfg");
256
257 return PVE::QemuServer::parse_config ($filename, $fh, $storecfg);
258}
259
260sub read_vzconfig {
261 my ($filename, $fh) = @_;
262
263 $filename =~ m|/(\d+)\.conf$|
264 || die "got strange filename '$filename'";
265
266 my $data = {};
267 while (defined (my $line = <$fh>)) {
268 next if $line =~ m/^#/;
269 next if $line =~ m/^\s*$/;
270
271 if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
272 my $name = lc ($1);
273 my $text = $2;
274
275 if ($text =~ m/^(\d+):(\d+)$/) {
276 my $bar = $1;
277 my $lim = $2;
278
279 $data->{$name}->{bar} = $bar;
280 $data->{$name}->{lim} = $lim;
281 } else {
282 $data->{$name}->{value} = $text;
283 }
284 } else {
285 die "unable to parse config line: $line\n";
286 }
287 }
288
289 return $data;
290}
291
292sub read_rsapubkey {
293 my ($filename, $fh) = @_;
294
295 my $line;
296
297 1 while (defined ($line = <$fh>) && ($line !~ m/^.*ssh-rsa\s/));
298
299 my $rsapubkey = $line;
300
301 $rsapubkey =~ s/^.*ssh-rsa\s+//i;
302 $rsapubkey =~ s/\s+root\@\S+\s*$//i;
303 $rsapubkey =~ s/\s+$//;
304
305 die "strange key format - not base64 encoded"
306 if $rsapubkey !~ m/^[A-Za-z0-9\+\/]+={0,2}$/;
307
308 die "unable to read '$filename'" if !$rsapubkey;
309
310 return $rsapubkey;
311}
312
313sub read_pcounter {
314 my ($filename, $fh) = @_;
315
316 my $res = {};
317
318 my $line;
319
320 while (defined ($line = <$fh>)) {
321 chomp $line;
322 next if $line =~ m/^\s*$/;
323
324 if ($line =~ m/^counter:(\S+):(\d+):$/) {
325 $res->{$1} = $2;
326 } else {
327 syslog ('err', "warning: unable to parse file '$filename'");
328 }
329 }
330
331 return $res;
332}
333
334sub write_pcounter {
335 my ($filename, $fh, $data) = @_;
336
337 foreach my $cn (keys %$data) {
338 print $fh "counter:$cn:$data->{$cn}:\n";
339 }
340
341 return $data;
342}
343
344sub update_pcounter {
345 my ($filename, $data, $counter) = @_;
346
347 $data->{$counter}++;
348
349 return $data;
350}
351
352sub read_var_lib_vmops {
353 my ($filename, $fh) = @_;
354
355 my $line;
356
357 my $res = {};
358
359 while (defined ($line = <$fh>)) {
360 chomp $line;
361
362 my $upid_hash;
363
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;
369 }
370 }
371
372 return $res;
373}
374
375sub write_var_lib_vmops {
376 my ($filename, $fh, $data) = @_;
377
378 foreach my $ckey (sort keys %$data) {
379 next if $ckey !~ m/^CID_(\d+)$/;
380 my $cid = $1;
381 my $vzl = $data->{$ckey};
382
383 foreach my $vekey (sort keys %$vzl) {
384 next if $vekey !~ m/^VEID_(\d+)$/;
385 my $veid = $1;
386
387 my $upid = PVE::Utils::upid_encode ($vzl->{$vekey});
388 print $fh "$upid\n";
389 }
390 }
391
392 return $data;
393}
394
395sub update_var_lib_vmops {
396 my ($filename, $vmops, $upid) = @_;
397
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;
402 }
403
404 return $vmops;
405}
406
407sub read_var_lib_vzlist {
408 my ($filename, $fh) = @_;
409
410 my $line;
411
412 my $res = {};
413
414 my $cid;
415
416 while (defined ($line = <$fh>)) {
417 chomp $line;
418
419 next if $line =~ m/^\#/; # skip comments
420 next if $line =~ m/^\s*$/; # skip empty lines
421
422 if ($line =~ m/^CID:(\d+):(\d+):(\d+):\S*$/) {
423 $cid = $1;
424 $res->{"CID_$cid"}->{lasttime} = $2;
425 $res->{"CID_$cid"}->{version} = $3;
426 next;
427 }
428
429 if (!defined ($cid)) {
430 warn "unable to parse line - undefined cluster ID: $line";
431 }
432
433 if ($line =~ m/^(\d+):([a-z]+):(\d+):(\S+):(\S+):(\S+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):$/) {
434 my $d = {};
435 $d->{type} = $2;
436 $d->{nproc} = $3;
437 $d->{status} = $4;
438 $d->{ip} = $5;
439 $d->{name} = $6;
440 $d->{mem} = $7;
441 $d->{maxmem} = $8;
442 $d->{disk} = $9;
443 $d->{maxdisk} = $10;
444 $d->{pctcpu} = $11;
445 $d->{uptime} = $12;
446 $d->{relcpu} = $13;
447
448 $res->{"CID_$cid"}->{"VEID_$1"} = $d;
449 } else {
450 warn "unable to parse line: $line";
451 }
452 }
453
454 return $res;
455}
456
457sub write_var_lib_vzlist {
458 my ($filename, $fh, $data) = @_;
459
460 print $fh "# Cluster wide VZ status\n\n";
461
462 foreach my $ckey (sort keys %$data) {
463 next if $ckey !~ m/^CID_(\d+)$/;
464 my $cid = $1;
465 my $vzl = $data->{$ckey};
466
467 print $fh "CID:$cid:$vzl->{lasttime}:$vzl->{version}:\n";
468
469 foreach my $vekey (sort keys %$vzl) {
470 my $d = $vzl->{$vekey};
471 next if $vekey !~ m/^VEID_(\d+)$/;
472 my $veid = $1;
473
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";
476 }
477
478 print $fh "\n";
479 }
480
481 return $data;
482}
483
484sub update_var_lib_vzlist {
485 my ($filename, $vzlist, $data, $cid) = @_;
486
487 my $old = $vzlist->{"CID_$cid"};
488
489 if ($old) {
490
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)
494
495 if (($old->{version} > $data->{version}) &&
496 ($old->{lasttime} >= $data->{lasttime})) {
497 return;
498 }
499 }
500
501 my $ckey = "CID_$cid";
502
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;
512 }
513 }
514
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;
524 }
525 }
526
527 $vzlist->{$ckey} = $data;
528
529 # remove non-existing cluster nodes
530 my $ccfg = read_file ("clustercfg");
531 PVE::Utils::foreach_cid ($vzlist, sub {
532 my ($cid, undef, $ckey) = @_;
533 if ($ccfg) {
534 delete $vzlist->{$ckey} if !defined ($ccfg->{$ckey});
535 } else {
536 delete $vzlist->{$ckey} if $cid != 0;
537 }
538 });
539
540 return $vzlist;
541}
542
543
544sub read_var_lib_syncstatus {
545 my ($filename, $fh) = @_;
546
547 my $line;
548
549 my $res = {};
550
551 while (defined ($line = <$fh>)) {
552 chomp $line;
553
554 next if $line =~ m/^\#/; # skip comments
555 next if $line =~ m/^\s*$/; # skip empty lines
556
557 if ($line =~ m/^(\d+):(\d+):$/) {
558 $res->{$1}->{lastsync} = $2;
559 }
560 }
561
562 return $res;
563}
564
565sub write_var_lib_syncstatus {
566 my ($filename, $fh, $data) = @_;
567
568 print $fh "# Cluster sync status (CID:TIME:)\n\n";
569
570 foreach my $cid (keys %$data) {
571 my $stime = $data->{$cid}->{lastsync};
572 print $fh "$cid:$stime:\n";
573 }
574
575 return $data;
576}
577
578sub read_etc_hostname {
579 my ($filename, $fd) = @_;
580
581 my $hostname = <$fd>;
582
583 chomp $hostname;
584
585 return $hostname;
586}
587
588sub write_etc_hostname {
589 my ($filename, $fh, $hostname) = @_;
590
591 print $fh "$hostname\n";
592
593 return $hostname;
594}
595
596sub read_root_dotforward {
597 my ($filename, $fh) = @_;
598
599 my $line;
600 while (defined ($line = <$fh>)) {
601 chomp $line;
602 next if $line =~ m/^\s*$/;
603 return $line;
604 }
605
606 return undef;
607}
608
609sub write_root_dotforward {
610 my ($filename, $fh, $mailto) = @_;
611
612 print $fh "$mailto\n";
613
614 return $mailto;
615}
616
617sub read_etc_pve_cfg {
618 my ($filename, $fh) = @_;
619
620 my $line;
621
622 my $res = {};
623
624 while (defined ($line = <$fh>)) {
625 chomp $line;
626 next if $line =~ m/^\#/; # skip comments
627 next if $line =~ m/^\s*$/; # skip empty lines
628
629 if ($line =~ m/^([^\s:]+):\s*(.*\S)\s*$/) {
630 $res->{lc($1)} = $2;
631 }
632
633 }
634
635 return $res;
636}
637
638sub write_etc_pve_cfg {
639 my ($filename, $fh, $data) = @_;
640
641 return if !$data;
642
643 foreach my $k (keys %$data) {
644 print $fh "$k: $data->{$k}\n";
645 }
646
647 return $data;
648}
649
650sub read_etc_pve_storagecfg {
651 my ($filename, $fh) = @_;
652
653 return PVE::Storage::parse_config ($filename, $fh);
654}
655
656sub read_etc_pve_qemu_server_cfg {
657 my ($filename, $fh) = @_;
658
659 my $line;
660
661 my $res = {};
662
663 while (defined ($line = <$fh>)) {
664 chomp $line;
665 next if $line =~ m/^\#/; # skip comments
666 next if $line =~ m/^\s*$/; # skip empty lines
667
668 if ($line =~ m/^([^\s:]+):\s*(.*\S)\s*$/) {
669 $res->{lc($1)} = $2;
670 }
671
672 }
673
674 return $res;
675}
676
677sub write_etc_pve_qemu_server_cfg {
678 my ($filename, $fh, $data) = @_;
679
680 return if !$data;
681
682 foreach my $k (keys %$data) {
683 print $fh "$k: $data->{$k}\n";
684 }
685
686 return $data;
687}
688
689sub read_etc_timezone {
690 my ($filename, $fd) = @_;
691
692 my $timezone = <$fd>;
693
694 chomp $timezone;
695
696 return $timezone;
697}
698
699sub write_etc_timezone {
700 my ($filename, $fh, $timezone) = @_;
701
702 print $fh "$timezone\n";
703
704 unlink ("/etc/localtime");
705 symlink ("/usr/share/zoneinfo/$timezone", "/etc/localtime");
706
707 return $timezone;
708}
709
710sub __dowhash_to_dow {
711 my ($d, $num) = @_;
712
713 my @da = ();
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};
721
722 return join ',', @da;
723}
724
725sub update_etc_crond_vzdump {
726 my ($filename, $jobs, $data) = @_;
727
728 my $digest = $data->{digest};
729
730 my $verify = 0;
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};
735 if (!$d) {
736 delete $jobs->{$jid};
737 } else {
738 $jobs->{$jid} = $d;
739 }
740 }
741 if ($verify && (!$digest || ($digest ne $jobs->{digest}))) {
742 die "unable to update a modified file '$filename'\n";
743 }
744
745 return $jobs;
746}
747
748sub read_etc_crond_vzdump {
749 my ($filename, $fh) = @_;
750
751 my $line;
752
753 my $jid = 1; # we start at 1
754 my $ejid = 0;
755
756 my $sha1 = Digest::SHA1->new;
757
758 my $res = {};
759
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'};
764
765 while (defined ($line = <$fh>)) {
766 $sha1->add ($line); # compute digest
767 chomp $line;
768 next if $line =~ m/^\s*$/;
769 next if $line =~ m/^\#/;
770 next if $line =~ m/^PATH\s*=/; # we always overwrite path
771
772 my $d;
773 my $err;
774
775 if ($line =~ m|^(\d+)\s+(\d+)\s+\*\s+\*\s+(\S+)\s+root\s+(/\S+/)?vzdump(\s+(.*))?$|) {
776
777 eval {
778 $d->{minute} = $1;
779 $d->{hour} = $2;
780 my $dow = $3;
781 my $param = $6;
782
783 # convenient startime can be used to sort jobs
784 $d->{starttime} = sprintf ("%02d:%02d", $d->{hour}, $d->{minute});
785
786 $dow = '1,2,3,4,5,6,7' if $dow eq '*';
787
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};
792 $d->{$r} = 1;
793 }
794
795 } elsif ($day =~ m/^(mon|tue|wed|thu|fri|sat|sun|[0-7])$/i) {
796 $day = $rdowmap->{$day} if $day =~ m/\d/;
797 $d->{lc($day)} = 1;
798 } else {
799 die "unable to parse day of week '$dow' in '$filename'\n";
800 $err = 1;
801 }
802 }
803
804 my $opt_all;
805 my $opt_exclude_path;
806 my $opt_compress = 0;
807 my $opt_dumpdir;
808 my $opt_storage;
809 my $opt_mailto;
810 my $opt_stop;
811 my $opt_suspend;
812 my $opt_snap;
813 my $opt_node;
814 my $opt_quiet;
815
816 local @ARGV = split /\s+/, $param;
817 if (!GetOptions ('all' => \$opt_all,
818 'compress' => \$opt_compress,
819 'mailto=s@' => \$opt_mailto,
820 'stop' =>\$opt_stop,
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)) {
827
828 die "unable to parse vzdump options in '$filename'\n";
829 } else {
830 if ($opt_snap) {
831 $d->{mode} = 'snapshot';
832 } elsif ($opt_suspend) {
833 $d->{mode} = 'suspend';
834 } elsif ($opt_stop) {
835 $d->{mode} = 'stop';
836 }
837
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;
844
845 my $vmlist = '';
846 foreach my $vmid (@ARGV) {
847 if ($vmid =~ m/^\d+$/) {
848 $vmlist .= $vmlist ? " $vmid" : $vmid;
849 } else {
850 die "unable to parse vzdump options in '$filename'\n";
851 }
852
853 }
854
855 $d->{vmlist} = $vmlist;
856 }
857
858 $d->{param} = $param;
859
860 $d->{dow} = __dowhash_to_dow ($d);
861
862 $res->{"JOB$jid"} = $d;
863 };
864
865 my $err = $@;
866
867
868 if ($err) {
869 syslog ('err', "warning: $err");
870
871 $res->{"EJOB$ejid"} = { line => $line };
872 $ejid++;
873
874 } else {
875 $jid++;
876 }
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 };
880 $ejid++;
881 } else {
882 syslog ('err', "ignoring malformed line in '$filename'");
883 }
884 }
885
886 $res->{digest} = $sha1->hexdigest;
887
888 return $res;
889}
890
891sub write_etc_crond_vzdump {
892 my ($filename, $fh, $data) = @_;
893
894 print $fh "# Atomatically generated file - do not edit\n\n";
895
896 print $fh "PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"\n\n";
897
898 my @jids;
899
900 foreach my $jid (keys %$data) {
901 next if $jid !~ m/^JOB\d+$/;
902 my $d = $data->{$jid};
903 next if !$d;
904 push @jids, $jid;
905 $d->{starttime} = sprintf ("%02d:%02d", $d->{hour}, $d->{minute}); # used to sort
906
907 my $dow;
908 if ($d->{mon} && $d->{tue} && $d->{wed} && $d->{thu} &&
909 $d->{fri} && $d->{sat} && $d->{sun}) {
910 $dow = '*';
911 } else {
912 $dow = __dowhash_to_dow ($d, 1);
913 }
914
915 $dow = '*' if !$dow;
916
917 $d->{dow} = $dow;
918
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};
925
926 if (my $mailto = $d->{mailto}) {
927 $mailto =~ s/[,;]/ /g;
928 foreach my $ma (split (/\s+/, $mailto)) {
929 $param .= " --mailto $ma";
930 }
931 }
932
933 $param .= " --all" if $d->{includeall};
934 $param .= " $d->{vmlist}" if $d->{vmlist};
935
936 $d->{param} = $param;
937 }
938
939 my $found = 0;
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) {
944
945 my $d = $data->{$jid};
946
947 printf $fh "$d->{minute} $d->{hour} * * %-11s root vzdump $d->{param}\n", $d->{dow};
948 $found = 1;
949 }
950
951 print $fh "\n" if $found;
952
953 $found = 0;
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";
959 $found = 1;
960 }
961
962 print $fh "\n" if $found;
963
964 return $data;
965}
966
967sub read_etc_network_interfaces {
968 my ($filename, $fh) = @_;
969
970 my $ifaces = {};
971
972 my $line;
973
974 my $fd2;
975
976 if ($fd2 = IO::File->new ("/proc/net/dev", "r")) {
977 while (defined ($line = <$fd2>)) {
978 chomp ($line);
979 if ($line =~ m/^\s*(eth[0-9]):.*/) {
980 $ifaces->{$1}->{exists} = 1;
981 }
982 }
983 close ($fd2);
984 }
985
986 # always add the vmbr0 bridge device
987 $ifaces->{vmbr0}->{exists} = 1;
988
989 if ($fd2 = IO::File->new ("/proc/net/if_inet6", "r")) {
990 while (defined ($line = <$fd2>)) {
991 chomp ($line);
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;
994 }
995 }
996 close ($fd2);
997 }
998
999 my $gateway = 0;
1000
1001 while (defined ($line = <$fh>)) {
1002 chomp ($line);
1003 next if $line =~ m/^#/;
1004
1005 if ($line =~ m/^auto\s+(.*)$/) {
1006 my @aa = split (/\s+/, $1);
1007
1008 foreach my $a (@aa) {
1009 $ifaces->{$a}->{autostart} = 1;
1010 }
1011
1012 } elsif ($line =~ m/^iface\s+(\S+)\s+inet\s+(\S+)\s*$/) {
1013 my $i = $1;
1014 $ifaces->{$i}->{type} = $2;
1015 while (defined ($line = <$fh>) && ($line =~ m/^\s+((\S+)\s+(.+))$/)) {
1016 my $option = $1;
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;
1022 $gateway = 1;
1023 } elsif ($id eq 'slaves') {
1024 foreach my $p (split (/\s+/, $value)) {
1025 next if $p eq 'none';
1026 $ifaces->{$i}->{$id}->{$p} = 1;
1027 }
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;
1032 }
1033 } elsif ($id eq 'bridge_stp') {
1034 if ($value =~ m/^\s*(on|yes)\s*$/i) {
1035 $ifaces->{$i}->{$id} = 'on';
1036 } else {
1037 $ifaces->{$i}->{$id} = 'off';
1038 }
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') {
1044 # always use names
1045 foreach my $bm (keys %$bond_modes) {
1046 my $id = $bond_modes->{$bm};
1047 if ($id eq $value) {
1048 $value = $bm;
1049 last;
1050 }
1051 }
1052 $ifaces->{$i}->{$id} = $value;
1053 } else {
1054 push @{$ifaces->{$i}->{options}}, $option;
1055 }
1056 }
1057 }
1058 }
1059
1060 if (!$gateway) {
1061 $ifaces->{vmbr0}->{gateway} = '';
1062 }
1063
1064 if (!$ifaces->{lo}) {
1065 $ifaces->{lo}->{type} = 'loopback';
1066 $ifaces->{lo}->{autostart} = 1;
1067 }
1068
1069 foreach my $iface (keys %$ifaces) {
1070 if ($iface =~ m/^bond\d+$/) {
1071
1072 } elsif ($iface =~ m/^vmbr\d+$/) {
1073 if (!defined ($ifaces->{$iface}->{bridge_fd})) {
1074 $ifaces->{$iface}->{bridge_fd} = 0;
1075 }
1076 if (!defined ($ifaces->{$iface}->{bridge_stp})) {
1077 $ifaces->{$iface}->{bridge_stp} = 'off';
1078 }
1079 } elsif ($iface =~ m/^(\S+):\d+$/) {
1080 if (defined ($ifaces->{$1})) {
1081 $ifaces->{$iface}->{exists} = $ifaces->{$1}->{exists};
1082 } else {
1083 $ifaces->{$1}->{exists} = 0;
1084 $ifaces->{$iface}->{exists} = 0;
1085 }
1086 }
1087
1088 $ifaces->{$iface}->{type} = 'manual' if !$ifaces->{$iface}->{type};
1089 }
1090
1091 return $ifaces;
1092}
1093
1094sub __print_interface {
1095 my ($fh, $ifaces, $iface) = @_;
1096
1097 return if !$ifaces->{$iface}->{type};
1098
1099 if ($ifaces->{$iface}->{autostart}) {
1100 print $fh "auto $iface\n";
1101 }
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};
1107
1108 if ($ifaces->{$iface}->{bridge_ports} || ($iface =~ m/^vmbr\d+$/)) {
1109 my $ports;
1110 if ($ifaces->{$iface}->{bridge_ports}) {
1111 $ports = join (' ', sort keys %{$ifaces->{$iface}->{bridge_ports}});
1112 }
1113 $ports = 'none' if !$ports;
1114 print $fh "\tbridge_ports $ports\n";
1115 }
1116
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";
1121 }
1122
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";
1127 }
1128
1129 if ($ifaces->{$iface}->{slaves} || ($iface =~ m/^bond\d+$/)) {
1130 my $slaves;
1131 if ($ifaces->{$iface}->{slaves}) {
1132 $slaves = join (' ', sort keys %{$ifaces->{$iface}->{slaves}});
1133 }
1134 $slaves = 'none' if !$slaves;
1135 print $fh "\tslaves $slaves\n";
1136 }
1137
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";
1142 }
1143
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";
1148 }
1149
1150 foreach my $option (@{$ifaces->{$iface}->{options}}) {
1151 print $fh "\t$option\n";
1152 }
1153
1154 print $fh "\n";
1155}
1156
1157sub write_etc_network_interfaces {
1158 my ($filename, $fh, $ifaces) = @_;
1159
1160 print $fh "# network interface settings\n";
1161
1162 foreach my $iface (keys %$ifaces) {
1163 delete ($ifaces->{$iface}->{printed});
1164 }
1165
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);
1172 }
1173 }
1174
1175 return $ifaces;
1176}
1177
1178sub read_etc_resolv_conf {
1179 my ($filename, $fh) = @_;
1180
1181 my $res = {};
1182
1183 while (my $line = <$fh>) {
1184 chomp $line;
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;
1189 }
1190 }
1191
1192 return $res;
1193}
1194
1195sub write_etc_resolv_conf {
1196 my ($filename, $fh, $resolv) = @_;
1197
1198 print $fh "search $resolv->{search}\n";
1199
1200 my $written = {};
1201 my $nslist = [];
1202
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";
1207 push @$nslist, $ns;
1208 }
1209 }
1210
1211 $resolv->{nameservers} = $nslist;
1212 return $resolv;
1213}
1214
1215sub ccache_default_writer {
1216 my ($filename, $data) = @_;
1217
1218 die "undefined config writer for '$filename' :ERROR";
1219}
1220
1221sub ccache_default_parser {
1222 my ($filename, $srcfd) = @_;
1223
1224 die "undefined config reader for '$filename' :ERROR";
1225}
1226
1227sub ccache_compute_diff {
1228 my ($filename, $shadow) = @_;
1229
1230 my $diff = '';
1231
1232 open (TMP, "diff -b -N -u '$filename' '$shadow'|");
1233
1234 while (my $line = <TMP>) {
1235 $diff .= $line;
1236 }
1237
1238 close (TMP);
1239
1240 $diff = undef if !$diff;
1241
1242 return $diff;
1243}
1244
1245sub write_file {
1246 my ($filename, $data, $full) = @_;
1247
1248 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1249
1250 die "file '$filename' not added :ERROR" if !defined ($ccache->{$filename});
1251
1252 my $writer = $ccache->{$filename}->{writer};
1253
1254 my $realname = $filename;
1255
1256 my $shadow;
1257 if ($shadow = $shadowfiles->{$filename}) {
1258 $realname = $shadow;
1259 }
1260
1261 my $fh = PVE::AtomicFile->open($realname, "w") ||
1262 die "unable to open file '$realname' for writing - $! :ERROR";
1263
1264 my $res;
1265
1266 eval {
1267 $res = &$writer ($filename, $fh, $data);
1268 };
1269
1270 $ccache->{$filename}->{version} = undef;
1271
1272 my $err = $@;
1273 $fh->detach() if $err;
1274 $fh->close(1);
1275
1276 die $err if $err;
1277
1278 my $diff;
1279 if ($shadow && $full) {
1280 $diff = ccache_compute_diff ($filename, $shadow);
1281 }
1282
1283 if ($full) {
1284 return { data => $res, changes => $diff };
1285 }
1286
1287 return $res;
1288}
1289
1290sub update_file {
1291 my ($filename, $data, @args) = @_;
1292
1293 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1294
1295 my $update = $ccache->{$filename}->{update};
1296
1297 die "unable to update/merge data" if !$update;
1298
1299 my $lkfn = "$filename.lock";
1300
1301 if (!open (FLCK, ">>$lkfn")) {
1302 die "unable to open lock file '$lkfn' - $?";
1303 }
1304
1305 if (!flock (FLCK, LOCK_EX)) {
1306 close (FLCK);
1307 die "unable to aquire lock for file '$lkfn' - $?";
1308 }
1309
1310 my $newdata;
1311
1312 eval {
1313
1314 my $olddata = read_file ($filename);
1315
1316 if ($data) {
1317 my $res = &$update ($filename, $olddata, $data, @args);
1318 if (defined ($res)) {
1319 $newdata = write_file ($filename, $res);
1320 } else {
1321 $newdata = $olddata;
1322 }
1323 } else {
1324 $newdata = $olddata;
1325 }
1326 };
1327
1328 my $err = $@;
1329
1330 close (FLCK);
1331
1332 die $err if $err;
1333
1334 return $newdata;
1335}
1336
1337sub discard_changes {
1338 my ($filename, $full) = @_;
1339
1340 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1341
1342 die "file '$filename' not added :ERROR" if !defined ($ccache->{$filename});
1343
1344 if (my $copy = $shadowfiles->{$filename}) {
1345 unlink $copy;
1346 }
1347
1348 return read_file ($filename, $full);
1349}
1350
1351sub read_file {
1352 my ($filename, $full) = @_;
1353
1354 my $parser;
1355
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;
1360 } else {
1361 $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
1362
1363 die "file '$filename' not added :ERROR" if !defined ($ccache->{$filename});
1364
1365 $parser = $ccache->{$filename}->{parser};
1366 }
1367
1368 my $fd;
1369 my $shadow;
1370
1371 poll() if $inotify; # read new inotify events
1372
1373 $versions->{$filename} = 0 if !defined ($versions->{$filename});
1374
1375 my $cver = $versions->{$filename};
1376
1377 if (my $copy = $shadowfiles->{$filename}) {
1378 if ($fd = IO::File->new ($copy, "r")) {
1379 $shadow = $copy;
1380 } else {
1381 $fd = IO::File->new ($filename, "r");
1382 }
1383 } else {
1384 $fd = IO::File->new ($filename, "r");
1385 }
1386
1387 my $acp = $ccache->{$filename}->{always_call_parser};
1388
1389 if (!$fd) {
1390 $ccache->{$filename}->{version} = undef;
1391 $ccache->{$filename}->{data} = undef;
1392 $ccache->{$filename}->{diff} = undef;
1393 return undef if !$acp;
1394 }
1395
1396 my $noclone = $ccache->{$filename}->{noclone};
1397
1398 # file unchanged?
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}))) {
1405
1406 my $ret;
1407 if (!$noclone && ref ($ccache->{$filename}->{data})) {
1408 $ret->{data} = dclone ($ccache->{$filename}->{data});
1409 } else {
1410 $ret->{data} = $ccache->{$filename}->{data};
1411 }
1412 $ret->{changes} = $ccache->{$filename}->{diff};
1413
1414 return $full ? $ret : $ret->{data};
1415 }
1416
1417 my $diff;
1418
1419 if ($shadow) {
1420 $diff = ccache_compute_diff ($filename, $shadow);
1421 }
1422
1423 my $res = &$parser ($filename, $fd);
1424
1425 if (!$ccache->{$filename}->{nocache}) {
1426 $ccache->{$filename}->{version} = $cver;
1427 }
1428
1429 # we cache data with references, so we always need to
1430 # dclone this data. Else the original data may get
1431 # modified.
1432 $ccache->{$filename}->{data} = $res;
1433
1434 # also store diff
1435 $ccache->{$filename}->{diff} = $diff;
1436
1437 my $ret;
1438 if (!$noclone && ref ($ccache->{$filename}->{data})) {
1439 $ret->{data} = dclone ($ccache->{$filename}->{data});
1440 } else {
1441 $ret->{data} = $ccache->{$filename}->{data};
1442 }
1443 $ret->{changes} = $ccache->{$filename}->{diff};
1444
1445 return $full ? $ret : $ret->{data};
1446}
1447
1448sub add_file {
1449 my ($id, $filename, $parser, $writer, $update, %options) = @_;
1450
1451 die "file '$filename' already added :ERROR" if defined ($ccache->{$filename});
1452 die "ID '$id' already used :ERROR" if defined ($ccachemap->{$id});
1453
1454 $ccachemap->{$id} = $filename;
1455 $ccache->{$filename}->{id} = $id;
1456
1457 $ccache->{$filename}->{parser} = $parser || \&ccache_default_parser;
1458 $ccache->{$filename}->{writer} = $writer || \&ccache_default_writer;
1459 $ccache->{$filename}->{update} = $update;
1460
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;
1474 } else {
1475 die "internal error - unsupported option '$opt'";
1476 }
1477 }
1478
1479
1480}
1481
1482sub poll {
1483 return if !$inotify;
1484
1485 if ($inotify_pid != $$) {
1486 syslog ('err', "got inotify poll request in wrong process - disabling inotify");
1487 $inotify = undef;
1488 } else {
1489 1 while $inotify && $inotify->poll;
1490 }
1491}
1492
1493sub flushcache {
1494 foreach my $filename (keys %$ccache) {
1495 $ccache->{$filename}->{version} = undef;
1496 $ccache->{$filename}->{data} = undef;
1497 $ccache->{$filename}->{diff} = undef;
1498 }
1499}
1500
1501sub inotify_close {
1502 $inotify = undef;
1503}
1504
1505sub inotify_init {
1506
1507 die "only one inotify instance allowed" if $inotify;
1508
1509 $inotify = Linux::Inotify2->new()
1510 || die "Unable to create new inotify object: $!";
1511
1512 $inotify->blocking (0);
1513
1514 $versions = {};
1515
1516 my $dirhash = {};
1517 foreach my $fn (keys %$ccache) {
1518 my $dir = dirname ($fn);
1519 my $base = basename ($fn);
1520
1521 $dirhash->{$dir}->{$base} = $fn;
1522
1523 if (my $sf = $shadowfiles->{$fn}) {
1524 $base = basename ($sf);
1525 $dir = dirname ($sf);
1526 $dirhash->{$dir}->{$base} = $fn; # change version of original file!
1527 }
1528 }
1529
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';
1533
1534 $inotify_pid = $$;
1535
1536 foreach my $dir (keys %$dirhash) {
1537
1538 my $evlist = IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE;
1539 $inotify->watch ($dir, $evlist, sub {
1540 my $e = shift;
1541 my $name = $e->name;
1542
1543 if ($inotify_pid != $$) {
1544 syslog ('err', "got inotify event in wrong process");
1545 }
1546
1547 if ($e->IN_ISDIR || !$name) {
1548 return;
1549 }
1550
1551 if ($e->IN_Q_OVERFLOW) {
1552 syslog ('info', "got inotify overflow - flushing cache");
1553 flushcache();
1554 return;
1555 }
1556
1557 if ($e->IN_UNMOUNT) {
1558 syslog ('err', "got 'unmount' event on '$name' - disabling inotify");
1559 $inotify = undef;
1560 }
1561 if ($e->IN_IGNORED) {
1562 syslog ('err', "got 'ignored' event on '$name' - disabling inotify");
1563 $inotify = undef;
1564 }
1565
1566 my $re = $dirhash->{$dir}->{_regex};
1567 if ($re && ($name =~ m|^$re$|)) {
1568
1569 my $fn = "$dir/$name";
1570 $versions->{$fn}++;
1571 #print "VERSION:$fn:$versions->{$fn}\n";
1572
1573 } elsif (my $fn = $dirhash->{$dir}->{$name}) {
1574
1575 $versions->{$fn}++;
1576 #print "VERSION:$fn:$versions->{$fn}\n";
1577 }
1578 });
1579 }
1580
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";
1591 }
1592 }
1593 }
1594 } else {
1595 my $fn = $dirhash->{$dir}->{$name};
1596 $versions->{$fn}++; # init with version
1597 #print "init:$fn:$versions->{$fn}\n";
1598 }
1599 }
1600 }
1601
1602}
1603
1604add_file ('hostname', "/etc/hostname",
1605 \&read_etc_hostname,
1606 \&write_etc_hostname);
1607
1608add_file ('syncstatus', "/var/lib/pve-manager/syncstatus",
1609 \&read_var_lib_syncstatus,
1610 \&write_var_lib_syncstatus);
1611
1612add_file ('vzlist', "/var/lib/pve-manager/vzlist",
1613 \&read_var_lib_vzlist,
1614 \&write_var_lib_vzlist,
1615 \&update_var_lib_vzlist);
1616
1617add_file ('vmops', "/var/lib/pve-manager/vmops",
1618 \&read_var_lib_vmops,
1619 \&write_var_lib_vmops,
1620 \&update_var_lib_vmops);
1621
1622add_file ('interfaces', "/etc/network/interfaces",
1623 \&read_etc_network_interfaces,
1624 \&write_etc_network_interfaces);
1625
1626add_file ('resolvconf', "/etc/resolv.conf",
1627 \&read_etc_resolv_conf,
1628 \&write_etc_resolv_conf);
1629
1630add_file ('timezone', "/etc/timezone",
1631 \&read_etc_timezone,
1632 \&write_etc_timezone);
1633
1634add_file ('pvecfg', "/etc/pve/pve.cfg",
1635 \&read_etc_pve_cfg,
1636 \&write_etc_pve_cfg);
1637
1638add_file ('storagecfg', "/etc/pve/storage.cfg",
1639 \&read_etc_pve_storagecfg, undef, undef,
1640 always_call_parser => 1);
1641
1642add_file ('rootrsapubkey', "/root/.ssh/id_rsa.pub",
1643 \&read_rsapubkey);
1644
1645add_file ('hostrsapubkey', "/etc/ssh/ssh_host_rsa_key.pub",
1646 \&read_rsapubkey);
1647
1648add_file ('clustercfg', "/etc/pve/cluster.cfg",
1649 \&PVE::Storage::read_cluster_config);
1650
1651add_file ('newclustercfg', "/etc/pve/master/cluster.cfg",
1652 \&PVE::Storage::read_cluster_config);
1653
1654add_file ('qemuservercfg', "/etc/pve/qemu-server.cfg",
1655 \&read_etc_pve_qemu_server_cfg,
1656 \&write_etc_pve_qemu_server_cfg);
1657
1658add_file ('vzdump', "/etc/cron.d/vzdump",
1659 \&read_etc_crond_vzdump,
1660 \&write_etc_crond_vzdump,
1661 \&update_etc_crond_vzdump);
1662
1663add_file ('aplinfo', "/var/lib/pve-manager/apl-available",
1664 \&read_aplinfo, undef, undef,
1665 noclone => 1);
1666
1667add_file ('dotforward', "/root/.forward",
1668 \&read_root_dotforward,
1669 \&write_root_dotforward);
1670
1671add_file ('usercfg', "/etc/pve/user.cfg",
1672 \&PVE::AccessControl::parse_config);
1673
1674# persistent counter implementation
1675add_file ('pcounter', "/var/lib/pve-manager/pcounter",
1676 \&read_pcounter,
1677 \&write_pcounter,
1678 \&update_pcounter,
1679 nocache => 1);
1680
16811;