Паравиртуализация LXC Антон Борисов Листинг 1. Характеристики ядра для ARM-системы на базе TrimSlice # file /boot/uImage Листинг 2. Часть конфигурационного файла контейнера lxc.utsname = LXC_Machine1 ... lxc.network.type = veth lxc.network.flags = up lxc.network.link = lxcbr0 lxc.network.name = eth0 lxc.network.mtu = 1500 lxc.network.ipv4 = 192.168.215.2/24 Листинг 3. Параметры сетевого стека для LXC-подсистемы # MIRROR to be used by ubuntu template at container creation: # Leaving it undefined is fine #MIRROR="http://archive.ubuntu.com/ubuntu" # or #MIRROR="http://:3142/archive.ubuntu.com/ubuntu" # LXC_AUTO - whether or not to start containers symlinked # under /etc/lxc/auto LXC_AUTO="true" # Leave USE_LXC_BRIDGE as "true" if you want to use lxcbr0 # for your containers. Set to "false" if you'll use virbr0 # or another existing bridge, or maсvlan to your host's NIC. USE_LXC_BRIDGE="true" # If you change the LXC_BRIDGE to something other than # lxcbr0, then you will also need to update your # /etc/lxc/lxc.conf as well as the configuration #(/var/lib/lxc//config) for any containers # already created using the default config to reflect # the new bridge name. # If you have the dnsmasq daemon installed, you'll also # have to update /etc/dnsmasq.d/lxc and restart the system # wide dnsmasq daemon. LXC_BRIDGE="lxcbr0" LXC_ADDR="192.168.215.1" LXC_NETMASK="255.255.255.0" LXC_NETWORK="192.168.215.0/24" LXC_DHCP_RANGE="192.168.215.2,192.168.215.254" LXC_DHCP_MAX="253" LXC_SHUTDOWN_TIMEOUT=120 service lxc-net restart && service lxc restart lxc-start --name LXC_Machine1 lxc-console --name LXC_Machine1 --tty=1 lxc.mount.entry = /lib /export/Data1/lib none ro,bind 0 0 lxc.cap.drop = sys_admin Листинг 4. Внутри контейнера можно запретить использовать некоторые функции Linux даже суперпользователю root@LXC_Machine1:~# mkswap ./zerofile lxc-cgroup -n LXC_Machine1 memory.limit_in_bytes 30000000 lxc.cgroup.memory.limit_in_bytes = 300000000 lxc.cgroup.cpuset.cpus = 0-1,3 lxc-cgroup --name LXC_Machine1 blkio.weight 500 lxc-create -n my-armhf-container -t ubuntu -- -a armhf ----------------------------------------------------------------------------------------------------------------- Межсетевой экран Linux. Взгляд изнутри Валентин Синицын int ip_rcv(…) { … return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); } int ret = 1; if (nf_hooks_active(pf, hook)) ret = nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh); if (ret == 1) ret = okfn(skb); return ret; int nf_hook_slow(…) { … elem = &nf_hooks[pf][hook]; next_hook: verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev, outdev, &elem, okfn, hook_thresh); if (verdict == NF_ACCEPT || verdict == NF_STOP) { ret = 1; } else if ((verdict & NF_VERDICT_MASK) == NF_DROP) { kfree_skb(skb); ret = NF_DROP_GETERR(verdict); if (ret == 0) ret = -EPERM; } else if … … return ret; } list_for_each_continue_rcu(*i, head) { struct nf_hook_ops *elem = (struct nf_hook_ops *)*i; repeat: verdict = elem->hook(hook, skb, indev, outdev, okfn); if (verdict != NF_ACCEPT) { if (verdict != NF_REPEAT) return verdict; goto repeat; } } return NF_ACCEPT; static struct nf_hook_ops iptable_filter_ops[] __read_mostly = { { .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_FILTER, .hook = iptable_filter_hook, .owner = THIS_MODULE }, { .pf = NFPROTO_IPV4, .hooknum = NF_INET_FORWARD, .priority = NF_IP_PRI_FILTER, .hook = iptable_filter_hook, .owner = THIS_MODULE }, { .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_FILTER, .hook = iptable_filter_hook, .owner = THIS_MODULE } }; static int __init iptable_filter_init(void) { … nf_register_hooks(iptable_filter_ops, ARRAY_SIZE(iptable_filter)); … } return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_filter); do { … if (!ip_packet_match(ip, indev, outdev, &e->ip, acpar.fragoff)) { no_match: e = ipt_next_entry(e); continue; } xt_ematch_foreach(ematch, e) { acpar.match = ematch->u.kernel.match; acpar.matchinfo = ematch->data; if (!acpar.match->match(skb, &acpar)) goto no_match; } … t = ipt_get_target(e); /* Standard target? */ if (!t->u.kernel.target->target) { int v = ((struct xt_standard_target *)t)->verdict; if (v < 0) { /* Pop from stack? */ if (v != XT_RETURN) { verdict = (unsigned int)(-v) - 1; break; } } … } acpar.target = t->u.kernel.target; acpar.targinfo = t->data; verdict = t->u.kernel.target->target(skb, &acpar); if (verdict == XT_CONTINUE) e = ipt_next_entry(e); else /* Verdict */ break; } while (!acpar.hotdrop) iptables -I FORWARD -p tcp --dport 25 -j DROP return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, rt->dst.dev, ip_forward_finish); static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) { … th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); if (th == NULL) { pr_debug("Dropping evil TCP offset=0 tinygram.\n"); par->hotdrop = true; return false; } … if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], ntohs(th->dest), !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) return false; … return true; } if (unlikely(skb->nf_trace)) trace_packet(skb, hook, in, out, table->name, private, e); static unsigned int trace_tg(struct sk_buff *skb, const struct xt_action_param *par) { skb->nf_trace = 1; return XT_CONTINUE; } iptables -t raw -I PREROUTING -p tcp --dport 25 -j TRACE # dmesg int nf_hook_slow(…) { … elem = &nf_hooks[pf][hook]; next_hook: verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev, outdev, &elem, okfn, hook_thresh); … } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { int err = nf_queue(skb, elem, pf, hook, indev, outdev, okfn, verdict >> NF_VERDICT_QBITS); if (err < 0) { … if (err == -ESRCH && (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) goto next_hook; kfree_skb(skb); } } … return ret; } void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) { … if (verdict == NF_ACCEPT) { afinfo = nf_get_afinfo(entry->pf); if (!afinfo || afinfo->reroute(skb, entry) < 0) verdict = NF_DROP; } if (verdict == NF_ACCEPT) { next_hook: verdict = nf_iterate(&nf_hooks[entry->pf][entry->hook], skb, entry->hook, entry->indev, entry->outdev, &elem, entry->okfn, INT_MIN); } switch (verdict & NF_VERDICT_MASK) { case NF_ACCEPT: case NF_STOP: local_bh_disable(); entry->okfn(skb); local_bh_enable(); break; case NF_QUEUE: … case NF_STOLEN: … default: kfree_skb(skb); … } … } static unsigned int nfqueue_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_NFQ_info *info = par->targinfo; unsigned int ret = NF_QUEUE_NR(info->queuenum); if (info->bypass) ret |= NF_VERDICT_FLAG_QUEUE_BYPASS; return ret; } #define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE) #!/usr/bin/env python # -*- coding: utf-8 -*- import nfqueue from socket import AF_INET # Возвращает true, если пакет следует принять def classify(data): … return 1 # Обработчик очереди def callback(i, payload): # Получаем тело пакета data = payload.get_data() # Вызываем функцию-классификатор if classify(data): # NF_ACCEPT, если классификатор вернул true payload.set_verdict(nfqueue.NF_ACCEPT) else: # NF_DROP – в противном случае payload.set_verdict(nfqueue.NF_DROP) return 1 # Подготовительные мероприятия q = nfqueue.queue() q.set_callback(callback) q.open() # Отменяем регистрацию текущего обработчика очереди для AF_INET q.unbind(AF_INET) # Регистрируем себя в качестве обработчика q.bind(AF_INET) # Создаем очередь с номером 0 q.create_queue(0) # Обрабатываем приходящие пакеты try: q.try_run() except KeyboardInterrupt: # Закрываем очередь перед выходом q.close() ----------------------------------------------------------------------------------------------------------------- Создаем отказоустойчивую систему. Используем зеркалирование БД MS SQL Server 2008 Виктор Юханов declare @ReturnCode numeric(1,0) /*ReturnCode 1 - Переключение выполнено, 0 - Переключение не выполнено */ if (exists(select mirroring_state, mirroring_role from sys.database_mirroring where mirroring_state is not null and mirroring_role =1)) BEGIN set @ReturnCode = 1; END else set @ReturnCode = 0; if @ReturnCode = 1 BEGIN /*Включаем job по переключению настроек rts-серверов */ EXEC msdb.dbo.sp_update_job @job_name = N'Change rts settings', @enabled = 1 ; /*Отключаем текущий job*/ EXEC msdb.dbo.sp_update_job @job_name = N'Mirroring monitor', @enabled = 0 ; END @echo off rem Подключаемся по ftp к rts-серверам и скачиваем файлы с настройками ftp -s:c:\MB\dwl_rts1.txt ftp -s:c:\MB\dwl_rts2.txt rem Заменяем в файле настроек адрес сервера БД setLocal EnableDelayedExpansion for /f "eol=; tokens=*" %%a in (c:\MB\conf_close_old.php) do (set str=%%a set str=!str:DB1=DB2! echo !str!>>c:\MB\conf_close.php) rem Загружаем измененные файлы на rts-серверы ftp -s:c:\MB\upl_rts1.txt ftp -s:c:\MB\upl_rts2.txt rem Удаляем временные файлы del c:\MB\*.php Структура файла dwn_rts1.txt: open get "conf_close.php" "c:\MB\conf_close_old.php" bye Структура файла upl_rts1.txt: open send "c:\MB\conf_close.php" "conf_close.php" bye EXEC msdb.dbo.sp_send_dbmail @profile_name = 'DB2_Profile', @recipients = 'help@help.ru', @copy_recipients='help2@help.ru', @importance='HIGH', @subject = 'RTS-SERVER SETTINGS CHANGED ON MIRROR', @body='Вследствие технических проблем на сервере БД DB1 настройки сервера приложений были АВТОМАТИЧЕСКИ изменены для работы с резервным сервером DB2. Пожалуйста, проверьте доступность сервиса. При необходимости вручную измените название сервера БД на DB2 в настроечных файлах на серверах rts1 и rts2.' EXEC msdb.dbo.sp_update_job @job_name = N'Principal monitor', @enabled = 1; EXEC msdb.dbo.sp_update_job @job_name = N'Change rts settings', @enabled = 0 ; declare @ReturnCode numeric(1,0) /* ReturnCode 1 – Переключение выполнено 0 – Переключение не выполнено */ if (exists(select mirroring_state, mirroring_role from sys.database_mirroring where mirroring_state is not null and mirroring_role =2)) BEGIN set @ReturnCode = 1; END else set @ReturnCode = 0; if @ReturnCode = 1 BEGIN /*Включаем job по переключению настроек rts-сервера*/ EXEC msdb.dbo.sp_update_job @job_name = N' Switch settings on principal', @enabled = 1 ; /*Отключаем текущий job*/ EXEC msdb.dbo.sp_update_job @job_name = N'Principal monitor', @enabled = 0 ; END ap(config)# interface Dot11Radio0.1 ap(config-if)# encapsulation dot1Q 2 << указываем номер vlan ap(config-if)# bridge-group 2 bridge-group 2 subscriber-loop-control bridge-group 2 block-unknown-source no bridge-group 2 source-learning no bridge-group 2 unicast-flooding bridge-group 2 spanning-disabled ap(config)# interface Dot11Radio0.2 ap(config-if)# encapsulation dot1Q 1 native << оставляем vlan 1 не тегированной ap(config-if)# bridge-group 1 ap(config)# interface GigabitEthernet0.1 ap(config-if)# encapsulation dot1Q 2 ap(config-if)# bridge-group 2 ap(config)# interface GigabitEthernet0.2 ap(config-if)# encapsulation dot1Q 1 native ap(config-if)# bridge-group 1 ap# sh vlans ap(config)# bridge irb conf t int fa 0/1 switchport mode trunk switchport trunk encapsulation dot1q switchport trunk native vlan 1 switchport trunk allowed vlan 1,2 Office-R(config)# interface FastEthernet0/0.1 Office-R(config-if)# encapsulation dot1Q 2 Office-R(config-if)# ip address 10.10.10.254 255.255.255.0 Office-R(config-if)# ip access-group FROM_GUEST-LAN-WIFI in << создадим позже Office-R(config-if)# ip nat inside Office-R(config)# ip dhcp pool GUEST-LAN-WIFI Office-R(dhcp-config)# import all Office-R(dhcp-config)# network 10.10.10.0 255.255.255.0 Office-R(dhcp-config)# default-router 10.10.10.254 Office-R(dhcp-config)# dns-server 8.8.8.8 8.8.4.4 Office-R(config)# ip access-list extended FROM_GUEST-LAN-WIFI Office-R(config-ext-nacl)# deny ip 10.10.10.0 0.0.0.255 192.168.0.0 0.0.0.255 Office-R(config-ext-nacl)# deny ip 10.10.10.0 0.0.0.255 25.1.1.0 0.0.0.255 Office-R(config-ext-nacl)# permit udp any any eq bootps Office-R(config-ext-nacl)# permit icmp 10.10.10.0 0.0.0.255 any echo Office-R(config-ext-nacl)# permit udp 10.10.10.0 0.0.0.255 any eq domain Office-R(config-ext-nacl)# permit tcp 10.10.10.0 0.0.0.255 any eq ftp domain www 443 Office-R(config-ext-nacl)# deny ip any any Office-R(config)# ip access-list extended NAT Office-R(config-ext-nacl)# 90 deny ip 10.10.10.0 0.0.0.255 192.168.0.0 0.0.0.255 Office-R(config-ext-nacl)# 95 permit ip 10.10.10.0 0.0.0.255 any ----------------------------------------------------------------------------------------------------------------- Магия doveadm Рашид Ачилов dsync <ключи> [mirror|backup] <локация 2> mail_location = mdbox:~/mail cd /usr/home/severtsev-rv mkdir tmp dsync -f -u severtsev-rv mirror mbox:/usr/home/severtsev-rv/tmp cp /var/mail/sevretsev-rv tmp cat tmp/inbox tmp/severtsev-rv tmp/newbox mv -f tmp/newbox tmp/inbox chown severtsev-rv:mail tmp/inbox rm -f tmp/severtsev-rv rm -rf /usr/home/severtsev-rv/mail dsync -f -u severtsev-rv mirror mbox:/usr/home/severtsev-rv/tmp &BCEEPwQwBDwENQRABEs- dsync -f -m INBOX backup mbox:/tmp/1 dsync -fR -m inbox backup mbox:/tmp/1 dsync -fDv -u severtsev-rv -m Drafts backup mbox:/tmp/2 PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin # Загрузка библиотеки. Используется только функция checkstop_userid. Эта функция проверяет UID и, если он не равен 0, завершает работу . commonlib.sh # Встроенная справка. Эта функция обрезана usage() { ... } # Основная функция – преобразование ящика формата mbox c проверкой места расположения домашнего каталога convertMboxWithCheckHomedir() { # Создать резервную копию ящика cp $_onebox $mname # Проверить наличие домашнего каталога и создать, если надо if [ ! -e $uhome ]; then install -d -m 0700 -g $ugid -o $uid $uhome fi # Создать рабочую папку cmdfile=`mktemp -d tempfldr.XXXXXX` chmod 1777 $cmdfile mv $_onebox $cmdfile # Преобразовать ящик, используя рабочую папку $dsync -u $uname -R -f backup mbox:/var/mail/$cmdfile # Проверить код возврата # Если INBOX существовал, то преобразованная почта останется в папке с именем равным имени пользователя, иначе будет переименована в INBOX if [ $? -ne 0 ]; then print_color $red "Conversion unsuccesful, mailbox skipped" print_color $blue "Restore mailbox from $mname, resolve problems and try again" else if [ -e $uhome/mail/mailboxes/INBOX ]; then print_color $violet "INBOX exist, converted mail will drop in folder $uname" else mv $uhome/mail/mailboxes/$uname $uhome/mail/mailboxes/INBOX fi fi rm -rf $cmdfile } mailspool="/var/mail" firstuid=1000 lastuid=65000 firstaduid=70000 # Проверить UID, если он не 0 – завершить работу checkstop_userid # Разобрать командную строку (обработка опущена) args=`getopt nf:l:d:m: $*`; ... dsync=`which dsync` if [ ${#dsync} -eq 0 ]; then print_color $red "Dovecot is not installed (dsync binary was not found)" exit 100 fi doveproc=`ps -axww | grep dovecot` if [ ${#doveproc} -eq 0 ]; then print_color $red "Dovecot not started, start it first" exit 117 fi cd $mailspool mboxes=`ls -1I` for _onebox in $mboxes do # Пустой почтовый ящик (имеет нулевой размер) – пропустить if [ ! -s $_onebox ]; then print_color $yellow "Skipped zero-sized mailbox $_onebox..." continue fi # Получить информацию о пользователе # Создаем переменные uname, uid, ugid, uhome и присваиваем им соответственно поля $1, $3, $4 и $9 uinfo=`pw usershow $_onebox` bla=`echo $uinfo | awk 'BEGIN {FS=":"} {printf "uname=%s;uid=%s;ugid=%s; uhome=%s",$1,$3,$4,$9}'` eval $bla # Если firstuid < UID < lastuid – обычный локальный пользователь if [ $uid -ge $firstuid ] && [ $uid -le $lastuid ]; then print_color $green "Convert mailbox for local user $uname..." convertMboxWithCheckHomedir # Если UID > firstaduid – обычный доменный пользователь elif [ $uid -ge $firstaduid ]; then print_color $green "Convert mailbox for domain user $uname..." convertMboxWithCheckHomedir # Иначе пропускем – это псевдопользователь else print_color $magenta "User $uname with UID $uid skipped..." continue fi done doveadm expunge -u severtsev-rv mailbox INBOX DELETED for _oneuser in $userlist do cd $_oneroot/$_oneuser # Если не существует подкаталога mail или в нем нет # подкаталога mailboxes – у этого пользователя еще нет # почты, его можно пропустить if [ ! -e mail ] || [ ! -e mail/mailboxes ]; then echo "Skipping user $_oneuser..." continue else cd mail/mailboxes fi echo "Expunge mail for $_oneuser user..." boxlist=`ls -1` # Выполняем expunge для каждой почтовой папки for _onebox in $boxlist do $dovepath expunge -u $_oneuser mailbox $_onebox DELETED done # Выполняем purge $dovepath purge -u $_oneuser done doveadm purge doveadm purge -u severtsev-rv # doveadm search -u severtsev-rv mailbox INBOX subject test # Эта переменная содержит символ «перевод строки», он же \n $newline=" " list=`doveadm search -u $user mailbox INBOX subject $subject` IFS=$newline for _onecell in $list do IFS=$saveifs set $_onecell doveadm fetch -u $user body mailbox-guid $1 uid $2 > msg.$2 done doveadm -Dv import -u ldapread mbox:/tmp/1 "" mailbox INBOX subject test doveadm -Dv move -u severtsev-rv Drafts mailbox INBOX subject test doveadm index -u severtsev-rv INBOX doveadm force-resync -u ldapread INBOX doveadm mailbox create -u severtsev-rv -s Trash/FullJunk doveadm mailbox list -8 -u severtsev-rv -s Root messages/2011 ----------------------------------------------------------------------------------------------------------------- Windows Server 2012. Новое в групповых политиках Сергей Яремчук Gpupdate.exe /force PS> New-GPO –Name "Configure firewall rules for remote gpupdate" –StarterGpoName "Group Policy Remote Update Firewall Ports" | New-GPLink –target "dc=example,dc=org" –LinkEnabled yes Import-Module GroupPolicy Get-Help *-gp* Get-Command -Module GroupPolicy PS > Invoke-GPUpdate -Computer <имя компьютера> PS> Get-ADComputer –filter * -Searchbase "cn=computers, dc=example,dc=org" | foreach{ Invoke-GPUpdate –computer $_.name –force –-RandomDelayInMinutes 0} ----------------------------------------------------------------------------------------------------------------- Windows Server 2012. Windows PowerShell Web Access Иван Коробко https://ИмяСервера/ИмяСайта Листинг 1. Установка компонента Windows PowerShell Web Access и необходимых компонентов $webAccess=Get-WindowsFeature | ? {$_.displayname -like "Windows PowerShell Web Access"} if ($webAccess.installed -eq $false) { $obj = Install-WindowsFeature -Name $webAccess.Name -IncludeManagementTools {Write-Host $obj.ExitCode} } else {Write-Host "Already Installed"} Add-PswaAuthorizationRule –UserName Test/AIvanov -ComputerName Test/Shiskin -ConfigurationName Conf1 Add-PswaAuthorizationRule –UserName Test/AIvanov -ComputerName * -ConfigurationName * Add-PswaAuthorizationRule –UserGroupName test\admins -ComputerName * -ConfigurationName * Add-PswaAuthorizationRule –UserName * -ComputerName * -ConfigurationName * Add-PswaAuthorizationRule –UserGroupName * -ComputerGroupName * -ConfigurationName * Add-PswaAuthorizationRule * * * Листинг 3. Создание PowerShell Web Access сайта Install-PswaWebApplication -WebApplicationName "PS Web Site" -UseTestCertificate Листинг 4. Работа с серверами домена $cred=Get-Credential -UserName "test\administrator" -Message "Enter Password" $session=New-PSSession -ComputerName . -Credential $cred -Name "Session1" Invoke-Command -Session $session -ScriptBlock { $filename= "c:\featureList.txt" Get-WindowsFeature | Format-Table -Property "displayname" | Out-File $filename -Encoding unicode -Force Get-Content $filename } Remove-PSSession $session ----------------------------------------------------------------------------------------------------------------- Разделяемые файловые ресурсы Windows. Доступ через HTTPS. Часть 2. Адаптация AjaXplorer Григорий Антропов # Chown -R apache: apache /opt/repositories // "PATH" => "AJXP_DATA_PATH/files" "PATH" => "/opt/repositories/defaultfiles" // "PATH" => "AJXP_DATA_PATH/personal/AJXP_USER" "PATH" => "/opt/repositories/personal/AJXP_USER" # Mount //Server_FQDN/Share1 //Server_FQDN/Share1 /opt/repositories/winshare cifs credentials=/etc/samba/winmount,uid=48,gid=48 0 0 # Mount //Server_FQDN/Users //Server_FQDN/Users /opt/repositories/personal cifs credentials=/etc/samba/winmount,uid=48,gid=48 0 0 username=mount_ajax password=secret # chown root:root /etc/samba/winmount # chmod 600 /etc/samba/winmount # mount -o remount //Server_FQDN/Share1 # mount -o remount //Server_FQDN/Users ----------------------------------------------------------------------------------------------------------------- IDS Suricata. Повышаем производительность в режиме NFQ Юрий Денисов iptables -A FORWARD -j NFQUEUE suricata -D -c /etc/suricata/suricata.yaml -q 0 -q 1 iptables -A FORWARD -j NFQUEUE -queue-balance 0:1 ----------------------------------------------------------------------------------------------------------------- Ускорение резервирования базы данных Павел Одинцов my $mysql_dump = "mysqldump -u$mysql_username -p$mysql_password --skip-lock-tables --add-drop-table "; print "Экспортируем базу данных\n"; `$mysql_dump $mysql_db_name > $db_backup_path`; print "Архивируем базу данных\n"; `tar -cpzf $db_backup_path_archive $db_backup_path`; my $mysql_dump = "mysqldump -u$mysql_username -p$mysql_password --skip-lock-tables --add-drop-table "; print "Экспортируем и архивируем базу данных\n"; `$mysql_dump $mysql_db_name | gzip > $db_backup_path_archive`; my $mysql_dump = "mysqldump -u$mysql_username -p$mysql_password --skip-lock-tables --add-drop-table "; print "Экспортируем и архивируем базу данных используя pigz\n"; `$mysql_dump $mysql_db_name | pigz > $db_backup_path_archive`; ----------------------------------------------------------------------------------------------------------------- Создание key/value-хранилищ данных. Часть 4. Сетевое взаимодействие с клиентом (Продолжение) Александр Календарев static void memcached_client(EV_P_ ev_io *io, int revents) { memcache_ctx *mctx = ( memcache_ctx*)io; if (revents & EV_READ) { / * в этом месте идет обработка ввода данных */ else if (revents & EV_WRITE) { / * в этом месте идет обработка вывода данных */ } else { / * что-то пошло не так, обработка ошибки */ } } typedef struct { ev_io io; /* дескриптор контекста libev */ /* буфер для обработки команд ввода */ char cmd[MAX_COMMAND_LEN]; int cmd_len; /* длина входных данных */ struct obuffer response; /* буфер ввода/вывода */ char *value; /* промежуточный буфер для вывода */ int value_len; /* длина выходных данных */ . . . /* прочие данные */ } memcache_ctx; int end = 0; int i = mctx->cmd_len ? mctx->cmd_len - 1 : 0; size_t bytes =0; /* Осуществляется цикл ввода, пока не встретится последовательность'\r\n'(или '\n') */ while (mctx->cmd_len < MAX_COMMAND_LEN && !end) { bytes = read(io->fd, mctx->cmd + mctx->cmd_len, MAX_COMMAND_LEN - mctx->cmd_len); if (bytes > 0) { if (bytes > BUFSIZE) { goto send_error; } mctx->cmd_len += bytes; while (i < mctx->cmd_len - 1) { if (mctx->cmd[i] == '\r' && mctx-> ↵ cmd[i+1] == '\n') { end = i + 2; mctx->cmd[i] = 0; break; } i++; } } else if (bytes == -1) { /* Конец ввода неблокируемого соединения */ if (errno == EAGAIN) break; /* Вызвано прерыванием функции */ if (errno == EINTR) continue; /* Другая ошибка ввода/вывода, закрываем соединение */ goto disconnect; } else goto disconnect; } /* Проверяем соответствие принятых длин */ if (mctx->cmd_len >= MAX_COMMAND_LEN && !end) goto disconnect; /* если не принято никаких данных, то выход по timeout */ if (!end) { set_client_timeout(io, RECV_TIMEOUT); return; } /* Парсинг команд */ if (strncmp(mctx->cmd, "set ", 4) == 0) { … /* Парсинг параметров команды "set" */ else if (strncmp(mctx->cmd, "get ", 4) == 0) { … /* Парсинг параметров команды "get" */ else if (strncmp(mctx->cmd, "delete ", 7) == 0) { … /* Парсинг параметров команды "delete" */ /* Команда quit,Закрываем соединение */ } if (strncmp(mctx->cmd, "quit", 4) == 0) { goto disconnect; /* Команда term,Завершение цикла прослушивания и программы в целом */ } if (strncmp(mctx->cmd, "term", 4) == 0) { goto exit; } … /* Обработка прочих команд */ /* Вывод в случае ошибки, строка "ERROR\r\n" */ send_error: obuffer_init(&mctx->response, "ERROR\r\n", sizeof("ERROR\r\n") - 1); goto send_reply; /* Вывод в случае окончания ввода, строка "END\r\n" */ send_end: obuffer_init(&mctx->response, "END\r\n", sizeof("END\r\n") - 1); send_reply: /* Вывод результата */ set_client_timeout(io, RECV_TIMEOUT); mctx->cmd_len = 0; ev_io_stop(EV_A_ io); ev_io_set(io, io->fd, EV_WRITE); ev_io_start(EV_A_ io); return; disconnect: /* Закрытие соединения */ close_io(EV_A_ io); memcached_client_free(mctx); return; exit: /* Закрытие всех соединений */ close_all(EV_A ); ev_unloop(EV_A_ EVUNLOOP_ALL); } #define OK 0 #define ERR 1 #define END 2 if (strncmp(mctx->cmd, "get ", 4) == 0) { switch (memcache_get(EV_A_ mctx)) { case OK: goto send_reply; case ERR: goto send_error; case END:goto send_end; } … int memcache_get(EV_P_memcache_ctx *mctx) { int len=0; char *key = mctx->cmd + 4; char *p = key; while(1) { /* выделение ключа, поиск символа «пробел» */ if (*p == ' ' || *p == '\r' || *p == '\n' ) break; /* ошибка при выделении ключа, не найден пробел */ if (len > MAX_COMMAND_LEN-5) return 1; p++; len++; } *(key+len-1) = '\0'; ctx* ctx = ev_userdata(EV_A); memset(mctx->value, BUFSIZE, '\0'); TCHDB *hdb = ctx->hdb; void * pdata = tchdbget2(hdb, key); if(!pdata) { ctx->ecode = tchdbecode(hdb); /* Если данных нет – ключ не найден, то вывод “END\r\n” */ if (ctx->ecode == TCENOREC) goto end; /* Прочие ошибки Tokkyo Cabinet, вывод “ERROR\r\n” */ goto error; } /* Реализация протокола memcached, вывод в формате: VALUE key flag длина\r\n данные \r\n” */ len = snprintf(mctx->value, BUFSIZE, "VALUE %s 0 %d\r\n%s\r\nEND\r\n", key, (int)strlen(pdata), (char*)pdata ); if (pdata) free(pdata); obuffer_init(&mctx->response, mctx->value, len); return OK; error: return ERR; end: return END; } memcache_set(EV_P_memcache_ctx *mctx, int readed, int end) { unsigned flags, exptime, bytes; char *p; p = mctx->cmd + 4; char* key = malloc(BUFSIZE); if (sscanf(p, "%s %u %u %u", key, &flags, &exptime, &bytes) != 4) { free(key); return 1; } mctx->flag = flags; mctx->exptime = exptime; int len = mctx->cmd_len - end; mctx->key = key; /* Контролируем размер входных данных, например, не более 1М */ if (!bytes || bytes > 1 << 20) { return 1; } mctx->cmd_len = 0; mctx->data_size = bytes; mctx->value_len = len; /* Если прочтено больше, чем указатель end на \r\n, т.е. все данные, а не только первая строка */ if (readed > end) { memcpy(mctx->value,mctx->cmd+end, readed-end); mctx->value[readed-end] = '\0'; ctx* ctx = ev_userdata(EV_A); /* Вызываем функцию-обработчик set */ if(memcached_process_set(mctx, ctx)) { mctx->key = NULL; free(key); return 1; } /* Пишем в выходной буфер об удачном завершении обработки: "STORED\r\n" */ obuffer_init(&mctx->response, "STORED\r\n", sizeof("STORED\r\n") - 1); mctx->key = NULL; free(key); return 0; } } /* Если прочтена только первая строка, то мы переопределяем функцию обработчика на функцию memcached_cb_set() и уже в ней обрабатываем принятые для set данные */ set_client_timeout((ev_io *)mctx, RECV_TIMEOUT); ev_set_cb((ev_io *)mctx, memcached_cb_set); return 2; } /* Обработка данных по вызову дополнительного обработчика на команду set */ static void memcached_cb_set(EV_P_ev_io *io, int revents) { memcache_ctx *mctx = (memcache_ctx*)io; /* Цикл обработки ввода данных, по заданному количеству байт. Код аналогичен обработчику memcached_client() */ while (mctx->value_len < mctx->value_size) { size_t bytes = read(io->fd, mctx->value + mctx->value_len, mctx->value_size - mctx->value_len); if (bytes > 0) { if (bytes > BUFSIZE) { goto error; } mctx->value_len += bytes; } else if(bytes == -1){ if (errno == EAGAIN) return; if (errno == EINTR) continue; goto disconnect; } } /* Установка заданного timeout*/ set_client_timeout(io, RECV_TIMEOUT); mctx->value[mctx->value_size-2] = 0; ctx* ctx = ev_userdata(EV_A); /* Вызов обработчика команды set */ int ret = memcached_process_set(mctx, ctx); /* Обработка результата */ switch (ret) { case 1 : goto error; break; case 2 : goto notstored; break; } /* Чистка данных */ if(mctx->key) { free(mctx->key); mctx->key = NULL; } mctx->cmd_len = 0; /* Переопределение функции обратного вызова на стандартный обработчик всех команд */ ev_io_stop(EV_A_ io); ev_set_cb(io, memcached_client); ev_io_set(io, io->fd, EV_WRITE); obuffer_init(&mctx->response, "STORED\r\n", sizeof("STORED\r\n") - 1); ev_io_start(EV_A_ io); return; /* Обработка данных команды set, т.е. непосредственно реализация логики команды set */ static int memcached_process_set(memcache_ctx *mctx, ctx* ctx) { TCHDB *hdb = ctx->hdb; if ( !tchdbput2(hdb, mctx->key, mctx->value) ) { ctx->ecode = tchdbecode(hdb); return 1; } free(mctx->key); mctx->key = NULL; memset(mctx->value, BUFSIZE, '\0'); return 0; } ev_io_init(&rep_io, replica_on_connect, sock, EV_READ); ev_io_start(loop, &rep_io); ----------------------------------------------------------------------------------------------------------------- Обмен данными между приложениями Алексей Вторников (3) Джек Лондон, США, Мартин Иден (4) Виктор Гюго, Франция, Человек, который смеется (3) Федор Достоевский, Россия, Идиот (2) Владимир Набоков, Лолита (3) Владимир Набоков, , Лолита (3) "Виктор Гюго", "Франция", "Человек, который смеется" (3) Василий Ардаматский, СССР, ""Сатурн" почти не виден" (3) Джек Лондон^США^Мартин Иден (3) Виктор Гюго^Франция^Человек, который смеется (3) Федор Достоевский^Россия^Идиот (3) Владимир Набоков^^Лолита (3) Василий Ардаматский^СССР^"Сатурн" почти не виден // Построчное считывание txt-файла private void loadSource (File file) { try { Scanner in = new Scanner (new FileReader (file)); while (in.hasNextLine ()) { // Обработать строку processLine (in.nextLine ()); } in.close (); } catch (IOException e) { System.err.println ("Unable to load file " + ↵ file.getPath ()); System.exit (1); } } // Разбиение строки на данные (токены) private void processLine (String line) { // Разделитель - "^" StringTokenizer st = new StringTokenizer (line, "^"); int tCount = st.countTokens (); for (int i = 0; i < tCount; i++) { // Выделить данные (токен) String token = st.nextToken ().trim (); // И передать его для дальнейшей обработки ... } } # Свойства подключения к СУБД Pervasive pervasive.driver = com.pervasive.jdbc.v2.Driver pervasive.server = localserver pervasive.dbname = abs pervasive.port = 1583 pervasive.encoding = cp866 # Свойства подключения к СУБД PostgreSQL postgresql.driver = org.postgresql.Driver postgresql.server = remoteserver postgresql.dbname = retail postgresql.user = admin postgresql.pswd = qwerty12345 ... private String pervDriver, pervConnectionString, postDriver, postConnectionString; private int pervPort; ... // Загрузить свойства и сформировать строки подключений к серверам СУБД private boolean loadProperties () { Properties props = new Properties (); final String pf = "exchange.prop"; File file = new File (pf); if (!file.exists ()) { System.out.println ("Properties file " + pf + " not exists"); return false; } try { InputStream is = new BufferedInputStream (new FileInputStream (file)); props.load (is); is.close (); } catch (IOException ioe) { System.out.println ("Can not load properties file"); return false; } // Свойства подключения к СУБД Pervasive pervDriver = props.getProperty ("pervasive.driver"); try { pervPort = Integer.parseInt (props.getProperty ("pervasive.port")); } catch (NumberFormatException nfe) { System.out.println ("Pervasive connection port unknown"); return false; } pervConnectionString = "jdbc:pervasive://" + props.getProperty ("pervasive.server") + ":" + pervPort + "/" + props.getProperty ("pervasive.dbname") + ";encoding=" + props.getProperty ("pervasive.encoding"); // Свойства подключения к СУБД PostgreSQL postDriver = props.getProperty ("postgresql.driver"); postConnectionString = "jdbc:postgresql://" + props.getProperty ("postgresql.server") + "/" + props.getProperty ("postgresql.dbname") + "?user=" + props.getProperty ("postgresql.user") + "&password=" + props.getProperty ("postgresql.pswd"); return true; } ... private Connection pervConnection, postConnection; ... // Создать соединения с СУБД private boolean createConnections () { try { Class.forName (pervDriver); pervConnection = DriverManager.getConnection (pervConnectionString); Class.forName (postDriver); postConnection = DriverManager.getConnection (postConnectionString); return true; } catch (ClassNotFoundException ex) { System.out.println ("Class not found: " + ex.getMessage ()); return false; } catch (SQLException ex) { System.out.println ("SQLException: " + ex.getMessage ()); System.out.println ("SQLState : " + ex.getSQLState ()); System.out.println ("VendorError : " + ex.getErrorCode ()); return false; } } ... // Закрыть соединения с СУБД private void closeConnections () { try { pervConnection.close (); postConnection.close (); } catch (SQLException ex) { System.out.println ("SQLException: " + ex.getMessage ()); System.out.println ("SQLState : " + ex.getSQLState ()); System.out.println ("VendorError : " + ex.getErrorCode ()); } finally { pervConnection = postConnection = null; } } // Таймер сканирования class Timer extends Thread { private Thread gettime; private SimpleDateFormat f = new SimpleDateFormat ("dd-MM-yyyy kk:mm:ss", Locale.US); private int timeout; Timer (int timeout) { this.timeout = timeout; } public void start () { gettime = new Thread (this); gettime.start (); } public void run () { while (true) { try {gettime.sleep (timeout);} catch (InterruptedException ie) {continue;} // Проверить активность соединений if (pervConnection != null && postConnection != null) { // Начать обмен } else { System.out.println ((f.format ( new java.util.Date ()) + " connection lost. Exit application"); System.exit (1); } } } } ... // Сканирование с периодичностью 5000 мс (5 с) Timer t = new Timer (5000); t.start (); ... String sql = "SELECT * FROM postdeals WHERE status = 0 AND security = 'OK'" ... try { // Получить данные из источника Statement postStatement = postConnection.createStatement (); ResultSet rs = postStatement.executeQuery (sql); // Загрузить данные в приемник ... } catch (SQLException sqle) { System.out.println ("SQLException: " + sqle.getMessage ()); System.out.println ("SQLState : " + sqle.getSQLState ()); System.out.println ("VendorError : " + sqle.getErrorCode ()); System.exit (1); } ... ... // Загрузить данные в приемник Statement pervStatement = pervConnection.createStatement (); while (rs.next ()) { // Формируем SQL-запрос на добавление новых данных String updSQL = "INSERT INTO pervdeals VALUES ("rs.getString ("id") + "," + rs.getDouble ("summa" + "," + ... +")"; int res = pervStatement.executeUpdate (updSQL); } rs.close (); pervStatement.close (); postStatement.close (); ... -----------------------------------------------------------------------------------------------------------------