Code Golf

May 26th, 2010 No comments

Мда.

Решили мы с другом поучаствовать в Code Golf на askdev.ru, с их задачкой по игре в города.
Закончили буквально за три минуты до завершения конкурса, и когда было пора отправлять решение, сайт лег :))

Решение вот такое:


#!/usr/bin/perl -CDAO
use utf8;
use List::Util qw/shuffle/;

my@a=qw{Калининград Вологда Далматово Дмитров Архангельск Владивосток Краков};

{@a=shuffle@a;(grep{$a[$_-1]=~/(.)(.)$/;$a[$_]!~/^$2/i&&$a[$_]!~/^$1/i|grep{/^$2/i}@a}1..$#a)&&redo}

print join ',', @a

По правилам этого конкурса подключение библиотек и вывод данных не учитывются, поэтому размер кода – 100 байт.

Восхитительные грабли.

April 28th, 2010 13 comments

Я недавно открыл для себя восхитительные грабли. Оказывается, что я ходил по ним годами, а наступил только вчера.

Допустим, нам нужно скачать какой-нибудь файлик из сети, посчитать его размер и сохранить в файл.

Код на perl будет примерно таким:


#!/usr/bin/perl -w
use strict;

if (system('wget -O- http://sveshnikov.ru/httpd.sh | wc -с > /tmp/size') != 0) {
die "OMG! Can't count bytes in httpd.sh file!!!";
}

В дальнейшем этот скрипт используется примерно так: if ./get_value; then работаем с /tmp/size; else сообщаем куда нужно; fi

Так вот, у этого скрипта хромает надежность. Почему? И как это сделать правильно? Жду ваших вариантов, мой – завтра :)

UPD:

Проблема заключается в том, что при использовании пайпов в $? окажется результат последней выполненной команды:

$ blablabla | wc; echo $?
zsh: command not found: blablabla
0 0 0
0

Я решил этот вопрос с помощью bash и его опции pipefail. Пример выше я бы переписал как-то так:


#!/usr/bin/perl -w
use strict;

sub my_system {
return system('bash', '-o', 'pipefail', '-c', @_);
}

unless (my_system('wget -O- http://sveshnikov.ru/httpd.sh | wc > /tmp/size') == 0) {
die "OMG! Can't count bytes in httpd.sh file!!!";
}

Теперь будут отлавливаться все ошибки.
В шелл-скриптах можно еще анализировать массив PIPESTATUS, в котором сохраняется exit code для всех команд ковейера, но мне показалось, что это неудобно.

Debian-пакеты с человеческим лицом на примере Zabbix 1.8

March 29th, 2010 No comments

Несколько задержавшийся кросс-пост с хабрахабра, в котором я описываю пример сборки debian-пакета с использованием CDBS.

Написать эту статью меня заставили две вещи: во-первых, вышел zabbix 1.8 – замечательной системы мониторинга, в которой, судя по новостям, наконец-то занялись проблемами юзабилити админского интерфейса. Во-вторых, появилось стойкое ощущение, что после статей “делаем debian-пакет на коленке”, большинство хабравчан утвердятся во мнении, что debian-пакеты придумали извращенцы для извращенцев.

Связывает два этих события то, что zabbix 1.8 пока нет в репозиториях убунты, и когда появится не понятно, а компилить и ставить из исходников что-то на продакшн-серверах, это, конечно, недостойное джентльмена занятие. В общем, появился хороший повод продемонстрировать, что debian-пакеты – это не страшно.

Итак, приступим:

apt-get install dh-make devscripts cdbs libmysqlclient-dev libcurl4-gnutls-dev
wget http://sunet.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/1.8/
tar zxvf zabbix-1.8.1.tar.gz
cd zabbix-1.8.1
dh_make --createorig --cdbs

Между делом, если бы мы пакетировали не zabbix, а более простую программу, которая обычно ставится с помощью ./configure; make; make install, то на этом создание пакета заканчивается. Осталось бы только собрать пакет с помощью команды ar debuild.

Но мы продолжим: откроем файл debian/control и пропишем себя, любимого, в качестве создателя пакета, и укажем, какие пакеты должны приехать на сервер вместе с установкой zabbix. Для этого нужно найти строку ‘Depends’ и добавить в ее конец следующее: “fping, adduser, apache2, php5, php5-mysql, php5-gd”.

Обычно скрипт configure можно вызывать без параметров и он сгенерирует жизнеспособный конфиг, но для zabbix это не так – нужно отдельными опциями включать компиляцию серверной части и агента. Так как агент из репозитория ubuntu меня полностью устраивает, ограничимся сборкой только сервера. Для этого нужно дописать в конец файла debian/rules следующую строку:

DEB_CONFIGURE_USER_FLAGS := --enable-server --with-mysql --with-libcurl

Сборка пакета осуществляется следующим образом: создается временная директория (debian/zabbix), в которую копируется содержимое пакета. Затем эта директория упаковывается в архив, добавляются метаданные, и все – пакет готов. Это очень грубое, но пока достаточное описание процесса. Бинарные файлы попадут в директорию debian/zabbix/usr/bin сами собой (CDBS запустит команду make install DESTDIR=debian/zabbix), а файлы php-интерфейса нам нужно туда поместить вручную. Для этого перечислим их в файле debian/install вот каким образом:

frontends/php/* usr/share/zabbix/
misc/conf/zabbix_server.conf etc/zabbix

Кроме того, для нормальной работы заббиксу нужны еще и директории для лог-файлов и локов. Они тоже являются частью пакета, поэтому создать их нужно прямо в той временной директории. Стандартный способ создания пустых директорий – перечисление их в файле debian/dirs:

/var/log/zabbix-server
/var/run/zabbix-server

Теперь дело за инит-скриптом. В комплекте с заббиксом идет инит-сктипт (misc/init.d/debian/zabbix-server), но без обработки напильником он работать не будет. Поэтому лучше его заменить скриптом из убунты (http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/karmic/zabbix/karmic/annotate/head%3A/debian/zabbix-server-mysql.zabbix-server.init), который нужно сохранить под именем debian/init.

Совсем не лишним было бы настроить logrotate:

cat > debian/logrotate
/var/log/zabbix-server/zabbix_server.log {
daily
rotate 7
compress
missingok
notifempty
create 0640 zabbix zabbix
sharedscripts
}
^D

Раз пакет мы собираем не для кого угодно, а только для себя, то почему бы и не положить конфиг апача рядом с заббикксом:

mkdir misc/apache2-vhosts
cat > misc/apache2-vhosts/zabbix

ServerName zabbix.example.com
ServerAdmin admin@example.com
DocumentRoot /usr/share/zabbix
CustomLog /var/log/apache2/zabbix_access.log combined
ErrorLog /var/log/apache2/zabbix_error_log

^D
echo "misc/apache2-vhosts/zabbix etc/apache2/sites-available" >> debian/install

Иногда недостаточно просто разложить файлики по файловой системе, чтобы настроить среду для исполнения программы. В таких случаях на помощь приходят preinst, postinst, prerm и postrm скрипты, которые выполняются уже на сервере, куда пакет устанавливается. Названия у них вполне понятные, пояcнить нужно только то, что ‘inst’, и ‘rm’ – это фактическая распаковка или удаление содежимого пакета (все той же временной директории debian/zabbix, в нашем случае), кроме того – каждый из этих скриптов может вызываться с разными аргументами в зависимости от ситуации. Нам интересен скрипт postinst, вызывающийся с аргументом configure – нужное место скрипта можно легко найти в шаблоне postinst.ex. Приступим:


cd debian
mv postinst.ex postinst
vim postinst

После строки configure) нужно вставить следующий код:

# Создадим пользователя
useradd zabbix || echo "User zabbix was not added"

# Установим права на рабочие директории
chown zabbix:zabbix /var/log/zabbix-server /var/run/zabbix-server

# В этой директории веб-интерфейс пытается сохранить конфиг-файл, дадим ему на это права:
chown www-data /usr/share/zabbix/conf
chmod 775 /usr/share/zabbix/conf

# Настроим автозапуск сервера:
update-rc.d zabbix-server defaults

# Включим новый virtualhost и перезапустим апач:
a2ensite zabbix
invoke-rc.d apache2 reload

;;

abort-upgrade|abort-remove|abort-deconfigure)

Осталась одна деталь: при стандартных настройках PHP интерфейс заббикса не заведется, нужно править max_execution_time и еще несколько параметров. Если бы мы готовили пакет для домашнего сервера-торрентокачалки, то, конечно, проще было бы поправить php.ini напрямую. Но идеологически более правильно (и это очевидно при коллективной разработке, когда роли админа и разработчика разделены) поместить эти настройки тоже в пакет. Сделать это можно так:


mkdir misc/php.conf
cat > misc/php.conf/zabbix.ini
post_max_size = 16M
max_execution_time = 300
mbstring.func_overload = 2
^D
echo "misc/php.conf/zabbix.ini etc/php5/conf.d" >> debian/install

На этом все, можно собрать пакет с помощью команды debuild, поставить его с помощью dpkg -i и apt-get install -f.

Categories: UNIX Tags: , , , ,

Магия в zsh

November 4th, 2008 1 comment

Захотелось мне в этот чудесный выходной понедельник поагитировать за свой любимый шелл. Чем, кстати, вообще может похвастаться какой-либо шелл, имея в конкурентах такого монстра, как bash? Ведь в bash есть все, что может только пожелать обычный пользователь. А, собственно, и нечем. Поэтому разработчикам zsh пришлось пойти на хитрость и добавить в свое детище некоторое количество магии :)

Большая ее порция досталась подсистеме автодополнения. Вот таким можно сделать дополнение имен файлов:
zsh menu completion
(выбрать нужный вариант можно табом по старинке, курсорными клавишами или стандартными emacs-хоткеями)

А вот так – дополнение опций:

Еще одна моя любимая магическая штуковина в автодополнении выглядит так: допустим, нужно подправить файл /usr/local/etc/rc.d/mysql. Пользователь bash бы ввел такую команду:
vim /usr/l[TAB]/e[TAB]/r[TAB]/m[TAB]
В zsh все несколько проще, а именно:
vim /u/l/e/r/m[TAB]
После нажатия [TAB] все элементы пути будут дополнены. Если вариантов несколько, можно будет вручную выбрать нужный.

Ну вот, я вам передал примерно 0.65 процента магии zsh. Помимо автодополнения, ее залежи находятся в подсистеме подстановок (man zshexpn), но там я сам не очень уверенно ориентируюсь. Все-таки одна из прелестей zsh заключается в том, что мне необязательно учиться им пользоваться – для начала можно ограничиться только bash-совместимым подмножеством функций, регулярно натыкаясь на приятные мелочи в реализации привычных вещей.

Дальнейшее чтение:
обзор zsh от Алексея Федорчука,
Собрание хаков и идиом zsh: zsh lovers.

Categories: SHELL, UNIX, tips Tags: , , ,

Чудеса!!

June 6th, 2008 3 comments

Я страсть как люблю хакерские программки, которые удивляют самим фактом своего существования.

Одна из таких: тетрис на sed’е. Это что-то невероятное!

p.s. да, и я тоже, оказывается, шаблонно мыслю :)

defun.ru – ресурс о функциональном программировании

April 11th, 2008 No comments

Не могу не пропиарить: www.defun.ru

Дефун это что-то типа Digg’а для людей интересующихся функциональным, декларативным и прочим “нестандартным” программированием.

(c) Semka

Основная тема — новости и ссылки о функциональном программировании, разных альтернативных и перспективных языках, ну и про computer science вообще. Ключевые слова — хаскель, erlang, lisp, smalltalk, ocaml. Ruby, groovy и питон туда тоже отлично впишутся.

Тру Программист

Там уже сейчас чтения на все выходные :)

Categories: общее Tags:

Пятничная загогулина (bash)

April 11th, 2008 15 comments

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

#!/usr/bin/bash
echo "one two three" | read a b c
echo "<$a> <$b> <$c>"

Что в нем не так?

Я ожидаю увидеть два ответа. Первый — это просто переписать этот код так, чтобы он заработал. И второй ответ — опционально — объяснение, почему не работает в таком виде.

p.s. второго ответа я пока сам не знаю, поэтому буду с интересом ждать его от вас :)
p.p.s. ответивший верно, как всегда – умничка (-ца (маловероятно)).

UPD: На второй вопрос ответ уже есть:
gds
есть мнение, что переменные, прочитанные read, остаются в переменных “экземпляра” баша, запущенного для выполнения “read a b c”, и не попадают в родительский баш, запустивший “echo … | read a b c”.

gds’у – респект!
А я понял, что в следующий раз надо постить задачки посложнее..

UPD2 свое решение выложу сегодня в конце рабочего дня
UPD3

Кажется, я нечаянно сорвал рабочий день моего начальника, но зато он мне прислал вот такое остроумное решение:
str=`echo "one two three" | (read a b c; echo "a=$a; b=$b; c=$c;") `
eval "$str";
echo "<$a> <$b> <$c>"

Петя, ты отжог :)

Но на самом деле все это можно сделать несколько проще – через “here doc”:
read a b c <<<`echo "one two three"`
echo "<$a> <$b> <$c>"

Заговор от битых кластеров (добавляем информацию для восстановления архивов с помощью par2)

April 10th, 2008 14 comments

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

Итак, к делу: регулярно, при записывании данных на диск, остается некоторое количество свободного места. Лет 5 назад можно было положить диск на полочку, пометив, что еще 30 мегабайт можно забить каким-нибудь хламом, но сейчас стоимость болванки – 10 рублей, такой аргумент не действует. И, соответственно, привычку оставлять место “на потом” надо искоренять. Мне кажется, наиболее разумным было бы добавить на диск данные для восстановления – т.н. корректирующие коды Рида-Соломона, которые могут пригодится в случае, если диск будет поврежден.

Собственно весь заговор выглядит следующим образом:
#sudo aptitude install par2
#man par2
#cd backups
#ls
dump.sql.gz
#par2 create -v -r10 -n1 -m500 dump.sql.gz
#ls
dump.sql.gz dump.sql.gz.par2 dump.sql.gz.vol000+100.par2

Эта команда добавит 10% избыточной информации к данным, запишет все это в один файл, при этом программе par2 разрешено использовать 500 мегабайт оперативной памяти. Файлы *.par2 следует записать на диск вместе с дампом.

Мне эта программа понравилась, я захотел ее проверить в боевых условиях. Сделал архив размером около 400 Мб, добавил данные для восстановления – еще 200Мб – *.par2 файлы (50% избыточность, по умолчанию – 5%). Все это я записал на CD-RW, в котором потом сделал, простите, дырку. В итоге стандартными средствами удавалось прочитать только первые 150 Кб данных. Чтобы восстановить файл нужно сначала все считать пускай с ошибками – для этого есть программа dd_rescue, которая является практчески полным аналогом dd с одним исключением – она умеет игнорировать ошибки чтения:

диск с дыркой

#sudo dd_rescue -Av -b 1048576 -B 1048576 /dev/scd0 brokencd.iso(-A – заполняь нулями те участки файла, которые считать не удалось, v – verbose, -b – размер блока данных, -B – размер блока данных для проблемных областей диска)
# sudo mount -o loop brokencd.iso mnt # монтируем получившийся образ диска
# cp mnt/* dump; cd dump # копируем содержимое
# par2 r archive.par2 # приводим в изначальный вид

На этом эксперимент завершился – все данные с диска восстановлены. Даже скучно.
Но пример, мне кажется, весьма красноречив :)

Categories: UNIX, tips Tags: , , , ,

Сдал на MCSA!

April 1st, 2008 10 comments

Это просто праздник какой-то, со второго раза удалось сдать оба экзамена на W2K3 MCSA – 70-290 и 70-291!
Если бы такие экзамены были у меня в университете, меня бы отчислили на первой же сессии, это единственное, что могу сказать. И еще, даже не ожидал, что они могут быть настолько сложны.

Но зато теперь я – Microsoft Certified System Administrator, урра!

Завтра мои данные появятся на https://mcp.microsoft.com/authenticate/validatemcp.aspx, id 7900401.

На очереди 70-297, даже не знаю, как я буду его сдавать.. :(
Но это потом, а пока – принимаю поздравления!

upd: смотрим на дату и больше не задаем глупых вопросов :)

Categories: общее Tags:

SSH для бэкапов и мониторинга: ограничиваем доступ.

March 4th, 2008 1 comment
“Бэкап – акт проявления трусости” (c) народная мудрость

Я труслив :) Во-первых, я делаю бэкапы. Во-вторых, я их боюсь передать без шифрования и, в третьих, иногда и храню их зашифрованными.

Как правильно организовать передачу бэкапов с одного сервера на другой? Дампы этого блога у меня передаются по http. Они зашифрованны, да и особо ценных данных здесь нет, поэтому http меня не смущает. А как поступить с данными по-важнее? Конечно, ssh. А как при этом запретить пользователю выполнять какие-либо действия, кроме как копирования к себе бэкапа? Представим себе, что бэкап-сервер находится во вражеском дата-центре или просто вы не можете проконтролировать к нему доступ (в т.ч. физический). Нас выручит опция command файла authorized_keys.

Например, если необходимо предоставить доступ только к файлу backup.tgz, то в authorized_keys в самом начале соответствующей строки можно дописать следующее “command=’cat backup.tgz’”. Теперь при каждом коннекте будет автоматически выполняться команда cat backup.tgz, вам остается только перенаправить вывод в файл. Если дампов несколько, то можно написать небольшой скриптик вида:#!/bin/sh
read file
case "$file" in
"foo") cat foo.tgz ;;
"bar") cat bar.tgz ;;
esac
И прописать его в качестве команды по умолчанию. Да, не лишним было бы добавить опции no-port-forwarding, no-pty и все прочие no-*

Теперь копировать файл с удаленной машины можно вот такой командой:echo "foo" | ssh backup@server > foo-`date +%Y-%m-%d`.tgz

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

По-моему, все, что я описал примитивно по сложности и вместе с тем весьма надежно. Так может быть прекратим без надобности дописывать authorized_keys на серверах? Одна запись для себя, любимого, одна для бэкапов, одна для мониторинга.. ой, еще каких-то две.. дальше продолжать? :)

Кстати, а есть ли способы более тонко настраивать уровень доступа к ssh?

Categories: SHELL, UNIX, tips Tags: , , ,