Home
Natural Oil For Tachikomas! [entries|archive|friends|userinfo]
Natural Oil For Tachikomas!

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Я становлюсь похожим на Raymond-а Chen-а [Ноя. 26, 2009|11:04 pm]
Raymond Chen отличается от других блогеров тем, что свои посты он пишет на несколько месяцев вперед. То есть уже написанная заметка попадает в очередь, и спустя некоторое время появляется в блоге. Кстати, M$ снова проявлет свою сущность злобного монополиста — её работники имеют возможность читать заметки из очереди публикаций Raymond-а Chena-а задолго до того, как они появляются на его сайте.

У меня накопилось уже 4 неопубликованные заметки на разные темы ([1] пример creative API abuse на Linux и FreeBSD, [2] предложение альтернативной технологии на замену HTML, [3] пост про опыт создания различных DSL и [4] string template language под Python, заточенный под задачи кодогенерации), никак не найду время довести их до ума.
ссылка3 комментария|Оставить комментарий

SVG 2 PNG, OMG! [Ноя. 7, 2009|01:02 am]

Есть такая всеядная софтина под названием ImageMagick. Знаменита прежде всего благодаря тому, что позволяет выполнять все операции из командной строки. Вот пример: convert foo.svg bar.png — преобразовать файл из формата SVG в PNG.

Проблема в том, что в Убунте ImageMagick поломался начиная с версии 9.4, и больше не способен корректно рендерить SVG, о чем имеется багрепорт. К счастью, в коментариях к багрепорту предложен workaround, позволяющий выполнить преобразование средствами Gnome.

Используя этот workaround, скрипт для преобразования SVG в PNG может выглядеть например так:

#! /bin/sh
# usage: svg2png src dest
#        svg2png src      - writes output to stdout
#        svg2png          - stdin->stdout conversion

input="$1"
output="$2"

if [ -z "${input}" ]; then input=/proc/self/fd/0; fi
if [ -z "${output}" ]; then output=/proc/self/fd/1; fi

gst-launch filesrc location="${input}" \
    ! gdkpixbufdec ! pngenc ! filesink location="${output}" 2>/dev/null
 

А все началось с того, что мне захотелось показать график. IMHO наиболее качественный результат для просмотра с экрана Gnuplot дает именно при рендере в SVG.

Xотелось бы выкладывать графику в формате SVG в сеть в исходном виде, учитывая что все нормальные браузеры обзавелись поддержкой SVG. Увы, воспринимать SVG, вставленный как <img> Firefox отказался, а элемент <svg> ЖЖ безжалостно вырезает.

ссылкаОставить комментарий

Condition variable, Windows prior to Vista: DIY [Ноя. 2, 2009|01:10 pm]

Все мы знаем, что условные переменные — это хорошо. Microsoft тоже это знает. На винде CV появились начиная с Висты. Для более ранних ОС необходимо изобретать что-то свое. Есть достаточно известная статья «Strategies for Implementation POSIX Condition Variables on Win32».

В статье предлагаются различные решения: через PulseEvent (который как известно is broken by design), через Manual Reset Events, несколько вариантов (ожидающие будятся с помощью event-a, при broadcast-е последний разбуженный тред сбрасывает event, последовательно наворачивается разные трюки, чтобы скомпенсировать неатомарность побудки) и через SignalObjectAndWait (вобще мрак, так как требует по всей программе использовать Mutex вместо Critical Section).

У меня появилась идея, как можно реализовать CV намного проще. В моем методе, CV состоит из Critical Section и списка ожидающих тредов. Треды засыпают с помощью SleepEx и пробуждаются через QueueUserAPC.

Подробности... )

Буду очень рад, если вы найдете у меня ошибки :)

ссылка1 комментарий|Оставить комментарий

Valgrind, отладка многопоточных программ [Окт. 16, 2009|11:45 pm]

Сегодня я вернулся из больницы. После прочтения накопившихся истории на баше, мне захотелось написать пост про valgrind. А точнее, про инструменты helgrind и drd в его составе.

Если коротко, valgrind распространяется под лицензией GPL, работает на Linux и MacOS, позволяет отлаживать различные нетривиальные баги. Наиболее известный инструмент в составе valgrind — это memcheck, тулза для диагностики ошибок работы с динамической памятью (утечки, переполнение буферов, обращение к освобожденному куску памяти и тп.). Также в пакете есть профайлеры (разные), и инструменты для поиска ошибок, связанных с многопоточностью.

Что любопытно, фактически valgrind — это платформа для разработки инструментов анализа программ во время выполнения. В дополнение к существующим инструментам, можно разработать что-то свое, если вдруг возникает такая потребность.

Рассмотрим helgrind и drd подробней... )

Совсем коротко, инструменты из состава valgrind впечатляют. Однако если для поиска утечек памяти и переполнения буферов с помощью memcheck никаких изменений в сходных текстах не требуется, то для диагностики ошибок связанных с многопоточностью требуются существенные изменения (в форме аннотаций). Потребность в аннотациях продиктована тем, что некоторые соглашения, которым следует программа, не отражены в коде (см. пример с condition variable выше).

Интересно, как в аналогичной ситуации выкручиваются инструменты от intel?

ссылка10 комментариев|Оставить комментарий

Всякая фигня [Окт. 12, 2009|01:17 pm]
Разборки с военкоматом отнимают не так много времени, однако я из-за этого нахожусь в достаточно вздрюченном состоянии, и до блога руки не доходят.

По работе всерьез консидерю написать собственный DNS-ресолвер. Можно было бы использовать стандартную функцию getaddrinfo(), но у нее есть небольшой недостаток — она не возвращает управление до тех пор, пока не сходит в сеть за ответом от DNS-сервера. А это плохо укладывается в общую концепцию «неблокирующие сокеты + epoll».

Также можно допиливать что-нибудь готовое.

Как оказалось, идея написать свой асинхронный DNS-ресолвер приходила не только мне. Например, так поступает nginx.
ссылка2 комментария|Оставить комментарий

Про бизона (анонс) [Сент. 7, 2009|01:14 am]
Изображение бизонаПо многочисленным просьбам, я начинаю небольшой цикл tips&tricks по бизону. Бизон — это генератор синтаксических анализаторов, на случай если картинка ввела кого-то в заблуждение.

Вынужден сразу предупредить, я буду упоминать только о фичах, которые мне довелось использовать. Самый большой пробел у меня — это GLR парсеры. G[eneralized]LR — это расширение LR парсеров, он допускает неоднозначные грамматики. В традиционных LR все неоднозначности должны быть разрешены на этапе компиляции. GLR разрешает неоднозначности в рантайме. Фактически, каждый раз когда входной поток лексем можно распарсить разными способами, парсер клонирует себя. Клоны функционируют параллельно, каждый из них исследует свой способ сопоставить поток лексем грамматике. В последствии клоны либо умирают, зайдя в тупик, либо вновь сливаются воедино. Это очень крутая технология, но к сожалению я не владею темой.

Вот примерный план серии:
  1. Отладка парсера
    Небольшая компиляция из документации. Использование трассы для автоматической визуализации дерева разбора. Трассировка с пользовательскими YYSTYPE/YYLTYPE. Немного улучшенные сообщения о синтаксических ошибках.
  2. Специальный токен EOF
    Почему бы нет?
  3. Специальный токен error
    Про концепцию error-rule. Иллюстрация особенностей и ограничений этого механизма на примерах. Похоже на exceptions из ЯП (а YYERROR совсем как throw), но только внешне.
  4. Процедурное редактирование потока лексем
    Некоторые особенности взаимодействия парсера и лексера. Как отредактировать поток лексем. Применение: эмуляция LALR(>1) в обычном бизоне, а также «расчистка» входного потока лексем при синтаксических ошибках. И еще зачем может понадобится «перемотка» потока лексем.
  5. Восстановление после синтаксических ошибок — с точки зрения пользователя
    Главный вопрос — насколько продвинутым должно быть восстановление. Сообщения о синтаксических ошибках — какие они должны быть. Подход гцц, с примерами. Подход парсеров на бизоне (по умолчанию), и почему нужен workaround.
  6. Примеры обработки синтаксических ошибок:
    • В выражениях
    • В statement-ах
    • В экзотике типа for(;;)
    • Может быть что-то еще...


Основные акценты серии — обработка синтаксических ошибок и хаки, направленные на обогащение декларативного императивным.
ссылка5 комментариев|Оставить комментарий

WTF! [Сент. 7, 2009|12:12 am]
Две недели назад, я писал о своем опыте столкновения с битым мультиком в формате MKV, и поделился мыслями о том, а не написать ли мне тулзу для починки.

Недавно у меня появилось немного времени, и я склепал черновой вариант. Вот о чем поведала мне программа:
+20.0M (14024d6 5.8%)           data corrupt: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+24.4M (185c6d4 7.1%)           recovered, discarding 4.4M (45a1fe 1.3%)
Любители логов, добро пожаловать! )


Фактически, ~30% файла в маленьких фрагментах затерто ноликами (проверено по hexdump). Это означает, что какой то ненатурал выложил в торрент недокаченный файл, никак по другому это объяснить нельзя.

Я еше не написал код, который «сшивает» ошметки вместе. Это не сложно. Однако, конечный результат предсказуемо хреновый, поэтому врядли я буду продолжать дальше.

Выводы.
Это банально, но всегда есть шанс, что из благого начинания получится фигня. Лучше, если это случиться рано, пока еще не потрачено много времени.

Еще, я удивился насколько быстро на современном компьютере работает самая тупая реализация поиска сигнатур. В смысле, найти в файле первое вхождение одной из множества «магических» цепочек байт, например 10 43 a7 70 или 1b 53 86 67 и тд. У меня на каждые 4Кб входного файла запускался тупой цикл по массиву сигнатур, в нем memchr по первому байту сигнатуры в пределах страницы и memcmp в позициях, где были совпадения. Я рад, что не стал делать что-то хитрое, а то было бы обидно :)
ссылка2 комментария|Оставить комментарий

Matroska, битые файлы [Авг. 23, 2009|02:40 am]
Сегодня я в очередной раз столкнулся с битым мультиком в формате MKV (это был "DI: Reactivation – REEMBODY"), и стало мне грустно. В интернете информации по починке битых MKV удручающе мало, предлагается использовать пакет mkvtoolsnix. Конкретно, разобрать контейнер на составляющие с помощью mkvextract, а затем собрать обратно тулзой mkvmerge. Якобы на этапе разборки все ошибки автоматически чинятся. ЩАЗ! В результате получается обрезанный файл, по месту первого повреждения.

Read more... )

P.S. Недавно еще ковырял формат DWARF (в нем представлена отладочная информация внутри ELF). Структурно устроен почти также, как MKV :)
ссылкаОставить комментарий

Мне не нравится современный интернет (монетизация) [Июл. 28, 2009|01:25 pm]
Мы потеряли инструкцию от стиральной машины, и я решил поискать её в инете. Мгновенно находится куча русскоязычных сайтов, которые предлагают купить у них искомую инструкцию! ИМХО, это невероятная наглость и надругательство над самой сутью сети, как источника доступной информации по чему угодно.

Меня адски злят потуги монетизации, которые происходят повсеместно в рунете. Почти также сильно, как предложения зарегистрироваться на сайте, чтобы иметь возможность прочитать ответ в их форуме на нагугленный вопрос или скачать файл (допустим игрушку, вышедшую в 1991 году). Это все становится особенно мерзким, когда повсеместно появляются предложения оплатить их услуги, отправив SMS.

Я знаю, как с этим бороться )
ссылка6 комментариев|Оставить комментарий

Ну что за фигня! [Июл. 25, 2009|02:48 pm]
Жалуюсь на жизнь. )
ссылка10 комментариев|Оставить комментарий

Кому нужны XML СУБДы? [Июн. 30, 2009|02:28 pm]
После универа я проработал два года в команде, которая создавала Native XML
СУБД
в порядке рисерча. На волне популярности, XML пролез в реляционки и породил так называемые XML-enabled РСУБД. В таких СУБД добавлен еще один тип колонки - «XML». Теперь в качестве атрибута у кортежа может быть XML-ный документ. Как-то так:

employee: id=123, name=<PersonName><GivenName>John</GivenName> <FamilyName>Smith</FamilyName></PersonName>

В этом даже есть определенный смысл — в разных культурах в имени человека выделяют различные компоненты. Часто в письменной речи они должны быть оформлены определенным образом, иначе невежливо. А проектировать реляционную схему для хранения имени для всех возможных культур — это overkill.

Read more... )

Необходимо понять, какие XML-ные возможности нужны в СУБД.

За все время разработки Седны мы нашли единственный хороший use case — автоматизация издательства. Документы в XML разметке сохранялись в БД, был реализован интерфейс для работы с документами, почти вся логика была реализована на XQuery. Все это реально работало в одном российском издательстве, которое издает энциклопедии. (Мы в смысле команда, лично я к этому не имею отношения.)

Кроме того, мы пытались найти интресные задачи для XML СУБД среди нашего community, но безуспешно. Помимо Седны, есть и другие Native XML СУБД, из наиболее популярных — eXist. Не знаю, проводилось ли исследование среди тамошнего community, но определенно это хорошая идея.

Наконец, если вы, уважаемый читатель моего дневника, можете поделиться своими мыслями, я буду просто счастлив!

Ps. )
ссылка12 комментариев|Оставить комментарий

(без темы) [Май. 26, 2009|12:06 am]
В последнем кулере были впечатляющие видео полета на разных девайсах.
http://www.youtube.com/watch?v=2TBndcBjQFM
http://www.youtube.com/watch?v=0snTqLQLpBA

Я подумал о том, что просто программировать для персоналок, в конце концов, не самая впечатляющая вещь в мире. Вот было бы круто работать в каком-нибудь исследовательском институте и заниматься работой на стыке областей... Зуб даю, что в таких девайсах есть бортовой компьютер с очень навороченной программой, с которого все и управляется. Запрограммировать, полетать, отдебажить по результатам :)

Генетика, исследования человеческого мозга, необычные применения технологии распознавания образов, разработки нового интерфейса для взаимодействия человека и компьютера (типа Microsoft Surface)... Интересных тем миллион.

Жаль, что в России работа в исследовательском институте означает низкую зарплату. Жаль, что у нас нету лабораторий, где занимаются чем-то подобным. Жаль, что я никогда не рассматривал научные исследования, как интересное занятие, и сейчас максимально далек от всего этого.

Меня всегда вдохновляли истории о том, как в одном месте собирается банда программистов, пишет что-то революционное, изменяет мир и зарабатывает кучу бабок (типа Masters of Doom или истории из Hackers Heroes of Computer Revolution Леви).

А сейчас вдруг подумалось - как же наверно охуительно круто, когда в одном месте собирается банда и строит самолет. И он на самом деле летит, в Реальном Мире!
ссылка4 комментария|Оставить комментарий

Давно ничего не писал... [Май. 7, 2009|04:00 pm]
За последние больше полу-года в моей жизни произошли разные и по-настоящему волнующие события.

Я женился на милой [info]rusdreamer (фотка).

Меня выперли с работы в институте, где я работал в команде, которая делала XML СУБД.

Последнее время я работаю в компании, которая занимается компьютерной безопасностью. Я делаю разные штуки для ядра FreeBSD и кое-что из юзерспэйсных утилит. Подумываю о смене работы, тк. (а) очень далеко ездить на работу, (б) народ тут необщительный и угрюмый, (в) обстановка просто провоцирует к распиздяйству, (г) нужны бабки.

Из совсем уж малозначимых мелочей - купил новый нотик (HP6910p), перешел на Linux как основную десктопную систему, а еще придумал и заимплементил на 30% собственный спецязык (aka DSL).
ссылка1 комментарий|Оставить комментарий

В 2030 году киборги говорят на языке Си [Ноя. 24, 2008|03:01 am]
Пересматривая GITS, обратил внимание, что за текст доктор набирает на терминале (когда происходит идентификация тела, в котором находится Puppet Master). Кстати девочки-операционистки походу тоже общаются на языке Си. Именно поэтому у них так много пальцев!




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

Unit-тесты и предсказание будущего [Окт. 14, 2008|12:58 pm]

Я написал свой собственный велосипедный фреймворк для unit-тестов. В отличие от CUnit/CppUnit он очень простой, всего из 7 функций.

void BeginTesting(int *argc, char **argv);

int EndTesting();

BOOL NextTest(const char *descriptFmt, ...);

BOOL BeginTestGroup(const char *descriptFmt, ...);

void EndTestGroup();

void FailTest(const char *messageFmt, ...);

void WriteTestTrace(const char *fmt, ...);

Как мне кажется, CUnit/CppUnit избыточно сложные, потому что они позволяют выбирать, какие прогонять тесты. Из-за этого приходится поддерживать тн. test registry, и регистрировать там тесты. Каждый тест должен быть оформлен как отдельная функция, указатель на которую хранится в TR.

Мне более симпатична другая концепция — все тесты прогоняются целиком. Тестовая программа содержит вызовы NextTest() и BeginTestGroup() / EndTestGroup() чтобы обозначить границы тестов. Вызов FailTest() отмечает текущий тест или группу тестов как заваленную.

Вызов WriteTestTrace() пишет отладочную информацию по ходу выполнения текущего теста; это должно помочь локализовать ошибку, если тест будет завален. Наконец BeginTesting() инициализирует тестовый фреймворк. Функция извлекает параметры настройки тестовой системы из командной строки программы. Распознанные опции удаляются из командной строки, и программа получает обновленные argc и argv. Вызов EndTesting() освобождает ресурсы и формирует exit code для программы. Exit code зависит от того, все ли тесты прошли успешно.

Пример:

int main(int argc, char **argv)
{
    FOO foo;

    BeginTesting(&argc, argv);
    NextTest("testing InitFoo");
    if (!InitFoo(&foo) || !ValidateFoo(&foo)) FailTest("");
    return EndTesting();
}

Ничего лишнего. Никаких test registry. Программа написана в простом процедурном стиле.

Фунции NextTest() и BeginTestGroup() возвращают булевое значение. Если это ИСТИНА, значит тест надо выполнять. В противном случае, тест можно не выполнять. Например, пользователь не хочет прогонять этот конкретный тест, или превышен лимит на количество заваленных тестов.

Тестовая программа может проверять возвращаемое значение и пропускать такие тесты, тогда тестирование будет проходить быстрее. Если программа игнорирует возвращаемое значение, ничего страшного. Когда стартует тест, который надо пропустить, тестовый фреймворк переключается в особый режим. В этом режиме вызовы FailTest() и WriteTestTrace() игнорируются.

В зависимости от значения опции --verbosity в командной строке, тестовый фреймворк выводит:

  • только метки проваленных тестов (тестам автоматически назначаются метки — T1, T2, T2.1 и тд.);
  • метка и описание причин, почему тест не прошел (это описание передавалось в функцию FailTest());
  • метка, описание причин неудачи, описание теста (описание теста передавалось в функцию NextTest() или BeginTestGroup());
  • метка, описание и трасса проваленных тестов.

Также есть возможность брякаться перед началом тестов, которые будут завалены (предсказание будущего ;). Этот режим включается опцией --debug-break.

Read more... )
ссылка9 комментариев|Оставить комментарий

Pygdb [Сент. 24, 2008|02:34 am]
GDB when the program's wrong and the debug's on
It's GDB when the system dies and you don't know why
It's hard to bear
With no one to help you
You're going nowhere
GDB when you press control and the box just rolls
It's GDB when the system dies and you don't know why
It's hard to bear
With no one beside you
You're going nowhere


Gdb это очень мощный отладчик, используемый в основном в Linux/Unix. Gdb — консольный отладчик, т.е. он принимает команды из терминала и выводит ответ туда же. Если перенаправить ввод-вывод, то можно генерировать команды для gdb скриптом, и автоматически анализировать ответ gdb. Это открывает много интересных возможностей!

Вывод gdb достаточно просто парсить (опции --annotate, --interpreter). В процессе написания собственного велосипеда, я наткнулся на очень удобную библиотеку, pygdb. Библиотека предоставляет класс Gdb. Каждой команде gdb соответствует метод этого класса; почти все они реализованы.

Зачем может потребоваться скриптовать gdb?
Read more... )
ссылкаОставить комментарий

Win API, security (2) [Сент. 10, 2008|08:36 pm]
В предыдущей части, я немного написал про то, как работает security в винде, с точки зрения API. Это про то, зачем нужен загадочный параметр lpSecurityAttributes в функции CreateFile и в других create-функциях.

Этот параметр позволяет установить Security Descriptor для создаваемого объекта. Security Descriptor определяет, каким пользователям разрешен доступ к объекту. Есть возможность разрешить только часть действий с объектом. Можно например дать разрешение на чтение файла Foo пользователю Васе, и не давать ему разрешение на запись.

В Security Descriptor пользователи идентифицируются с помощью SID. Security ID - это текстовая строка такого вида: S-1-5-32-500. Приведенный SID уникально идентифицирует пользователя BUILTIN\Admin. Для идентификации групп также используется SID (S-1-5-32-200 соответствет группе BUILTIN\Admins). При этом коллизий между пользователями и группами по SID нет.

На самом деле, security оперирует не пользователями, а SID-ами. Разрешения могут быть установлены как для пользователей, так и для групп, все в рамках единого механизма. ИМХО красиво.

Если значение параметра lpSecurityAttributes — NULL (как это обычно бывает), система устанавливает настройки безопасности по-умолчанию. Полный доступ к объекту разрешен активному пользователю, а также пользователю Local System. Всем остальным доступ запрещен.

В большинстве случаев, настройки по-умолчанию отлично подходят. Мы создаем что-то для себя, с чем не должны работать другие пользователи. «Security: Просто передай NULL!»

К несчастью, даже в сценарии «что-то для себя, с чем не должны работать другие пользователи», есть пара тонких моментов. Если их не учитывать, возможны мистические баги из-за security.

What's wrong with this code, part 6
What's wrong with this code, part 6 - the answers

После этих двух постов в блоге Larry Osterman, меня заинтересовала тема security на винде. Честно говоря я был в шоке - мне как-то ни разу не потребовалось Security API, соответственно представление о нем у меня было самое смутное. Как оказалось, очень неплохо представлять, как security взаимодействует с другими подсистемами. Чтобы ловить те самые мистические баги.

У Larry приведен невинный код, который реализует тривиальный лог. Открываем файл, форматируем строку с помощью printf, записываем в конец файла, закрываем файл. Однако в этом коде есть серьезная проблема. В определенных обстоятельствах, в лог попадает только самая первая запись, а все остальные записать не удается.

Попробую объяснить этот баг своими словами... )

В следующей части я собираюсь написать, на каких структурах данных реализован DACL (discretionary access control list) — список записей ACE/access control entry, каждая из которых разрешает или запрещает определенный набор действий с объектом, для некоторого SID.

PS. Насколько серьезным багом является описанная проблема с лог файлом?

При определенных настройках security, некоторые из методов работы с файлами перестают работать. Однако тех же результатов можно добиться, применяя другие, несколько более сложные методы. Вопрос — программа, которая не использует продвинутые методы, и не может работать в такой ситуации, она содержит серьезный баг? Или это просто еще одна фича, которая не поддерживается?
ссылкаОставить комментарий

Win API, security (overview) [Авг. 25, 2008|07:28 pm]
Read more... )
ссылкаОставить комментарий

Как отучить Python генерировать PYC файлы [Авг. 13, 2008|10:55 pm]

Python «компилирует» исходные файлы. Допустим в дирректории лежит модуль SPAM.PY. При первой загрузке этого модуля Python создает файл SPAM.PYC в той же дирректории. Сделано это для того, чтобы скрипты использующие кучу модулей быстрее запускались.

В ряде случаев такое поведение петона бесит. Например — когда в репозитории лежат петоновские скрипты, которые используются для сборки билда. Когда билд собирается в отдельной папке (out of source build), ни с того ни с сего появляющиеся в дереве исходников PYC файлы выглядят несколько странно.

Python 2.6 будет штатно поддерживать отключение генерации PYC файлов. Для более ранних версий нужно заменить функцию импорта модуля (__builtins__.__import__). Проще всего это сделать так:

import ihooks
mi = ihooks.ModuleImporter()
mi.set_loader(ihooks.FancyModuleLoader())
ihooks.install(mi)
ссылка6 комментариев|Оставить комментарий

Stress Test для кофеварки [Июл. 24, 2008|11:56 pm]
Если вы не в курсе, мы разрабатываем клевую XML СУБД)))
Кстати, у нас завелась новая кофеварка.


Мы провели небольшое исследование. Из одной порции молотого кофе заваривались 5 чашек кофе. Потом мы взяли 5 сэмплов (стопарики), а вот результат:


еще фотки )
ссылка23 комментария|Оставить комментарий

navigation
[ viewing | most recent entries ]
[ go | earlier ]

Реклама