Чтобы добавлять сообщения и комментарии, .

@Spumer:
Spumer

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

Во-вторых, исследуя траффик общения сервера и клиента игры Left 4 Dead 2, удалось выявить особый и, разумеется, недокументированный тип запроса.
Он предназначен для отображения списка серверов в главном меню и пакеты такого запроса содержат номер версии игры и текст InetSearchServerDetails. Стоит сказать, что для этого типа пакетов сервер уже реализует анти-ддос защиту: проверяет версию и следит за числом pps.
Но ситуация усугубляется тем, что для данного типа запросов нет никакого мастер-сервера. Точнее он есть, и возвращает лишь список серверов которые привязаны к Steam-группам в которых ты состоишь. И при получении такого списка клиент сразу же шлет INET_SEARCH-пакеты всем серверам.

Тем не менее, даже с кэшированием сервер тратит драгоценное время на проверку запроса и отправку ответа. Забегая вперед скажу, что полностью сняв с сервера эту обязанность мне удалось снизить задержки в игре (пинг) в 2, а то и 3 раза.

Решение достаточно простое: сервер не должен отвечать на все эти запросы. Вообще. Эту задачу можно попробовать переложить на плечи какой-нибудь службы, которая бы отвечала на весь этот флуд, изредка запрашивала актуальные данные с основного сервера, а ОСь бы грамотно распределяла ресурсы.

Но как отделить пакеты запросов инфы, от обычного обмена данными с сервером? Первой попыткой было использовать statefull firewall. Это обычный знакомый нам фаерволл, который следит за подключениями, может их маркеровать и тд. Не получилось. Вот так просто. Даже поймав пакет и отправив его куда надо (сторонней службе), фаерволл пометит соединение и все что придет с пары ip:port на пару ip:port будет отправлено туда же.
PS: Многие просто не смогут подключиться, так как клиент как правило использует тот же сокет для обмена данными, что и для запроса информации.

stateless firewall я даже не ковырял, и как я потом понял - он мне бы и не подошел.

Остался самый очевидный и как следствие - простой - способ отделить один пакет и направить куда надо. Это модуль ядра. И это сработало!
Я зарегистрировал обработчик пакетов самым первым (там можно выставлять приоритет), еще пока он обрабатывается в пределах RAW таблицы и никто его в глаза не видел. Нагло подменяю порт отправки, пересчитываю контрольную сумму и пропускаю его дальше! А дальше он проходит через воду, огонь и медные трубы и попадает прямо к службе, которая слушает нужный порт. Ответы от службы обратно транслируются и порт подменяется при отправлении.
Для простоты и удобства порты имеют жесткую связку:
27015 -> 27915
27016 -> 27916
...

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

Перспектив для развития достаточно много, можно отвечать не всем, проще управлять лимитами, детектировать атаки и собирать разного рода статистику. В общем, все что душе угодно. И главное - всё это не будет отнимать процессорного времени у сервера (игрового).

@Spumer:
Spumer

Хотелось бы сказать "по долгу службы", ан нет. Не в столь отдаленном прошлом необходимо было решить проблему с "DDoS". Это была не спланированная атака, никто не хотел причинить вред или что-то подобное. Нет. Просто слишком много людей хотело спросить сервер о его состоянии, но обо всем по порядку.

Речь идет о source-based серверах (игровых, на движке Source). При запуске такой сервер отправляет информацию о себе мастер-серверу, и уже с мастер-серевером происходит всё общение клиентов: получение списка серверов и базовая информация. Подробная инфофрмация будет запрошена напрямую. Такое общение происходит по спец. протоколу, который описан тут https://developer.valvesoftware.com/wiki/Server_queries и тут https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol

Атаковать сервер не так уж сложно, достаточно начать флудить запросами, а если еще провайдер позволяет ip-spoofing и на другой стороне нет даже rp-фильтра, то дело за малым. Проблему люди быстро научились решать кэшированием: https://forums.alliedmods.net/showthread.php?t=135543 Кэшируют конечно не всё, но ситуация стала определенно лучше. Проблема же кроется в другом, а для Left 4 Dead 2 их целых две.

@Spumer:
Spumer

Сегодня случилась забавная история с процессами на одном из подопечных серверов. Есть приложение, порождающее 6 процессов, которые последовательно занимают порты от 37500 до 37505. Каждый из процессов создает рядом .pid-файл и все мы знаем что там лежит. Затем по трагической случайности сервер был отключен и перезапущен, но тк система автономна, то приложение вновь начало запускать процессы, но на этот раз их честно запустилось лишь 5.
Оказалось, что после перезапуска первый из шести процессов получили тот же самый pid что и последний процесс до перезапуска. В итоге, когда дело дошло до запуска последнего, шестого, процесса, все проверки на наличие такового были пройдены и он успешно не запустился.
Впредь буду подчищать мусор при старте системы ;)

@Spumer:
Spumer

Зачем так жить? Google Translation Bot смеется надомной.

spumer: Союз не найден
Bot_ru2en: Union has been found

Обратно он, разумеется, жрет нормально.

spumer: Union has been found
Bot_en2ru: Союз был найден

@Spumer:
Spumer

Доступ к серверам осуществляю по ключу. При логине на один из которых система упорно ругалась на bad ownership or modes для файла authorized_keys. Во всех манах в инете пишут про права 600 и тому подобное.
Товарищи, не верьте им! Только православные 440 или даже 400 спасут вашу жизнь!
Как спасли и мою.
Ошибка Permission denied (publickey) больше не преследует меня.
Админьте правильно, ребяты.

@Spumer:
Spumer

Дружишка работает фотографом в Питере, недавно было мероприятие, посетил и сделал небольшой ролик: youtube.com
Мне понравился.

@Spumer:
Spumer

Сделал -очередной- сниппет для Sublime Text 2 (удобный кросплатформенный редактор) с кратким текстом GPL 3 на основе данных с gnu.org
Забрать можно тут: gist.github.com

@Spumer:
Spumer

Left 4 Dead 2 перешел на SteamPipe и технология изготовления патчей тоже изменилась.
Хэши для версий игры я собираю с помощью инструментов из ACFToolkit, а вот способ создания патчей из этого набора мне не понравился.
Каждая строка в файле версии имеет вид:
551 1140245206795384743 96ac598192327ac271eb499b43f3fda37bf58be8 11 bin\icudt42.dll

Нас интересует только хэш и имя файла. Обрезается это очень просто, в моем случае notepad++ делает это по regexp: ^(.*)\t\t
Затем линуксовой утилитой diff я сравниваю содержимое файлов: diff --strip-trailing-cr -e -d file1.toc file2.toc > diff.ver
Затем (руками!) удаляю лишнее, получаются записи вида:
bin\valve_avi.dll
bin\vaudio_miles.dll
и так далее.

Перехожу в директорию с игрой и пакую это все:
for /f "delims=" %L in (diff.ver) do path\to\7za.exe a -t7z patch.7z "%L"

Способ не для ленивых, но патчи выходят не очень часто. Возможно, со след. патчем я сделаю нормальный скрипт для обработки.

@Spumer:
Spumer

Добрый день, Жуйк. Сегодня я пережил один из самых сложных и банальных сисадминских дней в своей жизни. Я переустанавливал операционную систему на файловом хранилище, ибо предыдущая перестала удовлетворять мои желания. Речь идет о замене OpenMediaVault на NAS4Free.
Основной причиной использования NAS в моем случае является наличие iSCSI, который активно используется в решаемых мной задачах. Однако, быстрота установки NAS4Free на флешку и геморой с OMV почти полностью компенсировался одной особенностью, а именно — iSCSI.
Так как я уже имел дело с iSCSI в OMV, то с той же легкостью приступил к настройке и на новой платформе, но радоваться успеху было рано, оно не работало. Сперва меня смутило отсутствие имени раздела в списке, из-за чего я провел лишний час в попытке заменить драйвера и вообще разобраться почему так. Выяснил что так и должно быть и на работоспособность совершенно не влияет. Далее, я заглянул в логи и увидел кучу сообщений like %istgt_lu_pass_init: **ERROR** LU1: LUN0: cam_open() failed% при каждой попытке подключения к iSCSI Target со стороны. Одно из таких сообщений вывело меня на единственную(!) ссылку в гугле: eromerica.wordpress.com
Описываемая в блоге проблема полностью совпала с моей. Проделав все в точности "по гайду" я убедился в работоспособности как iSCSI так и рейда, однако мне требовалося прямой доступ к устройству, а не через прослойку iSCSI->file->UFS->hardware. Мне было необходимо получить: iSCSI->hardware.
Немного пораскинув мозгами, я нашел решение: ошибки в логах свидетельствуют о проблемах при инициализации устройства, в статье диск был проинициализирован (добавлен) и отформатирован в UFS(!!!), что натолкнуло на простую идею — необходимо сообщить системе что с диском все хорошо (/проинициализировать/), дав в руки знакомую FS.
Таким образом, я использовал Adaptec 2405 с RAID 1, который был сперва добавлен в NAS4Free и затем отформатирован как UFS том. Это позволило приложению подключиться к диску и установить файловую систему на свое усмотрение (долой UFS!).
Замечу, что я не монтировал отформатировнный в UFS рейд, моя цель была iSCSI->hardware.

@Spumer:
Spumer

За несколько часов я перепробовал почти все возможные и невозможные варианты, и когда провалилась бинарная запись в файл — я понял — надо что-то менять, не в данных, не во мне, а в подходе. До того момента я старался избегать "грязных" хаков, трюков, основанных на знании работы компилятора и задачи программы, за которые она /никогда/ не выйдет. Я старался абстрагироваться, делать все максимально прозрачно (насколько мне позволяла лень :Р) и грамотно. Но у меня не осталось выхода. 

И так, я знал три(?) вещи: 
1. Итераторы не трогали данные, во всяком случае без моего приказа.
2. Итераторы предоставляли прямой доступ к данным
3. При создании итераторов я использовал метод std::basic_string c_str()/data() из описания которых следует: Modifying the character array accessed through data is *undefined behavior*.
В дополнение ко всему этому я знал, что строка изменяться уже не будет, и взглотнув побольше воздуха я сделал это — получил прямой доступ к данным через итератор, нагло избавился от const модификатора, который имел место быть благодаря c_str()/data() и стал записывать туда данные.
В исходном тексте замена пробельного символа на перенос стала выглядеть так: 	(*(xmlChar*)u8_word.base()) = '\n';
Дописав код я скомпилировал, запустил и... выдохнул. Оно работало! Данные отображались верно, без доп. копирования, проверок (ранее я пытался записывать по слову) и прочего. "Dirty hack" — то самое выражение, которое пришло в голову сразу после получения (ожидаемого?) результата.

Будь у меня более свежая голова, мозг товарища и пицца, я думаю мы разделались бы с этим за 10 минут. Но это был веселый опыт разработки. Больше, пожалуй, мне добавить нечего. Пишите код правильно ребята, не давайте доводить себя до отчаянья. У меня не было ни времени ни возможности обратиьтся к товарищу, и я решил эту проблему иначе.
Тем не менее, ссылки по теме:
https://github.com/spumer/tenzor-test/ — исходный код, виновник торжества.
http://en.cppreference.com/w/cpp/string/basic_string/data — ... through data is undefined behavior.
http://utfcpp.sourceforge.net/ — utf8 C++ portable library
http://www.xmlsoft.org/html/libxml-xmlstring.html#xmlChar

@Spumer:
Spumer

Сталось так, что при работе с библиотекой libxml2 понадобилось подсчитывать кол-во символов в строке, с целью ограничить длину строки в N символов и остальную ее часть перенести на новую(строку). Символы в этой строке естественно могут занимать от 1 до 6 байт, в соответствии с форматом UTF-8. Самолично парсить строку по стандарту не было никакого желания, а уж темболее проходиться по граблям желания было еще меньше. Благо это тема довольно изъезженная и решение в виде небольшой портабильной библиотеки уже было написано.

Язык С++, и библиотека была написана в виде итераторов для прохода по последовательности байт и функций, которые осуществляют некоторые простые операции как с итераторами, так и с самими последовательностями. libxml2 для хранения данных использует свой собственный тип xmlChar, хотя на деле это ни что иное как unsigned char. Камнем преткновения стала запись в файл, ведь именно туда мне было необходимо выгрузить все отформатированные данные. unsigned char в принципе не хотел записываться в файл и каждый раз он получался пустым, запись же в бинарном виде либо полностью колечило данные, либо их non-ASCII составляющую. Все попытки воссоздать правильную последовательность xmlChar байт с разбивкой на N символов в строке заканчивались одним — полный бред на выходе.

Немного о том, как работает UTF-8 библиотечка: созданные итераторы как и предполагается не трогают данные по которым следуют, но доступ к ним предоставляют. Доступ к символу в моем случае можно было осуществить двумя способами: operator* и метод base(), где первый возвращает данные типа unsigned int, второй же возвращает указатель на место в последовательности, в котором он в данный момент находится. Так, например, если *u8_it вернет нам 'c', то (*u8_it.base()) будет равен &("ascending"[2]). Если же вы(как и я) передавали при создании итератора константный указатель на данные (e.g. const xmlChar *) то при использовании base() для записи вам придется избавиться от const модификатора, но об этом далее.

@Spumer:
Spumer

Было бы удобно, иметь возможность публиковать код без уничтожения форматирования, пусть даже без подсветски синтаксиса. Есть ли способ сохранить пропорции отступов и переносов не в ущерб отображению на сайте?

@Spumer:
Spumer

Закинул на свой гитхаб бетку тестового задания, в состояние релиза должно перейти сегодня вечером ;) https://github.com/spumer/tenzor-test

@Spumer:
Spumer

Знаете, а libxml2 забавная штука. Например, пустое пространство определяет как тег text, и чтобы полноценно запарасить без фантомов приходится задавать флаг HTML_PARSE_NOBLANKS при обработке, а так же дополнительно проверять node->name на соответствие NULL или "text".
void parseNode(htmlNodePtr node, int level) {
	while(node != NULL) {
		/* skip blank tags */
		if( node->name != NULL && xmlStrcmp(node->name, (const xmlChar *)"text") ) {
			printf("TAG: %s. Level: %d\n", node->name, level);
			if(node->xmlChildrenNode) parseNode(node->xmlChildrenNode, level+1);
		}
		node = node->next;
	}
	return;
}

@Spumer:
Spumer

Что более тру?
		uint32_t (*piVersusSurvivorCompletion)[4] = reinterpret_cast<uint32_t(*)[4]>((unsigned char *)(this) + 976);
		for(i = 0; i < 4; ++i) {
			piVersusSurvivorCompletion[flipped][i] = g_totalResult / 4;
		}
		piVersusSurvivorCompletion[flipped][0] += g_totalResult % 4;
или же
		uint32_t *piVersusSurvivorCompletion = reinterpret_cast<uint32_t*>((unsigned char *)(this) + 976);
		for(i = 0; i < 4; ++i) {
			piVersusSurvivorCompletion[flipped*4 + i] = g_totalResult / 4;
		}
		piVersusSurvivorCompletion[flipped*4] += g_totalResult % 4;

@Spumer:
Spumer

Решил прикрутить к серверам работу с NFS, да так, чтоб из под обычного юзера можно было спокойно работать с примонтированным каталогом имея все привилегии. Для себя уяснил одну вещь — на серверной стороне каталогом должен кто-то обладать. Если же на сервере NFS каталог относится к пользователю nobody, то при монтировании его на стороне клиента он не получает должных прав и записывать в него возможно только под рутом, но только с включенным no_root_squash. После того как я овладел каталогом на сервере, он стал монтироваться с соответствующими правами на клиенте, а владельцем становился один из пользователей. По какому критерию выбирается владелец на клиенте я не знаю, в моем случае именно необходимый мне выпадает. Такие вот дела.

@Spumer:
Spumer

Пишу с другом апдейтер. На моих плечах работа с файлами и списками файлов.
Тестирую на своем каталоге с исходниками, размер которого 936 МБ в котором 40 217 файлов и 4146 каталогов. Программа обходит рекурсивно указанную папку и создает md5-хэш для каждого файла и каталога на основе его содержимого. В качестве данных для хэширования каталога, я решил использовать хэши его файлов/каталогов.

Для указанного мной ранее каталога, это заняло 15 мин. и 44 сек.
Компилировал без оптимизации, разве что -s :3
Для того же самого каталога, но с -O3 и -Os, это заняло 15 мин. 39 сек.
И наконец, просто с -O2 это заняло 15 мин. 27 сек

Пока без поддержки многоядерности. Посему наблюдается лишь 50% заргузки на моем ноуте.
Потребление памяти не замерял, но точно менее 10МБ.
Достаточно неплохие результаты на мой взгляд, буду продолжать работать в этом направлении :)

@Spumer:
Spumer

Успешно перенес систему с одного хоста на другой. Использовал netcat и tar.
На принимающей стороне:  / # nc -l 7000 | tar -xzvpf -
На отправляющей стороне: / # tar -X tar.exclude -czvpf - * | nc ip.ad.dr.ess 7000
Содержимое tar.exclude:
/mnt/*
/tmp/*
/usr/src/*
/usr/portage/distfiles/*
/var/tmp/*
/proc/*
/dev/*
/sys/*

@Spumer:
Spumer

Небольшая инструкция по простой записи образа с windows 7 и выше на флешку и последующей инсталяцией.

Качаешь UltraISO, ezbsystems.com
Никаких кряков не надо, хватит ссаной триалки.

1. Запускаешь от имени Администратора
2. Выбираешь "Пробный период"
3. Файл->Открыть... выбираешь скаченный .iso образ
4. Самозагрузка->Записать образ жесткого диска
5. Выбираешь устройство соответствующее флешке, на которую будешь записывать
5.1 Метод записи-> USB-HDD+
5.2 Убедись что не стоит галочка на "Create Boot Partition"

Возможные проблемы:
Q: Не видно флешки в устройствах, при записи.
A: Программа запущена без прав доступа к устройствам (не от имени Администратора)

Q: Таких пунктов вообще нет, у меня всё на английском
A: Включи русский язык или переводи лично, как тебе удобнее.

После всего этого, при загрузке компьютера попадаешь в Boot Menu и выбираешь флешку, в качестве устройства, с которого стоит начать загрузук.
Либо в BIOS изменяешь приоритет загрузки (обычно на вкладке Boot).

@Spumer:
Spumer

Недавно переносил форум с phpbb на более сложный, но в тоже время и более удобный движок. Конвертер из phpbb в ibp просто ужасен, пришлось отказаться. Выбор пал на xenforo. Конвертация прошла быстро и без единой ошибки, сконвертировало абсолютно всё.
Дабы форум не вывалился из индекса и хоть какой-то процент старых ссылок вел куда надо, написал простенькие rewrite-правила, которые превращают ссылки на темы и сообщения из phpbb-style в xenforo-style.
К сожалению не удалось придумать подобного правила для разделов/подразделов, в силу того, что phpbb не различает их, в то время как xenforo делит на categories и forums.