← На главную

Мини заметки – выпуск 6

Главные темы шестого выпуска: комбинаторика на Haskell, исправление кодировки без использования iconv, полуавтоматическая поисковая оптимизация заголовков, настройка unbound и pptp, а также создание мгновенного снимка файловой системы под FreeBSD. Предыдущие выпуски: первый, второй, третий, четвертый и пятый.

1. Размещение «шариков» по «коробкам» на Haskell

Дано N разноцветных шариков, M коробок различной вместимости, а также стол бесконечной вместимости. Нужно получить список всех возможных размещений (не путать с размещением в комбинаторике) шариков по коробкам и столу. Вместо шариков могут быть яблоки или посылки, а вместо коробок – корзины или грузовики, суть от этого не меняется. Решение на Haskell:

module Placements where import Data.List -- первый аргумент - список "шариков" -- второй аргумент - вместимость "коробок" placements :: Eq a => [a] -> [Int] -> [[[a]]] placements itemsList [] = [[ itemsList ]] placements itemsList (maxItems:maxItemsTail) = [ (s:t) | s <- seqList, t <- allTails s] where -- все варианты размещения "шариков" в текущей "коробке" seqList = filter (\s -> length s <= maxItems) $ subsequences itemsList -- все варианты размещения "шариков" -- в остальных "коробках" и на "столе" allTails s = placements (itemsList \\ s) maxItemsTail

Если вам попадалось готовое решение, поделитесь, пожалуйста, ссылкой.

2. Настройка unbound

Поднятие локального DNS сервера может быть целесообразным по крайней мере в двух случаях. Первый – когда вам хочется ускорить загрузку веб-страниц за счет кэширования ответов DNS сервера провайдера. Если у провайдера тормозной DNS, прирост скорости весьма ощутим. Второй случай – когда часть доменных имен вы хотите резолвить на одних DNS серверах, а часть – на других, например, если вы работаете дома через VPN.

Для этих целей идеально подходит DNS сервер unbound:

pkg_add -r unbound

После установки пишем в /usr/local/etc/unbound/unbound.conf что-то вроде:

server: # принимаем пакеты только с локалхоста interface: 127.0.0.1 access-control: 127.0.0.0/8 allow # минимальный и максимальный TTL cache-min-ttl: 21600 cache-max-ttl: 86400 forward-zone: # за адресами доменов компании ходим по VPN name: "company.example.ru." forward-addr: 33.33.33.1 forward-addr: 33.33.33.2 forward-zone: # остальные домены резолвим у провайдера name: "." forward-addr: 192.168.0.1

В /etc/rc.conf прописываем:

unbound_enalbe="YES"

Запускаем:

/usr/local/etc/rc.d/unbound start

Проверяем:

tcpdump -i tun0 udp port 53 dig @127.0.0.1 intra.company.example.ru

В /etc/resolv.conf прописываем:

nameserver 127.0.0.1

Чтобы resolv.conf не перезаписывался благодаря DHCP провайдера, в /etc/dhclient.conf пишем:

interface "rl0" { supersede domain-name-servers 127.0.0.1; }

Некоторые приложения понадобится перезапустить, иначе они не увидят новых настроек.

3. Использование разных ssh-ключей для разных серверов

Что делать, если требуется получить доступ к N серверам с одной машины, используя различные identity file для каждого сервера?

Копируем закрытый ключ для i-го сервера, например, в ~/.ssh/id_rsa.i-server (также копируем открытый ключ в id_rsa.pub.i-server – чтобы не потерять), меняем права доступа на 600, а затем пишем в ~/.ssh/config:

Host *.i-server.example.ru IdentityFile ~/.ssh/id_rsa.i-server User myusername

Повторяем описанные действия для i от 1 до N.

4. Настройка pptp под FreeBSD

Устанавливаем pptpclient:

pkg_add -r pptpclient

Правим /etc/ppp/ppp.conf:

company: set authname mylogin set authkey mypassword set timeout 0 set ifaddr 0 0 # весь трафик - через VPN! # add default HISADDR # через VPN ходим только в заданную подсеть set 123.45.67.0/24 HISADDR # ... и к заданному хосту set 45.67.89.10 HISADDR

Указываем, через какой шлюз гнать трафик к VPN серверу:

route add -host 33.33.33.1 192.168.0.1

Проверяем:

pptp 33.33.33.1 company

Варнинги типа «/bin/ip: not found» игнорируем, логи смотрим в /var/log/ppp.log. По умолчанию в ppp.conf прописано «enable dns», что приводит к перезаписи resolv.conf. Если вы оставили эту опцию, то после разрыва соединения с VPN сервером должны вручную восстановить resolv.conf:

nameserver 192.168.0.1

Также после разрыва соединения понадобится восстановить шлюз по умолчанию:

route add default 192.168.0.1

Напоминаю, что посмотреть все роуты можно командой «netstat -nr».

5. Создание мгновенного снимка ФС во FreeBSD

Снапшоты файловой системы во FreeBSD можно создать несколькими способами. Наиболее простым, по моим представлениям, является следующий:

# pkg_add -r freebsd-snapshot $ echo "1111" > test.txt # snapshot make -g4 /usr:backup

Здесь флаг -g задает максимальное количество снапшотов. Если превысить это число, происходит автоматическая ротация.

# snapshot list /usr Filesystem User User% Snap Snap% Snapshot /usr 146GB 25.8% 346MB 0.1% backup.0 # mkdir /mnt/backup # snapshot mount /usr:backup.0 /mnt/backup # cat /mnt/backup/home/eax/test.txt 1111 $ echo "2222" > test.txt # cat /mnt/backup/home/eax/test.txt 1111 # snapshot umount /mnt/backup # ls -la /usr/.snap/ total 357652 drwxrwxr-x 2 root operator 512 4 май 11:40 . drwxr-xr-x 17 root wheel 512 14 окт 21:59 .. -r-------- 1 root operator 627341157960 4 май 12:12 backup.0 # snapshot make -g0 /usr:backup

Мгновенные снимки часто используются при резервном копировании. Например, если на сервере крутится некая СУБД, мы можем останавливаем ее (или временно запретить запись и скинуть все данные на диск), создать снапшот файловой системы, после чего снова запускаем СУБД (разрешить запись) и сделать резервную копию из снапшота.

В результате сервер не простаивает, а мы получаем резервную копию данных в непротиворечивом состоянии. За счет использования механизма copy-on-write создание мгновенного снимка происходит очень быстро (на то он и мгновенный), а место на диске расходуется с умом.

6. Полуавтоматическая поисковая оптимизации заголовков

Дан список URL и поисковых запросов, по которым продвигаются соответствующие страницы. Следующий скрипт проверяет, заголовки каких страниц следует оптимизировать:

#!/usr/bin/perl # check-header.pl v 0.1 # (c) Alexandr Alexeev 2012 | http://eax.me/ use strict; use warnings; use utf8; use Mojo::UserAgent; my $ua = Mojo::UserAgent->new(); while(my $line = <>) { chomp($line); my ($url, $query) = split /\t/, $line; utf8::decode($query); my $title; eval { $title = $ua->get($url) ->res->dom->html->head->title->text; }; if($@) { print "ERROR:\t$url\n"; next; } if(titleMatch($title, $query)) { print "OK:\t$url\n"; } else { utf8::encode($query); print "FIXME:\t$url\t$query\n"; } } sub titleMatch { my($title, $query) = @_; $title = lc $title; $query = lc $query; my @tmp = split /\s+/, $query; for my $q(@tmp) { return 0 if(index($title, $q) < 0); } return 1; }

Проверка довольно топорная – если страница продвигается по запросу «купить яблоки», то в title должны содержаться слова «купить» и «яблоки». Этот скрипт удобно использовать со скриптом из десятого пункта предыдущего выпуска мини-заметок.

7. Как скопировать музыку с CD под UNIX

В портах FreeBSD есть такая замечательная утилита audio/ripit, предназначенная для конвертирования музыки с CD дисков в MP3. Устанавливаем, немного правим /usr/local/bin/ripit.pl:

my $cddev = "/dev/cd0"; my $outputdir = "/home/user/cdrip";

Затем просто вставляем диск в CD привод и говорим из под рута «ripit». Музыка в формате MP3 магическим образом окажется в директории $outputdir.

8. Удобные сочетания клавиш в bash

Недавно коллега показал мне два очень удобных сочетания клавиш в bash. До этого я умел пользоваться только стрелочками Вверх и Вниз, а также клавишей Tab.

Сочетание Ctr+R позволяет быстро выполнять команды, набранные ранее. Жмем Ctr+R и вводим часть команды. Если bash предложил то, что мы хотели, жмем Enter. Если снова нажать Ctr+R, будет предложена другая команда. Нажатие Escape возвращает нас в нормальный режим.

Сочетание Alt+точка подставляет в вводимую команду последний использованный аргумент. Например, если выполнить команду «ls -la», затем набрать «cat » и нажать Alt+точка, команда превратиться в «cat -la». Повторное нажатие Alt+точка подставляет другие аргументы, используемые ранее. Это сочетание особенно удобно при выполнении нескольких команд над файлами, к которым мы обращаемся по абсолютным или длинным относительным путям.

Может, есть еще какие-то сочетания клавиш, которые мне давно следовало бы держать на вооружении?

9. Утилита sloth

Если какой-то фоновый процесс съедает много ресурсов, а (re)nice не помогает, попробуйте утилиту sloth:

pkg_add -r sloth

Эта программа бомбит процесс сигналами SIGSTOP и SIGCONT, что существенно замедляет его выполнение:

$ time md5 PCBSD9.0-x86-DVD.iso MD5 (PCBSD9.0-x86-DVD.iso) = 805eebb4d24e2ada0466d5baa1997b45 real 1m46.598s user 0m17.914s sys 0m7.744s $ time sloth 5000 md5 PCBSD9.0-x86-DVD.iso MD5 (PCBSD9.0-x86-DVD.iso) = 805eebb4d24e2ada0466d5baa1997b45 real 7m16.996s user 0m18.523s sys 0m7.713s

Если для вашей системы не оказалось готового пакета, вы можете собрать sloth из исходников. Последние представляют собой сотню строк на языке Си.

10. Исправление кодировки без использования iconv

Недавно мне попался дамп базы данных с испорченной кодировкой. Декодер студии Артемия Лебедева определил, что исправить кодировку можно путем перекодирования текста из cp1252 в cp1251. Но ни enconv, ни iconv никак не могли справиться этой, казалось бы, элементарнейшей задачей. В итоге пришлось написать такой скрипт:

#!/usr/bin/perl # decoder.pl v 0.2 # (c) Alexandr Alexeev 2012 | http://eax.me/ use strict; use warnings; use utf8; use List::MoreUtils qw/uniq/; # http://www.artlebedev.ru/tools/decoder/advanced/ my @bad = split //, '<здесь был список испорченных символов, но MySQL его профукал>'; my @good = split //, 'абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'; my %map; $map{$_} = shift @good for (@bad); while(my $line = <>) { utf8::decode($line); $line = join '', ( map { defined $map{$_} ? $map {$_} : $_ } (split //, $line) ); utf8::encode($line); print $line; }

Как обычно, я буду несказанно рад вашим вопросам и дополнениям. Также мне было бы интересно узнать, какие пункты этого выпуска мини-заметок больше всего вам понравились и почему.

Дополнение: Мини заметки – выпуск 7