#!/usr/bin/perl -CSD # Purpose: template toolkit helper script ################################################################################ use strict; use warnings; use English; use YAML::XS qw(LoadFile); use Hash::Merge qw(merge); use DBI; use Capture::Tiny qw(capture); sub sync_extra_sockets; sub db_connect; my $dbh; my $config = {}; foreach my $file (@ARGV) { $config = merge($config, LoadFile($file)); } # rw connection to central node my $dbhost = $config->{database}->{central}->{dbhost}; my $dbport = $config->{database}->{central}->{dbport}; my $dbname = $config->{ossbss}->{provisioning}->{database}->{name}; my $dbcredentials = "/etc/mysql/sipwise_extra.cnf"; unless(defined $dbhost) { print "Error: Could not determine provisioning db hostname\n"; exit 1; } unless(defined $dbport) { print "Error: Could not determine provisioning db port\n"; exit 1; } unless(defined $dbname) { print "Error: Could not determine provisioning db name\n"; exit 1; } $dbh = db_connect(); exit 1 unless(sync_extra_sockets($dbh, $config->{kamailio}->{lb}->{extra_sockets}, 1, 1, 1)); exit 1 unless(sync_rtp_interfaces($dbh, { map {my $tmp = $_; $tmp =~ s/:.*//; ($_ => $_, $tmp => $tmp)} @{$config->{rtp_interfaces}} }, 1, 1, 1)); exit 1 unless(sync_smsc_peers($dbh, { map { ($_->{id} => $_->{id}) } @{$config->{sms}->{smsc}} }, 0, 1, 0)); exit 1 unless(sync_general_timezone($dbh, $config->{general}->{timezone})); exit 1 unless(sync_db_timezones($dbh)); exit 1 unless(sync_li_admins($dbh, $config->{www_admin}->{lawful_intercept_admins})); $dbh->disconnect; exit 0; sub db_connect { my $dbh = DBI->connect("DBI:mysql:database=${dbname};host=${dbhost};port=${dbport};" . "mysql_read_default_file=${dbcredentials}", "", "", { PrintError => 0 }); unless (defined $dbh) { die "Error: Could not connect to database '$dbname' at '$dbhost:$dbport' using '${dbcredentials}': $DBI::errstr\n"; } return $dbh; } sub generic_enum_sync { my $dbh = shift; my $config_hash = shift; my $usr_pref = shift; my $dom_pref = shift; my $peer_pref = shift; my $pref_name = shift; my $sth = $dbh->prepare("select id from voip_preferences where attribute=?"); $sth->execute($pref_name); my $res = $sth->fetchrow_hashref(); unless (defined $res) { print "Error: Could not find preference '$pref_name' in db\n"; return; } my $pref_id = $res->{id}; $sth->finish; my $enum_insert_sth = $dbh->prepare("insert into voip_preferences_enum (preference_id, label, value, usr_pref, dom_pref, peer_pref) values(?, ?, ?, ?, ?, ?)"); my $enum_update_sth = $dbh->prepare("update voip_preferences_enum set value=? where id=?"); my $enum_delete_sth = $dbh->prepare("delete from voip_preferences_enum where id=?"); $sth = $dbh->prepare("select id, label, value from voip_preferences_enum where preference_id=?"); $sth->execute($pref_id); my $db_hash = $sth->fetchall_hashref('label'); $sth->finish; foreach my $row (keys %{$db_hash}) { $row = $db_hash->{$row}; next if $row->{label} eq 'default'; if (!exists $config_hash->{$row->{label}}) { print "$pref_name $row->{label} does not exist anymore in config, delete from db\n"; $enum_delete_sth->execute($row->{id}); } elsif ($config_hash->{$row->{label}} ne $row->{value}) { print "update $pref_name $row->{label}=$row->{value} to $row->{label}=$config_hash->{$row->{label}} in db\n"; $enum_update_sth->execute($config_hash->{$row->{label}}, $row->{id}); delete $config_hash->{$row->{label}}; } else { print "$pref_name $row->{label}=$row->{value} is sync between config and db\n"; delete $config_hash->{$row->{label}}; } } foreach my $label (keys %{$config_hash}) { print "insert new $pref_name $label=$config_hash->{$label} from config into db\n"; $enum_insert_sth->execute($pref_id, $label, $config_hash->{$label}, $usr_pref ? 1 : 0, $dom_pref ? 1 : 0, $peer_pref ? 1: 0); } $enum_insert_sth->finish; $enum_update_sth->finish; $enum_delete_sth->finish; return 1; } ## kamailio.lb.extra_sockets handling: ############################## sub sync_extra_sockets { return generic_enum_sync(@_, 'outbound_socket'); } ## rtp_* interfaces handling: ############################## sub sync_rtp_interfaces { return generic_enum_sync(@_, 'rtp_interface'); } # ## smsc_* interfaces handling: ############################## sub sync_smsc_peers { return generic_enum_sync(@_, 'smsc_peer'); } ## general.timezone handling: ############################## sub sync_general_timezone { my $dbh = shift; my $tz = shift; my $ok = 1; my ($sql_log_bin) = $dbh->selectrow_array('SELECT @@sql_log_bin'); eval { die "Error: general.timezone value is not set\n" unless $tz; $dbh->do("SET sql_log_bin=0"); my ($current_tz) = $dbh->selectrow_array(<do(<do("SET sql_log_bin=$sql_log_bin"); return $ok; } ## /usr/share/zoneinfo into MariaDB ############################## sub sync_db_timezones { my $dbh = shift; my ($out, $err, $rc) = capture { system('/usr/bin/mysql_tzinfo_to_sql /usr/share/zoneinfo'); }; if ($rc) { print "Error: $err\n"; return; } my ($tzinfo_version, undef, undef) = capture { system('dpkg-query -f "\${Version}" -W tzdata'); }; unless ($tzinfo_version) { print "Error: Could not retrieve tzdata package version\n"; return; } chomp $tzinfo_version; my $sql = ''; eval { $dbh->begin_work() or die "Cannot start tx: $DBI::errstr\n"; my ($cur_tzinfo_version) = $dbh->selectrow_array(<do('USE mysql') or die "Cannot use mysql database: $DBI::errstr\n"; my $batch; my $delim = ';'; while ($sql = <$sql_stream>) { if ($sql =~ m#^\s*\\d\s+(\S+)#) { $delim = $1; } else { if ($sql =~ /^(.*)\Q$delim\E\s*$/) { if ($1) { $batch .= $1; } $dbh->do($batch) or die "Cannot insert timezone data: $DBI::errstr\n"; $batch = undef; } else { $batch .= $sql; } } } close $sql_stream; $dbh->do(<rollback(); return; } $dbh->commit(); return 1; } ## add LI admins into MariaDB ############################## sub sync_li_admins { my ($dbh, $cfg_admins) = @_; my $sql = ''; eval { $dbh->begin_work() or die "Cannot start tx: $DBI::errstr\n"; $dbh->do('USE billing') or die "Cannot use billing database: $DBI::errstr\n"; my $existent_admins = $dbh->selectall_hashref(<{username} && $li_admin->{email}) { print "A Lawful Intercept Administrator does not have username or password set\n"; next; } if (!exists $existent_admins->{$li_admin->{username}} || !$existent_admins->{$li_admin->{username}}->{email} || ($li_admin->{email} ne $existent_admins->{$li_admin->{username}}->{email})) { #new admin or updated email $vals and $vals .= ","; $vals .= "('".$li_admin->{username}."','".$li_admin->{email}."','".$password."', 1, 1, 1, 1)"; } #delete all admins that have been found in config.yml and use the remaining ones for the delete statement delete $existent_admins->{$li_admin->{username}}; } if ($vals) { #insert new admins or update email $dbh->do(<do(<rollback(); return; } $dbh->commit(); return 1; } ## END OF FILE #################################################################