]> git.proxmox.com Git - pmg-api.git/blame - PMG/DBTools.pm
debian/control: depend on sudo
[pmg-api.git] / PMG / DBTools.pm
CommitLineData
a355f100
DM
1package PMG::DBTools;
2
3use strict;
4use warnings;
5
6use POSIX ":sys_wait_h";
7use POSIX ':signal_h';
8use DBI;
9
f3464b71 10use PVE::SafeSyslog;
a355f100
DM
11use PVE::Tools;
12
f3464b71 13use PMG::Utils;
0a580593
DM
14use PMG::RuleDB;
15
987ee804
DM
16our $default_db_name = "Proxmox_ruledb";
17
a355f100
DM
18sub open_ruledb {
19 my ($database, $host, $port) = @_;
20
21 $port = 5432 if !$port;
22
987ee804 23 $database = $default_db_name if !$database;
a355f100
DM
24
25 if ($host) {
26
155b0da9
DM
27 # Note: pmgtunnel uses UDP sockets inside directory '/var/run/pmgtunnel',
28 # and the cluster 'cid' as port number. You can connect to the
29 # socket with: host => /var/run/pmgtunnel, port => $cid
30
a355f100
DM
31 my $dsn = "dbi:Pg:dbname=$database;host=$host;port=$port;";
32
33 my $timeout = 5;
34 # only low level alarm interface works for DBI->connect
35 my $mask = POSIX::SigSet->new(SIGALRM);
36 my $action = POSIX::SigAction->new(sub { die "connect timeout\n" }, $mask);
37 my $oldaction = POSIX::SigAction->new();
38 sigaction(SIGALRM, $action, $oldaction);
39
40 my $rdb;
41
42 eval {
43 alarm($timeout);
97cab76a 44 $rdb = DBI->connect($dsn, "root", undef,
a355f100
DM
45 { PrintError => 0, RaiseError => 1 });
46 alarm(0);
47 };
48 alarm(0);
49 sigaction(SIGALRM, $oldaction); # restore original handler
f1bf78ff 50
a355f100
DM
51 die $@ if $@;
52
53 return $rdb;
54 } else {
155b0da9 55 my $dsn = "DBI:Pg:dbname=$database;host=/var/run/postgresql;port=$port";
a355f100 56
97cab76a 57 my $dbh = DBI->connect($dsn, "root", undef,
a355f100
DM
58 { PrintError => 0, RaiseError => 1 });
59
60 return $dbh;
61 }
62}
63
97cab76a
DM
64sub postgres_admin_cmd {
65 my ($cmd, $options, @params) = @_;
66
67 $cmd = ref($cmd) ? $cmd : [ $cmd ];
68 my $uid = getpwnam('postgres') || die "getpwnam postgres failed\n";
69
70 local $> = $uid;
71 $! && die "setuid postgres ($uid) failed - $!\n";
72
73 PVE::Tools::run_command([@$cmd, '-U', 'postgres', @params], %$options);
74}
75
a355f100
DM
76sub delete_ruledb {
77 my ($dbname) = @_;
78
97cab76a 79 postgres_admin_cmd('dropdb', undef, $dbname);
a355f100
DM
80}
81
f1bf78ff
DM
82sub database_list {
83
84 my $database_list = {};
85
86 my $parser = sub {
87 my $line = shift;
88
89 my ($name, $owner) = map { PVE::Tools::trim($_) } split(/\|/, $line);
90 return if !$name || !$owner;
91
92 $database_list->{$name} = { owner => $owner };
93 };
94
97cab76a 95 postgres_admin_cmd('psql', { outfunc => $parser }, '--list', '--quiet', '--tuples-only');
f1bf78ff
DM
96
97 return $database_list;
98}
99
100my $dbfunction_maxint = <<__EOD;
101 CREATE OR REPLACE FUNCTION maxint (INTEGER, INTEGER) RETURNS INTEGER AS
102 'BEGIN IF \$1 > \$2 THEN RETURN \$1; ELSE RETURN \$2; END IF; END;' LANGUAGE plpgsql;
103__EOD
104
105my $dbfunction_minint = <<__EOD;
106 CREATE OR REPLACE FUNCTION minint (INTEGER, INTEGER) RETURNS INTEGER AS
107 'BEGIN IF \$1 < \$2 THEN RETURN \$1; ELSE RETURN \$2; END IF; END;' LANGUAGE plpgsql;
108__EOD
109
110# merge function to avoid update/insert race condition
111# see: http://www.postgresql.org/docs/9.1/static/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING
112my $dbfunction_merge_greylist = <<__EOD;
113 CREATE OR REPLACE FUNCTION merge_greylist (in_ipnet VARCHAR, in_host INTEGER, in_sender VARCHAR,
114 in_receiver VARCHAR, in_instance VARCHAR,
115 in_rctime INTEGER, in_extime INTEGER, in_delay INTEGER,
116 in_blocked INTEGER, in_passed INTEGER, in_mtime INTEGER,
117 in_cid INTEGER) RETURNS INTEGER AS
118 'BEGIN
119 LOOP
120 UPDATE CGreylist SET Host = CASE WHEN MTime >= in_mtime THEN Host ELSE in_host END,
121 CID = maxint (CID, in_cid), RCTime = minint (rctime, in_rctime),
122 ExTime = maxint (extime, in_extime),
123 Delay = maxint (delay, in_delay),
124 Blocked = maxint (blocked, in_blocked),
125 Passed = maxint (passed, in_passed)
126 WHERE IPNet = in_ipnet AND Sender = in_sender AND Receiver = in_receiver;
127
128 IF found THEN
129 RETURN 0;
130 END IF;
131
132 BEGIN
133 INSERT INTO CGREYLIST (IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, MTime, CID)
134 VALUES (in_ipnet, in_host, in_sender, in_receiver, in_instance, in_rctime, in_extime,
135 in_delay, in_blocked, in_passed, in_mtime, in_cid);
136 RETURN 1;
137 EXCEPTION WHEN unique_violation THEN
138 -- do nothing - continue loop
139 END;
140 END LOOP;
141 END;' LANGUAGE plpgsql;
142__EOD
143
144my $cgreylist_ctablecmd = <<__EOD;
145 CREATE TABLE CGreylist
146 (IPNet VARCHAR(16) NOT NULL,
147 Host INTEGER NOT NULL,
148 Sender VARCHAR(255) NOT NULL,
149 Receiver VARCHAR(255) NOT NULL,
150 Instance VARCHAR(255),
151 RCTime INTEGER NOT NULL,
152 ExTime INTEGER NOT NULL,
153 Delay INTEGER NOT NULL DEFAULT 0,
154 Blocked INTEGER NOT NULL,
155 Passed INTEGER NOT NULL,
156 CID INTEGER NOT NULL,
157 MTime INTEGER NOT NULL,
158 PRIMARY KEY (IPNet, Sender, Receiver));
159
160 CREATE INDEX CGreylist_Instance_Sender_Index ON CGreylist (Instance, Sender);
161
162 CREATE INDEX CGreylist_ExTime_Index ON CGreylist (ExTime);
163
164 CREATE INDEX CGreylist_MTime_Index ON CGreylist (MTime);
165__EOD
166
167my $clusterinfo_ctablecmd = <<__EOD;
168 CREATE TABLE ClusterInfo
169 (CID INTEGER NOT NULL,
170 Name VARCHAR NOT NULL,
171 IValue INTEGER,
172 SValue VARCHAR,
173 PRIMARY KEY (CID, Name))
174__EOD
175
176my $daily_stat_ctablecmd = <<__EOD;
177 CREATE TABLE DailyStat
178 (Time INTEGER NOT NULL UNIQUE,
179 CountIn INTEGER NOT NULL,
180 CountOut INTEGER NOT NULL,
181 BytesIn REAL NOT NULL,
182 BytesOut REAL NOT NULL,
183 VirusIn INTEGER NOT NULL,
184 VirusOut INTEGER NOT NULL,
185 SpamIn INTEGER NOT NULL,
186 SpamOut INTEGER NOT NULL,
187 BouncesIn INTEGER NOT NULL,
188 BouncesOut INTEGER NOT NULL,
189 GreylistCount INTEGER NOT NULL,
190 SPFCount INTEGER NOT NULL,
191 PTimeSum REAL NOT NULL,
192 MTime INTEGER NOT NULL,
193 RBLCount INTEGER DEFAULT 0 NOT NULL,
194 PRIMARY KEY (Time));
195
196 CREATE INDEX DailyStat_MTime_Index ON DailyStat (MTime);
197
198__EOD
199
200my $domain_stat_ctablecmd = <<__EOD;
201 CREATE TABLE DomainStat
202 (Time INTEGER NOT NULL,
203 Domain VARCHAR(255) NOT NULL,
204 CountIn INTEGER NOT NULL,
205 CountOut INTEGER NOT NULL,
206 BytesIn REAL NOT NULL,
207 BytesOut REAL NOT NULL,
208 VirusIn INTEGER NOT NULL,
209 VirusOut INTEGER NOT NULL,
210 SpamIn INTEGER NOT NULL,
211 SpamOut INTEGER NOT NULL,
212 BouncesIn INTEGER NOT NULL,
213 BouncesOut INTEGER NOT NULL,
214 PTimeSum REAL NOT NULL,
215 MTime INTEGER NOT NULL,
216 PRIMARY KEY (Time, Domain));
217
218 CREATE INDEX DomainStat_MTime_Index ON DomainStat (MTime);
219__EOD
220
221my $statinfo_ctablecmd = <<__EOD;
222 CREATE TABLE StatInfo
223 (Name VARCHAR(255) NOT NULL UNIQUE,
224 IValue INTEGER,
225 SValue VARCHAR(255),
226 PRIMARY KEY (Name))
227__EOD
228
229my $virusinfo_stat_ctablecmd = <<__EOD;
230 CREATE TABLE VirusInfo
231 (Time INTEGER NOT NULL,
232 Name VARCHAR NOT NULL,
233 Count INTEGER NOT NULL,
234 MTime INTEGER NOT NULL,
235 PRIMARY KEY (Time, Name));
236
237 CREATE INDEX VirusInfo_MTime_Index ON VirusInfo (MTime);
238
239__EOD
240
241# mail storage stable
242# QTypes
243# V - Virus quarantine
244# S - Spam quarantine
245# D - Delayed Mails - not implemented
246# A - Held for Audit - not implemented
247# Status
248# N - new
249# D - deleted
250
251my $cmailstore_ctablecmd = <<__EOD;
252 CREATE TABLE CMailStore
253 (CID INTEGER DEFAULT 0 NOT NULL,
254 RID INTEGER NOT NULL,
255 ID SERIAL UNIQUE,
256 Time INTEGER NOT NULL,
257 QType "char" NOT NULL,
258 Bytes INTEGER NOT NULL,
259 Spamlevel INTEGER NOT NULL,
260 Info VARCHAR NULL,
261 Sender VARCHAR(255) NOT NULL,
262 Header VARCHAR NOT NULL,
263 File VARCHAR(255) NOT NULL,
264 PRIMARY KEY (CID, RID));
265 CREATE INDEX CMailStore_Time_Index ON CMailStore (Time);
266
267 CREATE TABLE CMSReceivers
268 (CMailStore_CID INTEGER NOT NULL,
269 CMailStore_RID INTEGER NOT NULL,
270 PMail VARCHAR(255) NOT NULL,
271 Receiver VARCHAR(255),
272 TicketID INTEGER NOT NULL,
273 Status "char" NOT NULL,
274 MTime INTEGER NOT NULL);
275
276 CREATE INDEX CMailStore_ID_Index ON CMSReceivers (CMailStore_CID, CMailStore_RID);
277
278 CREATE INDEX CMSReceivers_MTime_Index ON CMSReceivers (MTime);
279
280__EOD
281
282my $cstatistic_ctablecmd = <<__EOD;
283 CREATE TABLE CStatistic
284 (CID INTEGER DEFAULT 0 NOT NULL,
285 RID INTEGER NOT NULL,
286 ID SERIAL UNIQUE,
287 Time INTEGER NOT NULL,
288 Bytes INTEGER NOT NULL,
289 Direction Boolean NOT NULL,
290 Spamlevel INTEGER NOT NULL,
291 VirusInfo VARCHAR(255) NULL,
292 PTime INTEGER NOT NULL,
293 Sender VARCHAR(255) NOT NULL,
294 PRIMARY KEY (CID, RID));
295
296 CREATE INDEX CStatistic_Time_Index ON CStatistic (Time);
297
298 CREATE TABLE CReceivers
299 (CStatistic_CID INTEGER NOT NULL,
300 CStatistic_RID INTEGER NOT NULL,
301 Receiver VARCHAR(255) NOT NULL,
302 Blocked Boolean NOT NULL);
303
304 CREATE INDEX CStatistic_ID_Index ON CReceivers (CStatistic_CID, CStatistic_RID);
305__EOD
306
307# user preferences (black an whitelists, ...)
308# Name: perference name ('BL' -> blacklist, 'WL' -> whitelist)
309# Data: arbitrary data
310my $userprefs_ctablecmd = <<__EOD;
311 CREATE TABLE UserPrefs
312 (PMail VARCHAR,
313 Name VARCHAR(255),
314 Data VARCHAR,
315 MTime INTEGER NOT NULL,
316 PRIMARY KEY (PMail, Name));
317
318 CREATE INDEX UserPrefs_MTime_Index ON UserPrefs (MTime);
319
320__EOD
758c7b6b 321
0a580593
DM
322sub cond_create_dbtable {
323 my ($dbh, $name, $ctablecmd) = @_;
324
325 eval {
326 $dbh->begin_work;
327
328 my $cmd = "SELECT tablename FROM pg_tables " .
329 "WHERE tablename = lower ('$name')";
330
331 my $sth = $dbh->prepare ($cmd);
758c7b6b 332
0a580593
DM
333 $sth->execute();
334
335 if (!(my $ref = $sth->fetchrow_hashref())) {
336 $dbh->do ($ctablecmd);
337 }
758c7b6b 338
0a580593
DM
339 $sth->finish();
340
341 $dbh->commit;
342 };
343 if (my $err = $@) {
344 $dbh->rollback;
9ef3f143 345 die $err;
0a580593
DM
346 }
347}
f1bf78ff
DM
348
349sub create_ruledb {
350 my ($dbname) = @_;
351
987ee804 352 $dbname = $default_db_name if !$dbname;
f1bf78ff 353
97cab76a
DM
354 my $silent_opts = { outfunc => sub {}, errfunc => sub {} };
355 # make sure we have user 'root'
356 eval { postgres_admin_cmd('createuser', $silent_opts, '-D', 'root'); };
357
f1bf78ff
DM
358 # use sql_ascii to avoid any character set conversions, and be compatible with
359 # older postgres versions (update from 8.1 must be possible)
f1bf78ff 360
97cab76a
DM
361 postgres_admin_cmd('createdb', undef, '-E', 'sql_ascii', '-T', 'template0',
362 '--lc-collate=C', '--lc-ctype=C', $dbname);
f1bf78ff
DM
363
364 my $dbh = open_ruledb($dbname);
365
366 #$dbh->do ($dbloaddrivers_sql);
367 #$dbh->do ($dbfunction_update_modtime);
368
369 $dbh->do ($dbfunction_minint);
370
371 $dbh->do ($dbfunction_maxint);
372
373 $dbh->do ($dbfunction_merge_greylist);
374
375 $dbh->do (
376<<EOD
377 CREATE TABLE Attribut
378 (Object_ID INTEGER NOT NULL,
379 Name VARCHAR(20) NOT NULL,
380 Value BYTEA NULL,
381 PRIMARY KEY (Object_ID, Name));
382
383 CREATE INDEX Attribut_Object_ID_Index ON Attribut(Object_ID);
384
385 CREATE TABLE Object
386 (ID SERIAL UNIQUE,
387 ObjectType INTEGER NOT NULL,
388 Objectgroup_ID INTEGER NOT NULL,
389 Value BYTEA NULL,
390 PRIMARY KEY (ID));
391
392 CREATE TABLE Objectgroup
393 (ID SERIAL UNIQUE,
394 Name VARCHAR(255) NOT NULL,
395 Info VARCHAR(255) NULL,
396 Class VARCHAR(10) NOT NULL,
397 PRIMARY KEY (ID));
398
399 CREATE TABLE Rule
400 (ID SERIAL UNIQUE,
401 Name VARCHAR(255) NULL,
402 Priority INTEGER NOT NULL,
403 Active INTEGER NOT NULL DEFAULT 0,
404 Direction INTEGER NOT NULL DEFAULT 2,
405 Count INTEGER NOT NULL DEFAULT 0,
406 PRIMARY KEY (ID));
407
408 CREATE TABLE RuleGroup
409 (Objectgroup_ID INTEGER NOT NULL,
410 Rule_ID INTEGER NOT NULL,
411 Grouptype INTEGER NOT NULL,
412 PRIMARY KEY (Objectgroup_ID, Rule_ID, Grouptype));
413
414 $cgreylist_ctablecmd;
415
416 $clusterinfo_ctablecmd;
417
418 $daily_stat_ctablecmd;
419
420 $domain_stat_ctablecmd;
421
422 $statinfo_ctablecmd;
423
424 $cmailstore_ctablecmd;
425
426 $cstatistic_ctablecmd;
427
428 $userprefs_ctablecmd;
429
430 $virusinfo_stat_ctablecmd;
431EOD
432 );
433
434 return $dbh;
435}
436
0a580593
DM
437sub cond_create_action_quarantine {
438 my ($ruledb) = @_;
439
440 my $dbh = $ruledb->{dbh};
441
442 eval {
443 my $sth = $dbh->prepare(
444 "SELECT * FROM Objectgroup, Object " .
445 "WHERE Object.ObjectType = ? AND Objectgroup.Class = ? " .
446 "AND Object.objectgroup_id = Objectgroup.id");
447
448 my $otype = PMG::RuleDB::Quarantine::otype();
449 if ($sth->execute($otype, 'action') <= 0) {
450 my $obj = PMG::RuleDB::Quarantine->new ();
451 my $txt = decode_entities(PMG::RuleDB::Quarantine->otype_text);
452 my $quarantine = $ruledb->create_group_with_obj
9578dcd7 453 ($obj, $txt, 'Move to quarantine.');
0a580593
DM
454 }
455 };
456}
457
458sub cond_create_std_actions {
459 my ($ruledb) = @_;
460
461 cond_create_action_quarantine($ruledb);
462
463 #cond_create_action_report_spam($ruledb);
464}
465
466
e7c865af
DM
467sub upgrade_mailstore_db {
468 my ($dbh) = @_;
469
470 eval {
471 $dbh->begin_work;
472
473 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('MailStore')";
474
475 my $sth = $dbh->prepare($cmd);
476 $sth->execute();
477 my $ref = $sth->fetchrow_hashref();
478 $sth->finish();
479
480 if ($ref) { # table exists
481
482 $cmd = "INSERT INTO CMailStore " .
483 "(CID, RID, ID, Time, QType, Bytes, Spamlevel, Info, Header, Sender, File) " .
484 "SELECT 0, ID, ID, Time, QType, Bytes, Spamlevel, Info, Header, Sender, File FROM MailStore";
485
486 $dbh->do($cmd);
487
488 $cmd = "INSERT INTO CMSReceivers " .
489 "(CMailStore_CID, CMailStore_RID, PMail, Receiver, TicketID, Status, MTime) " .
490 "SELECT 0, MailStore_ID, PMail, Receiver, TicketID, Status, 0 FROM MSReceivers";
491
492 $dbh->do($cmd);
493
494 $dbh->do("SELECT setval ('cmailstore_id_seq', nextval ('mailstore_id_seq'))");
495
496 $dbh->do("DROP TABLE MailStore");
497 $dbh->do("DROP TABLE MSReceivers");
498 }
499
500 $dbh->commit;
501 };
502 if (my $err = $@) {
503 $dbh->rollback;
504 die $err;
505 }
506}
507
508sub upgrade_dailystat_db {
509 my ($dbh) = @_;
510
511 eval { # make sure we have MTime
512 $dbh->do("ALTER TABLE DailyStat ADD COLUMN MTime INTEGER;" .
513 "UPDATE DailyStat SET MTime = EXTRACT (EPOCH FROM now());");
514 };
515
516 eval { # make sure we have correct constraints for MTime
517 $dbh->do ("ALTER TABLE DailyStat ALTER COLUMN MTime SET NOT NULL;");
518 };
519
520 eval { # make sure we have RBLCount
521 $dbh->do ("ALTER TABLE DailyStat ADD COLUMN RBLCount INTEGER;" .
522 "UPDATE DailyStat SET RBLCount = 0;");
523 };
524
525 eval { # make sure we have correct constraints for RBLCount
526 $dbh->do ("ALTER TABLE DailyStat ALTER COLUMN RBLCount SET DEFAULT 0;" .
527 "ALTER TABLE DailyStat ALTER COLUMN RBLCount SET NOT NULL;");
528 };
529
530 eval {
531 $dbh->begin_work;
532
533 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('DailyStat_MTime_Index')";
534
535 my $sth = $dbh->prepare($cmd);
536 $sth->execute();
537 my $ref = $sth->fetchrow_hashref();
538 $sth->finish();
539
540 if (!$ref) { # index does not exist
541 $dbh->do ("CREATE INDEX DailyStat_MTime_Index ON DailyStat (MTime)");
542 }
543
544 $dbh->commit;
545 };
546 if (my $err = $@) {
547 $dbh->rollback;
548 die $err;
549 }
550}
551
552sub upgrade_domainstat_db {
553 my ($dbh) = @_;
554
555 eval { # make sure we have MTime
556 $dbh->do("ALTER TABLE DomainStat ADD COLUMN MTime INTEGER;" .
557 "UPDATE DomainStat SET MTime = EXTRACT (EPOCH FROM now());" .
558 "ALTER TABLE DomainStat ALTER COLUMN MTime SET NOT NULL;");
559 };
560
561 eval {
562 $dbh->begin_work;
563
564 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('DomainStat_MTime_Index')";
565
566 my $sth = $dbh->prepare($cmd);
567 $sth->execute();
568 my $ref = $sth->fetchrow_hashref();
569 $sth->finish();
570
571 if (!$ref) { # index does not exist
572 $dbh->do ("CREATE INDEX DomainStat_MTime_Index ON DomainStat (MTime)");
573 }
574
575 $dbh->commit;
576 };
577 if (my $err = $@) {
578 $dbh->rollback;
579 die $@;
580 }
581}
582
583sub upgrade_statistic_db {
584 my ($dbh) = @_;
585
586 eval {
587 $dbh->begin_work;
588
589 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('Statistic')";
590
591 my $sth = $dbh->prepare($cmd);
592 $sth->execute();
593 my $ref = $sth->fetchrow_hashref();
594 $sth->finish();
595
596 if ($ref) { # old table exists
597
598 my $timezone = tz_local_offset();;
599
600 $dbh->do("INSERT INTO VirusInfo (Time, Name, Count, MTime) " .
601 "SELECT ((time + $timezone) / 86400) * 86400 as day, virusinfo, " .
602 "count (virusinfo), max (Time) FROM Statistic " .
603 "WHERE virusinfo IS NOT NULL GROUP BY day, virusinfo");
604
605 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime (time());
606 my $end = timelocal(0, 0, 0, $mday, $mon, $year);
607 my $start = $end - 3600*24*7; # / days
608
609 $cmd = "INSERT INTO CStatistic " .
610 "(CID, RID, ID, Time, Bytes, Direction, Spamlevel, VirusInfo, PTime, Sender) " .
611 "SELECT 0, ID, ID, Time, Bytes, Direction, Spamlevel, VirusInfo, PTime, Sender FROM Statistic " .
612 "WHERE time >= $start";
613
614 $dbh->do($cmd);
615
616 $dbh->do("SELECT setval ('cstatistic_id_seq', nextval ('statistic_id_seq'))");
617
618 $dbh->do("INSERT INTO StatInfo (name, ivalue) VALUES ('virusinfo_index', " .
619 "nextval ('statistic_id_seq'))");
620
621 $cmd = "INSERT INTO CReceivers (CStatistic_CID, CStatistic_RID, Receiver, Blocked) " .
622 "SELECT 0, Mail_ID, Receiver, Blocked FROM Receivers " .
623 "WHERE EXISTS (SELECT * FROM CStatistic WHERE CID = 0 AND RID = Mail_ID)";
624
625 $dbh->do($cmd);
626
627 $dbh->do("DROP TABLE Statistic");
628 $dbh->do("DROP TABLE Receivers");
629 }
630
631 $dbh->commit;
632 };
633 if (my $err = $@) {
634 $dbh->rollback;
635 die $err;
636 }
637}
638
639sub upgrade_greylist_db {
640 my ($dbh) = @_;
641
642 eval {
643 $dbh->begin_work;
644
645 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('Greylist')";
646
647 my $sth = $dbh->prepare($cmd);
648 $sth->execute();
649 my $ref = $sth->fetchrow_hashref();
650 $sth->finish();
651
652 if ($ref) { # table exists
653
654 $cmd = "INSERT INTO CGreylist " .
655 "(IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, MTime, CID) " .
656 "SELECT IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, RCTime, 0 FROM Greylist";
657
658 $dbh->do($cmd);
659
660 $dbh->do("DROP TABLE Greylist");
661 }
662
663 $dbh->commit;
664 };
665 if (my $err = $@) {
666 $dbh->rollback;
667 die $err;
668 }
669}
670
671sub upgrade_userprefs_db {
672 my ($dbh) = @_;
673
674 eval {
675 $dbh->do("ALTER TABLE UserPrefs ADD COLUMN MTime INTEGER;" .
676 "UPDATE UserPrefs SET MTime = EXTRACT (EPOCH FROM now());" .
677 "ALTER TABLE UserPrefs ALTER COLUMN MTime SET NOT NULL;");
678 };
679
680
681 eval {
682 $dbh->begin_work;
683
684 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('UserPrefs_MTime_Index')";
685
686 my $sth = $dbh->prepare($cmd);
687 $sth->execute();
688 my $ref = $sth->fetchrow_hashref();
689 $sth->finish();
690
691 if (!$ref) { # index does not exist
692 $dbh->do("CREATE INDEX UserPrefs_MTime_Index ON UserPrefs (MTime)");
693 }
694
695 $dbh->commit;
696 };
697 if ($@) {
698 $dbh->rollback;
699 die $@;
700 }
701}
702
0a580593
DM
703sub upgradedb {
704 my ($ruledb) = @_;
705
706 my $dbh = $ruledb->{dbh};
707
e7c865af 708 $dbh->do($dbfunction_minint);
0a580593 709
e7c865af 710 $dbh->do($dbfunction_maxint);
0a580593 711
e7c865af 712 $dbh->do($dbfunction_merge_greylist);
0a580593 713
758c7b6b 714 # make sure we do not use slow sequential scans when upgraing
0a580593
DM
715 # database (before analyze can gather statistics)
716 $dbh->do("set enable_seqscan = false");
717
97cab76a
DM
718 my $tables = {
719 'DailyStat'=> $daily_stat_ctablecmd,
720 'DomainStat', $domain_stat_ctablecmd,
721 'StatInfo', $statinfo_ctablecmd,
722 'CMailStore', $cmailstore_ctablecmd,
723 'UserPrefs', $userprefs_ctablecmd,
724 'CGreylist', $cgreylist_ctablecmd,
725 'CStatistic', $cstatistic_ctablecmd,
726 'ClusterInfo', $clusterinfo_ctablecmd,
727 'VirusInfo', $virusinfo_stat_ctablecmd,
728 };
729
730 foreach my $table (keys %$tables) {
731 cond_create_dbtable($dbh, $table, $tables->{$tables});
732 }
0a580593 733
e7c865af 734 cond_create_std_actions($ruledb);
0a580593 735
e7c865af 736 upgrade_mailstore_db($dbh);
0a580593 737
e7c865af 738 upgrade_statistic_db($dbh);
0a580593 739
e7c865af 740 upgrade_userprefs_db($dbh);
0a580593 741
e7c865af 742 upgrade_greylist_db($dbh);
0a580593 743
e7c865af 744 upgrade_dailystat_db($dbh);
0a580593 745
e7c865af 746 upgrade_domainstat_db($dbh);
0a580593
DM
747
748 # update obsolete content type names
749 eval {
e7c865af
DM
750 $dbh->do("UPDATE Object " .
751 "SET value = 'content-type:application/java-vm' ".
752 "WHERE objecttype = 3003 " .
753 "AND value = 'content-type:application/x-java-vm';");
0a580593
DM
754 };
755
97cab76a
DM
756 foreach my $table (keys %$tables) {
757 eval { $dbh->do("ANALYZE $table"); };
758 warn $@ if $@;
759 }
0a580593
DM
760}
761
762sub init_ruledb {
763 my ($ruledb, $reset, $testmode) = @_;
764
765 my $dbh = $ruledb->{dbh};
766
767 if (!$reset) {
768 # Greylist Objectgroup
769 my $greylistgroup = PMG::RuleDB::Group->new
770 ("GreyExclusion", "-", "greylist");
771 $ruledb->save_group ($greylistgroup);
772
773 } else {
774 # we do not touch greylist objects
775 my $glids = "SELECT object.ID FROM Object, Objectgroup WHERE " .
776 "objectgroup_id = objectgroup.id and class = 'greylist'";
777
778 $dbh->do ("DELETE FROM Rule; " .
779 "DELETE FROM RuleGroup; " .
780 "DELETE FROM Attribut WHERE Object_ID NOT IN ($glids); " .
781 "DELETE FROM Object WHERE ID NOT IN ($glids); " .
782 "DELETE FROM Objectgroup WHERE class != 'greylist';");
783 }
784
785 # WHO Objects
786
787 # Blacklist
788 my $obj = PMG::RuleDB::EMail->new ('nomail@fromthisdomain.com');
789 my $blacklist = $ruledb->create_group_with_obj(
790 $obj, 'Blacklist', 'Global blacklist');
758c7b6b 791
0a580593
DM
792 # Whitelist
793 $obj = PMG::RuleDB::EMail->new('mail@fromthisdomain.com');
794 my $whitelist = $ruledb->create_group_with_obj(
795 $obj, 'Whitelist', 'Global whitelist');
796
797 # WHEN Objects
798
799 # Working hours
a29b9649 800 $obj = PMG::RuleDB::TimeFrame->new(8*60, 16*60);
0a580593
DM
801 my $working_hours =$ruledb->create_group_with_obj($obj, 'Office Hours' ,
802 'Usual office hours');
803
804 # WHAT Objects
805
0a580593 806 # Images
a29b9649
DM
807 $obj = PMG::RuleDB::ContentTypeFilter->new('image/.*');
808 my $img_content = $ruledb->create_group_with_obj(
809 $obj, 'Images', 'All kinds of graphic files');
810
0a580593 811 # Multimedia
a29b9649
DM
812 $obj = PMG::RuleDB::ContentTypeFilter->new('audio/.*');
813 my $mm_content = $ruledb->create_group_with_obj(
814 $obj, 'Multimedia', 'Audio and Video');
0a580593 815
a29b9649
DM
816 $obj = PMG::RuleDB::ContentTypeFilter->new('video/.*');
817 $ruledb->group_add_object($mm_content, $obj);
0a580593
DM
818
819 # Office Files
a29b9649
DM
820 $obj = PMG::RuleDB::ContentTypeFilter->new('application/vnd\.ms-excel');
821 my $office_content = $ruledb->create_group_with_obj(
822 $obj, 'Office Files', 'Common Office Files');
758c7b6b 823
a29b9649
DM
824 $obj = PMG::RuleDB::ContentTypeFilter->new(
825 'application/vnd\.ms-powerpoint');
758c7b6b 826
a29b9649 827 $ruledb->group_add_object($office_content, $obj);
758c7b6b 828
a29b9649 829 $obj = PMG::RuleDB::ContentTypeFilter->new('application/msword');
0a580593 830 $ruledb->group_add_object ($office_content, $obj);
758c7b6b 831
a29b9649
DM
832 $obj = PMG::RuleDB::ContentTypeFilter->new(
833 'application/vnd\.openxmlformats-officedocument\..*');
834 $ruledb->group_add_object($office_content, $obj);
758c7b6b 835
a29b9649
DM
836 $obj = PMG::RuleDB::ContentTypeFilter->new(
837 'application/vnd\.oasis\.opendocument\..*');
838 $ruledb->group_add_object($office_content, $obj);
0a580593 839
a29b9649
DM
840 $obj = PMG::RuleDB::ContentTypeFilter->new(
841 'application/vnd\.stardivision\..*');
842 $ruledb->group_add_object($office_content, $obj);
758c7b6b 843
a29b9649
DM
844 $obj = PMG::RuleDB::ContentTypeFilter->new(
845 'application/vnd\.sun\.xml\..*');
846 $ruledb->group_add_object($office_content, $obj);
758c7b6b 847
0a580593 848 # Dangerous Content
a29b9649
DM
849 $obj = PMG::RuleDB::ContentTypeFilter->new(
850 'application/x-ms-dos-executable');
851 my $exe_content = $ruledb->create_group_with_obj(
852 $obj, 'Dangerous Content', 'executable files and partial messages');
758c7b6b 853
a29b9649
DM
854 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-java');
855 $ruledb->group_add_object($exe_content, $obj);
856 $obj = PMG::RuleDB::ContentTypeFilter->new('application/javascript');
857 $ruledb->group_add_object($exe_content, $obj);
858 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-executable');
859 $ruledb->group_add_object($exe_content, $obj);
79ef2452
DM
860 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-ms-dos-executable');
861 $ruledb->group_add_object($exe_content, $obj);
a29b9649
DM
862 $obj = PMG::RuleDB::ContentTypeFilter->new('message/partial');
863 $ruledb->group_add_object($exe_content, $obj);
864 $obj = PMG::RuleDB::MatchFilename->new('.*\.(vbs|pif|lnk|shs|shb)');
865 $ruledb->group_add_object($exe_content, $obj);
be6c5e4a 866 $obj = PMG::RuleDB::MatchFilename->new('.*\.\{.+\}');
a29b9649 867 $ruledb->group_add_object($exe_content, $obj);
0a580593
DM
868
869 # Virus
758c7b6b
DM
870 $obj = PMG::RuleDB::Virus->new();
871 my $virus = $ruledb->create_group_with_obj(
872 $obj, 'Virus', 'Matches virus infected mail');
873
0a580593
DM
874 # WHAT Objects
875
876 # Spam
758c7b6b
DM
877 $obj = PMG::RuleDB::Spam->new(3);
878 my $spam3 = $ruledb->create_group_with_obj(
879 $obj, 'Spam (Level 3)', 'Matches possible spam mail');
e7c865af 880
758c7b6b
DM
881 $obj = PMG::RuleDB::Spam->new(5);
882 my $spam5 = $ruledb->create_group_with_obj(
883 $obj, 'Spam (Level 5)', 'Matches possible spam mail');
e7c865af 884
758c7b6b
DM
885 $obj = PMG::RuleDB::Spam->new(10);
886 my $spam10 = $ruledb->create_group_with_obj(
887 $obj, 'Spam (Level 10)', 'Matches possible spam mail');
0a580593
DM
888
889 # ACTIONS
890
758c7b6b
DM
891 # Mark Spam
892 $obj = PMG::RuleDB::ModField->new('X-SPAM-LEVEL', '__SPAM_INFO__');
893 my $mod_spam_level = $ruledb->create_group_with_obj(
e7c865af 894 $obj, 'Modify Spam Level',
758c7b6b 895 'Mark mail as spam by adding a header tag.');
0a580593
DM
896
897 # Mark Spam
758c7b6b
DM
898 $obj = PMG::RuleDB::ModField->new('subject', 'SPAM: __SUBJECT__');
899 my $mod_spam_subject = $ruledb->create_group_with_obj(
e7c865af 900 $obj, 'Modify Spam Subject',
758c7b6b 901 'Mark mail as spam by modifying the subject.');
e7c865af 902
0a580593 903 # Remove matching attachments
758c7b6b
DM
904 $obj = PMG::RuleDB::Remove->new(0);
905 my $remove = $ruledb->create_group_with_obj(
906 $obj, 'Remove attachments', 'Remove matching attachments');
e7c865af 907
0a580593 908 # Remove all attachments
758c7b6b
DM
909 $obj = PMG::RuleDB::Remove->new(1);
910 my $remove_all = $ruledb->create_group_with_obj(
911 $obj, 'Remove all attachments', 'Remove all attachments');
0a580593
DM
912
913 # Accept
758c7b6b
DM
914 $obj = PMG::RuleDB::Accept->new();
915 my $accept = $ruledb->create_group_with_obj(
916 $obj, 'Accept', 'Accept mail for Delivery');
0a580593
DM
917
918 # Block
758c7b6b
DM
919 $obj = PMG::RuleDB::Block->new ();
920 my $block = $ruledb->create_group_with_obj($obj, 'Block', 'Block mail');
0a580593
DM
921
922 # Quarantine
758c7b6b
DM
923 $obj = PMG::RuleDB::Quarantine->new();
924 my $quarantine = $ruledb->create_group_with_obj(
925 $obj, 'Quarantine', 'Move mail to quarantine');
0a580593
DM
926
927 # Notify Admin
758c7b6b
DM
928 $obj = PMG::RuleDB::Notify->new('__ADMIN__');
929 my $notify_admin = $ruledb->create_group_with_obj(
930 $obj, 'Notify Admin', 'Send notification');
0a580593
DM
931
932 # Notify Sender
758c7b6b
DM
933 $obj = PMG::RuleDB::Notify->new('__SENDER__');
934 my $notify_sender = $ruledb->create_group_with_obj(
935 $obj, 'Notify Sender', 'Send notification');
0a580593
DM
936
937 # Add Disclaimer
758c7b6b
DM
938 $obj = PMG::RuleDB::Disclaimer->new ();
939 my $add_discl = $ruledb->create_group_with_obj(
940 $obj, 'Disclaimer', 'Add Disclaimer');
0a580593
DM
941
942 # Attach original mail
943 #$obj = Proxmox::RuleDB::Attach->new ();
758c7b6b 944 #my $attach_orig = $ruledb->create_group_with_obj ($obj, 'Attach Original Mail',
0a580593
DM
945 # 'Attach Original Mail');
946
947 ####################### RULES ##################################
948
949 ## Block Dangerous Files
a29b9649 950 my $rule = PMG::RuleDB::Rule->new ('Block Dangerous Files', 93, 1, 0);
0a580593
DM
951 $ruledb->save_rule ($rule);
952
953 $ruledb->rule_add_what_group ($rule, $exe_content);
954 $ruledb->rule_add_action ($rule, $remove);
955
956 ## Block Viruses
a29b9649 957 $rule = PMG::RuleDB::Rule->new ('Block Viruses', 96, 1, 0);
0a580593
DM
958 $ruledb->save_rule ($rule);
959
960 $ruledb->rule_add_what_group ($rule, $virus);
961 $ruledb->rule_add_action ($rule, $notify_admin);
758c7b6b 962
0a580593
DM
963 if ($testmode) {
964 $ruledb->rule_add_action ($rule, $block);
965 } else {
966 $ruledb->rule_add_action ($rule, $quarantine);
967 }
968
969 ## Virus Alert
a29b9649 970 $rule = PMG::RuleDB::Rule->new ('Virus Alert', 96, 1, 1);
0a580593
DM
971 $ruledb->save_rule ($rule);
972
973 $ruledb->rule_add_what_group ($rule, $virus);
974 $ruledb->rule_add_action ($rule, $notify_sender);
975 $ruledb->rule_add_action ($rule, $notify_admin);
976 $ruledb->rule_add_action ($rule, $block);
758c7b6b 977
0a580593 978 ## Blacklist
a29b9649 979 $rule = PMG::RuleDB::Rule->new ('Blacklist', 98, 1, 0);
0a580593
DM
980 $ruledb->save_rule ($rule);
981
982 $ruledb->rule_add_from_group ($rule, $blacklist);
983 $ruledb->rule_add_action ($rule, $block);
984
985 ## Modify header
986 if (!$testmode) {
a29b9649 987 $rule = PMG::RuleDB::Rule->new ('Modify Header', 90, 1, 0);
0a580593
DM
988 $ruledb->save_rule ($rule);
989 $ruledb->rule_add_action ($rule, $mod_spam_level);
990 }
991
992 ## Whitelist
a29b9649 993 $rule = PMG::RuleDB::Rule->new ('Whitelist', 85, 1, 0);
0a580593
DM
994 $ruledb->save_rule ($rule);
995
996 $ruledb->rule_add_from_group ($rule, $whitelist);
997 $ruledb->rule_add_action ($rule, $accept);
998
999 if ($testmode) {
a29b9649 1000 $rule = PMG::RuleDB::Rule->new ('Mark Spam', 80, 1, 0);
0a580593
DM
1001 $ruledb->save_rule ($rule);
1002
1003 $ruledb->rule_add_what_group ($rule, $spam10);
1004 $ruledb->rule_add_action ($rule, $mod_spam_level);
1005 $ruledb->rule_add_action ($rule, $mod_spam_subject);
1006 } else {
1007 # Quarantine/Mark Spam (Level 3)
a29b9649 1008 $rule = PMG::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 3)', 80, 1, 0);
0a580593
DM
1009 $ruledb->save_rule ($rule);
1010
1011 $ruledb->rule_add_what_group ($rule, $spam3);
1012 $ruledb->rule_add_action ($rule, $mod_spam_subject);
1013 $ruledb->rule_add_action ($rule, $quarantine);
1014 #$ruledb->rule_add_action ($rule, $count_spam);
1015 }
1016
1017 # Quarantine/Mark Spam (Level 5)
a29b9649 1018 $rule = PMG::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 5)', 79, 0, 0);
0a580593
DM
1019 $ruledb->save_rule ($rule);
1020
1021 $ruledb->rule_add_what_group ($rule, $spam5);
1022 $ruledb->rule_add_action ($rule, $mod_spam_subject);
1023 $ruledb->rule_add_action ($rule, $quarantine);
1024
1025 ## Block Spam Level 10
a29b9649 1026 $rule = PMG::RuleDB::Rule->new ('Block Spam (Level 10)', 78, 0, 0);
0a580593
DM
1027 $ruledb->save_rule ($rule);
1028
1029 $ruledb->rule_add_what_group ($rule, $spam10);
1030 $ruledb->rule_add_action ($rule, $block);
1031
1032 ## Block Outgoing Spam
a29b9649 1033 $rule = PMG::RuleDB::Rule->new ('Block outgoing Spam', 70, 0, 1);
0a580593
DM
1034 $ruledb->save_rule ($rule);
1035
1036 $ruledb->rule_add_what_group ($rule, $spam3);
1037 $ruledb->rule_add_action ($rule, $notify_admin);
1038 $ruledb->rule_add_action ($rule, $notify_sender);
1039 $ruledb->rule_add_action ($rule, $block);
1040
1041 ## Add disclaimer
a29b9649 1042 $rule = PMG::RuleDB::Rule->new ('Add Disclaimer', 60, 0, 1);
0a580593
DM
1043 $ruledb->save_rule ($rule);
1044 $ruledb->rule_add_action ($rule, $add_discl);
1045
1046 # Block Multimedia Files
a29b9649 1047 $rule = PMG::RuleDB::Rule->new ('Block Multimedia Files', 87, 0, 2);
0a580593
DM
1048 $ruledb->save_rule ($rule);
1049
1050 $ruledb->rule_add_what_group ($rule, $mm_content);
1051 $ruledb->rule_add_action ($rule, $remove);
1052
1053 #$ruledb->rule_add_from_group ($rule, $anybody);
1054 #$ruledb->rule_add_from_group ($rule, $trusted);
1055 #$ruledb->rule_add_to_group ($rule, $anybody);
1056 #$ruledb->rule_add_what_group ($rule, $ct_filter);
1057 #$ruledb->rule_add_action ($rule, $add_discl);
1058 #$ruledb->rule_add_action ($rule, $remove);
1059 #$ruledb->rule_add_action ($rule, $bcc);
1060 #$ruledb->rule_add_action ($rule, $storeq);
1061 #$ruledb->rule_add_action ($rule, $accept);
1062
1063 cond_create_std_actions ($ruledb);
1064}
1065
8f4e102b
DM
1066sub get_remote_time {
1067 my ($rdb) = @_;
1068
1069 my $sth = $rdb->prepare("SELECT EXTRACT (EPOCH FROM TIMESTAMP (0) WITH TIME ZONE 'now') as ctime;");
1070 $sth->execute();
1071 my $ctinfo = $sth->fetchrow_hashref();
1072 $sth->finish ();
1073
1074 return $ctinfo ? $ctinfo->{ctime} : 0;
1075}
1076
cfdf6608
DM
1077sub init_masterdb {
1078 my ($lcid, $database) = @_;
1079
1080 die "got unexpected cid for new master" if !$lcid;
1081
1082 my $dbh;
1083
1084 eval {
1085 $dbh = open_ruledb($database);
1086
1087 $dbh->begin_work;
1088
1089 print STDERR "update quarantine database\n";
1090 $dbh->do ("UPDATE CMailStore SET CID = $lcid WHERE CID = 0;" .
1091 "UPDATE CMSReceivers SET CMailStore_CID = $lcid WHERE CMailStore_CID = 0;");
1092
1093 print STDERR "update statistic database\n";
1094 $dbh->do ("UPDATE CStatistic SET CID = $lcid WHERE CID = 0;" .
1095 "UPDATE CReceivers SET CStatistic_CID = $lcid WHERE CStatistic_CID = 0;");
1096
1097 print STDERR "update greylist database\n";
1098 $dbh->do ("UPDATE CGreylist SET CID = $lcid WHERE CID = 0;");
1099
1100 $dbh->commit;
1101 };
1102 my $err = $@;
1103
1104 if ($dbh) {
1105 $dbh->rollback if $err;
1106 $dbh->disconnect();
1107 }
1108
1109 die $err if $err;
1110}
1111
987ee804
DM
1112sub update_master_clusterinfo {
1113 my ($clientcid) = @_;
1114
1115 my $dbh = open_ruledb();
1116
1117 $dbh->do("DELETE FROM ClusterInfo WHERE CID = $clientcid");
1118
1119 my @mt = ('CMSReceivers', 'CGreylist', 'UserPrefs', 'DomainStat', 'DailyStat', 'VirusInfo');
1120
1121 foreach my $table (@mt) {
1122 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $clientcid, 'lastmt_$table', " .
1123 "EXTRACT(EPOCH FROM now())");
1124 }
1125}
1126
1127sub update_client_clusterinfo {
1128 my ($mastercid) = @_;
1129
1130 my $dbh = open_ruledb();
1131
1132 $dbh->do ("DELETE FROM StatInfo"); # not needed at node
1133
1134 $dbh->do ("DELETE FROM ClusterInfo WHERE CID = $mastercid");
1135
1136 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $mastercid, 'lastid_CMailStore', " .
1137 "COALESCE (max (rid), -1) FROM CMailStore WHERE cid = $mastercid");
1138
1139 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $mastercid, 'lastid_CStatistic', " .
1140 "COALESCE (max (rid), -1) FROM CStatistic WHERE cid = $mastercid");
1141
1142 my @mt = ('CMSReceivers', 'CGreylist', 'UserPrefs', 'DomainStat', 'DailyStat', 'VirusInfo');
1143
1144 foreach my $table (@mt) {
1145 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $mastercid, 'lastmt_$table', " .
1146 "COALESCE (max (mtime), 0) FROM $table");
1147 }
1148}
1149
1150sub init_nodedb {
1151 my ($cinfo) = @_;
1152
1153 my $ni = $cinfo->{master};
1154
1155 die "no master defined - unable to sync data from master\n" if !$ni;
1156
1157 my $master_ip = $ni->{ip};
1158 my $master_cid = $ni->{cid};
22c16ff6 1159 my $master_name = $ni->{name};
987ee804
DM
1160
1161 my $fn = "/tmp/masterdb$$.tar";
1162 unlink $fn;
1163
1164 my $dbname = $default_db_name;
1165
1166 eval {
1167 print STDERR "copying master database from '${master_ip}'\n";
1168
97cab76a
DM
1169 open (my $fh, ">", $fn) || die "open '$fn' failed - $!\n";
1170
1171 postgres_admin_cmd(
1172 ['/usr/bin/ssh', '-o', 'BatchMode=yes',
1173 '-o', "HostKeyAlias=${master_name}",
1174 $master_ip, 'pg_dump'],
1175 { output => '>&' . fileno($fh) },
1176 $dbname, '-F', 'c');
22c16ff6 1177
97cab76a 1178 close($fh);
987ee804
DM
1179
1180 my $size = -s $fn;
1181
1182 print STDERR "copying master database finished (got $size bytes)\n";
1183
1184 print STDERR "delete local database\n";
1185
97cab76a 1186 postgres_admin_cmd('dropdb', undef, $dbname , '--if-exists');
987ee804
DM
1187
1188 print STDERR "create new local database\n";
1189
97cab76a 1190 postgres_admin_cmd('createdb', undef, $dbname);
987ee804
DM
1191
1192 print STDERR "insert received data into local database\n";
1193
1194 my $mess;
1195 my $parser = sub {
1196 my $line = shift;
1197
1198 if ($line =~ m/restoring data for table \"(.+)\"/) {
1199 print STDERR "restoring table $1\n";
1200 } elsif (!$mess && ($line =~ m/creating (INDEX|CONSTRAINT)/)) {
1201 $mess = "creating indexes";
1202 print STDERR "$mess\n";
1203 }
1204 };
1205
97cab76a
DM
1206 my $opts = {
1207 outfunc => $parser,
1208 errfunc => $parser,
1209 errmsg => "pg_restore failed"
1210 };
1211
1212 postgres_admin_cmd('pg_restore', $opts, '-d', $dbname, '-v', $fn);
987ee804
DM
1213
1214 print STDERR "run analyze to speed up database queries\n";
1215
97cab76a 1216 postgres_admin_cmd('psql', { input => 'analyze;' }, $dbname);
987ee804
DM
1217
1218 update_client_clusterinfo($master_cid);
1219 };
1220
1221 my $err = $@;
1222
1223 unlink $fn;
1224
1225 die $err if $err;
1226}
1227
f3464b71
DM
1228sub cluster_sync_status {
1229 my ($cinfo) = @_;
1230
1231 my $dbh;
1232
1233 my $minmtime;
1234
1235 foreach my $ni (values %{$cinfo->{ids}}) {
1236 next if $cinfo->{local}->{cid} == $ni->{cid}; # skip local CID
1237 $minmtime->{$ni->{cid}} = 0;
1238 }
1239
1240 eval {
1241 $dbh = open_ruledb();
1242
1243 my $sth = $dbh->prepare(
1244 "SELECT cid, MIN (ivalue) as minmtime FROM ClusterInfo " .
1245 "WHERE name = 'lastsync' AND ivalue > 0 " .
1246 "GROUP BY cid");
1247
1248 $sth->execute();
1249
1250 while (my $info = $sth->fetchrow_hashref()) {
1251 foreach my $ni (values %{$cinfo->{ids}}) {
1252 next if $cinfo->{local}->{cid} == $ni->{cid}; # skip local CID
1253 if ($ni->{cid} == $info->{cid}) { # node exists
1254 $minmtime->{$ni->{cid}} = $info->{minmtime};
1255 }
1256 }
1257 }
1258
1259 $sth->finish ();
1260 };
1261 my $err = $@;
1262
1263 $dbh->disconnect() if $dbh;
1264
1265 if ($err) {
1266 syslog('err', PMG::Utils::msgquote($err));
1267 }
1268
1269 return $minmtime;
1270}
1271
cfdf6608 1272
a355f100 12731;