|
November 21st, 2024
11:05 pm - День отладчика Ничего не предвещало грядущих трудностей. Милая девушка со звучной должностью контент-менеджер пожаловалась, что не может разместить файлик: система ругается страшными словами "Network error". Ничего, подумал я, у них на этаже сеть собрана по дендро-фекальной технологии, может действительно на жирном файле рвётся и не даёт загрузить. Проэскалировал проблему сисадминам, и думать о ней забыл.
Некоторые поговаривают, что российские программисты прям супер талантливые. Возможно, если рассматривать их поодиночке, это действительно так. Как говорится, к пуговицам претензии есть? Нет, пришиты намертво. Но вот когда эти программисты собираются вместе и выдают продукт — тушите свет. Система, о которой шла речь выше, один из самых ярких тому примеров. Как вы уже поняли, с сетью было всё в порядке, и проблема прилетела обратно ко мне.
Хотелось бы думать, что имея за собой больше десятка лет развития и львиную долю рынка, люди хоть как-то научились информировать пользователей о проблемах в софте. Увы, "Network error" выдаётся вообще в любом случае, если ответ от сервера оказался не в том формате, в каком надо. Сделаю небольшое отступление и скажу громадное спасибо тем разрабам, которые сделали developer tools обязательным инструментом в браузере, без них геморроя было бы на порядки больше. Итак, посмотрев сетевые запросы этим прекрасным инструментом, я увидел, что после выбора файла жабоскрипт тут же загружает его на сервер, не доевреиваясь пока пользователь сохранит всю форму. Только вот вместо JSON с атрибутами сохранённого файла внезапно в ответ прилетает страница логина.
Ага, я тоже, скажем так, удивился. Не сообщение об ошибке, не 403-й статус, реальная страница входа, которую, понятно, не видно пользователю, а видно только на вкладке «Ответ» свойств сетевого запроса. Много лет разрабатываемая CMS-ка, оказывается, не способна различить обращение пользователя в закрытый раздел и вызов API. Ну да и фиг с ней, раз есть примерное понятие, куда копать, пришла пора спросить у Яндекса, где моя любимая что же более другие люди с такой проблемой делали. Несмотря на распространённость системы, Яндекс, если отбросить шелуху и SEO, выдал всего три варианта: дать нужные права, убрать ненужные файлы правил доступа, убрать лишние настройки хостера. Ну, последнее сразу не подходит, у нас свой сервер, настроек доступа не оказалось, а права выданы, и даже в большем, чем требуется, объёме.
Покрутив руль и попинав колёса, то бишь подёргав за настройки в официальном интерфейсе, я понял, что пришла пора перейти к эфиру сырцам, чтобы понять, что всё-таки там у ней внутре происходить. К счастью, написано всё на скриптах, подавляющая часть которых лежит в открытом виде. Спасибо хоть за такой подарочек. Впрочем, стиль написания у них такой, что и обфускации не надо. Начнём с того, что вызываемый скрипт содержал ровно одну строку кода: переход к другому файлу с тем же именем, но лежащему в другом месте. Естественно, в этом файле были ссылки на другие файлы, дальше вместо включения файлов стали попадаться вызовы классов — да, там причудливо мешается ООП и процедурная лапша — так что стало понятно, надо смотреть значения вживую.
Что характерно, так как это CMS, то простой вывод она к пользователю пропускает только в нужные моменты, и естественно, пока идёт инициализация и авторизация, весь вывод падает куда-то в район /dev/null. Открывать файл, писать в него, и закрывать снова геморройно, но я уже был готов от отчаяния на это пойти. К счастью, тут меня озарила, не побоюсь этого слова, генитальная идея. Можно посылать HTTP-шный заголовок! Он прекрасно пролетает к клиенту, если в файле их несколько с одним именем, останется только последний, и сразу будет ясно, у какого обрыва оборвалась тропинка.
Долго ли, коротко ли, но в конце пятого файла таки нашлась строчка, проверяющая право чтения у файла, которая возвращала мне отказ. Там внутри было написано такое, что это неслыхано, поэтому я сначала решил посмотреть за аргументы. Их, собственно, три: юзверь, идентификатор сайта, для которого проверяется, и путь. С юзверем всё было в порядке, но остальные два меня удивили. Во-первых, идентификатором сайта оказалось значение "ru". Я сначала погрешил на ошибку в настройках, всё ещё раз проверил, везде буквенно-цифровые обозначения стоят. Проверил на тестовом сайте, где всё работает, там тоже "ru". Что делать, опять лезем с сырцы, и действительно. Если принудительно в аргументах не задан идентификатор сайта, то его значением по умолчанию является код языка. Блин, они возможность держать несколько сайтов на одном движке как минимум десять лет назад запустили.
Теперь что удивило во-вторых, хотя после первого пункта удивлять не должно. Путь, который проверялся функцией, принадлежал не тому сайту, где работала невинно пострадавшая контент-менеджер, а самому первому, который на этом экземпляре движка был развёрнут. Ну а чо, логично: работаем на одном сайте, а права проверяем на дефолтном. Впрочем, фиг с ним, пошли на дефолтный сайт, выдадим права и будем радоваться.
Агащаз. В файловом менеджере системы есть наш старый сайт, есть наш новый портал, а дефолтного сайта нет. Пользователь, под которым это всё смотрится, имеет полные админские права, и от его орлиного взора скрыться ничего не может. Ладно, гора не идёт к Магомету, пойдёт Магомет. В URL для просмотра структуры в аргументах явно пишется идентификатор — сейчас мы пропишем туда идентификатор базового сайта, и решим нашу проблему. Залезаю в адресную строку, чтобы отредактировать, и тут то самое чувство: идентификатор там базового сайта, а показывает совсем другой. К счастью, эта загадка решилась достаточно быстро: когда разделяли сайты на разные доменные имена, путь на диске и путь к сайту указали не те, в результате чего один вообще не показывался, а другой спутался с первым. Но пути распутались, базовый сайт появился в интерфейсе, доступ контент-менеджерам открыли — и всё заработало.
Какая же мораль всей этой длинной унылой истории? Если придётся внедрять российский софт, требуйте доплаты за ухудшение условий труда. Или хотя бы молока за вредность.
|
December 22nd, 2023
05:38 pm - Си -- говно, а MSVC -- дважды. Как вы думаете, на что я сегодня убил полдня? Правильно, на отладку сишного кода. Полдня это потому что код сидит внутри библиотеки, которая дёргается в рантайм-контексте. Поэтому никаких тестов, скомпилили библиотечку, засунули в систему, подняли клиента, дёрнули процесс, система упала, посмотрели логи, вернулись к началу.
ну ладно, я человек упорот упорный, докопался. Так как в Си, как известно, строк нет, у меня был статический массивчик, куда клалось имя текущего верхнего элемента с тем, что если в кишках его найдётся ошибка, добавить это имя в лог. Потыкав пальцем в небо, я дал этому массиву размер в 128 байт, потому как для большинства имён это было с хорошим запасом. Но кому-то понадобилось назвать элемент «Установка прокладкоустойчивой канализационной арматуры с автоматическим фекальным запором "Сфинктер-ВЖ-ЕБ" №2». И, бинго, оно оказалось 129 символов, почему на моменте копирования из контейнера в переменную всё и рушилось.
Казалось бы, причём тут MSVC, когда сам рак? Чукча, конечно, тупой, но соломку подстелил: копировалось оно безопасной функцией strcpy_s. В MSDN пикселями по экрану четко написано: возвращаем код ошибки и ставим dest[0]=0, если dest слишком маленький. То есть по идее в случае слишком длинного имени я максимум получаю неполное сообщение о проблеме в логе проверок, и то, если оно таки будет. Собственно почему так долго и искал: всё смотрел глубже по коду, а strcpy_s она ж безопасная, там все проверки есть. Авотхуй, при копировании большего буфера в меньший трапнулась, как таиландский шимейл.
|
April 29th, 2022
11:00 pm - stkHTTor: Release Мы строили, строили и наконец построили! Урря!

Как я уже писал ранее, это совершенно тупая утиль, работающая прослойкой между программой, не умеющей в SOCKS-прокси, и Tor'ом. Заодно показывает хуй урядовому лайну, которое хотело лишить меня эльфийских веществ.
Кроме того, что криво-косо изображает из себя прокси, оно умеет следующее:
- Запускаться как консольное приложение и писать свой pid в файл, чтобы можно было легко убить
- Брать адрес, на котором слушать запросы, из переменной http_proxy
- Не делает больше ничего лишнего
- Не требует конфигурационных файлов
Лежит как всегда на шитхабе: https://github.com/steinkrauz/stkhttor
P.S. Дата создания проекта: 26 марта 2022 г., 22:45:23. Учитывая, что последние несколько дней я в основном ленился, получается, что на эту фитюльку ушёл ровно месяц. Йопаный стыд.
|
April 20th, 2022
11:09 pm - c# command line parser Писал, значится я себе соксопрокси, и чтобы не заморачиваться с файлом конфигурации, решил все настройки делать через командную строку. Тупым перебором с кучей сравнений или хитрым свитчем делать это было не интересно, да и бинарный сериалайзер я только что написал... В общем, эта хрень работает через рефлексию. Зато практически никакого запутанного кода, и минимум писанины для конечного пользователя.
А потому, поддавшись тлетворному влиянию эльфийских товарищей, всё это дело я оформил в отдельную библиотеку, точнее просто файлик с исходниками, которой можно тупо добавить в свой проект и использовать. Что оно умеет:
- короткую и длинную форму для параметра
- целые, дробные и строковые значения для параметров
- обязательность параметра
- параметр-флаг
- автоматическая генерация справки
В принципе, базовые потребности закрывает, для чего-то более извращённого лучше использовать более сложные вещи.
Смотреть тут
https://github.com/steinkrauz/StkCli
|
April 5th, 2022
11:35 pm - stkHTTor - Dogfooding DISCLAIMER. Во всём виновата эта остроухая сволочь из Хьюстона, научившая плохому.
В общем, мне не понравились существующие тулзы, и я написал свою, которая тупо изображает из себя HTTP-прокси, а на самом деле отправляет запросы через Tor (который в свою очередь изображает из себя SOCKS-прокси, а этот вариант умеют использовать не только лишь все утилиты, мало кто может).
В основном оно уже работает:

Осталось только прикрутить ключи командной строки, чтобы можно было указавать порты, и в общем-то можно будет публиковать. Требует оно .NET 5, так что по идее ещё и кросплатформенно, но это надо будет отдельно проверить.
Fun fact: made with pure editor, 100% IDE free. ( proof )
|
February 4th, 2022
08:59 pm - Запретите уже Си Потому что у меня лапки и я в Си не могу.
Полдня исправлял странный баг, при котором документ с одной привязкой отображался в системе нормально, а с двумя и более — рушил всё намертво. А оказалось совершенно элементарно, я написал
вместо того, чтобы
(*docs)[i++] = sp_tags[j];
Ну да, забыл про приоритеты.
В общем, заберите нах это пакость, где ссылки на ссылки на куски памяти передают, и дайте с нормальными контейнерами подеградировать.
|
February 2nd, 2022
10:05 pm - Статистика TrueConf Накидал на днях анализатор логов TrueConf. Чтобы, значится, можно было сказать, сколько конференций было за день, кто участвовал, сколько вся эта мутотень длилась.
https://github.com/steinkrauz/trcanal
Продукт выдаёт в консоль и в базу данных, чтобы потом можно было генеральские графики строить.
Вот так вот и деградируешь потихоньку. Потому что сначала спарсить лог конференциий, потом спарсить лог звонков, затем спарсить лог веб-сервера портала (потому что, блядь, т.н. админы шарепойнта не способны из него достать статистику показа страничек, ага). А на что-то хотя бы немного сложное и познавательное времени в результате не остаётся.
Тьфу.
|
March 29th, 2021
09:53 am - Кресты -- говно Рассмотрим сферический пример в вакууме: две проце дуры, каждая из которых итерирует последовательный коньтейнер и добавляет туда элементы.
#include <iostream>
#include <vector>
#include <list>
using namespace std;
void fart1()
{
vector<int> c;
c.push_back(1);
for(auto a:c) {
c.push_back(a++);
if (c.size()>10) break;
}
cout<<"Count:"<<c.size()<<"\n";
}
void fart2()
{
list<int> c;
c.push_back(1);
for(auto a:c) {
c.push_back(a++);
if (c.size()>10) break;
}
cout<<"Count:"<<c.size()<<"\n";
}
int main()
{
fart1();
fart2();
return 0;
}
Казалось бы, в языке здорового человека никаких различий быть не может. Но плюсы считают по-другому:
[stk@archtest Source]$ ./cppshit
Count:2
Count:11
ибо у этих пидоров реализация течёт сквозь абстракцию даже в стандартной библиотеке, причём на официальном, мать их, уровне.
|
July 30th, 2020
12:14 am - Нюансы сишечки Я в принципе сишечку довольно-таки люблю, и даже к плюсам отношусь тепло. Но иногда как вляпаешься...
Если написать вот так:
char *str = "1111-2222-333-444/001";
char *pC = strstr(str, "/");
if (pC!=NULL) *pC = '\0';
puts(str);
то получим segmentation fault (core dumped).
А вот если мы напишем вот так
char str[] = "1111-2222-333-444/001";
char *pC = strstr(str, "/");
if (pC!=NULL) *pC = '\0';
puts(str);
то всё заработает как полагается.
Не, полминуты чесания тыковки, и причины становятся совершенно очевидными, а тем, кто сразу видит выжимку из кода так и ещё быстрее всё понятно станет. Но изначально это проявилось при тестировании уже рабочего куска кода, который внезападно вдруг начал крашиться. Омерзительное ощущение.
|
December 10th, 2019
08:24 pm - Пора на свалку, на свалку пора Знаете, что забавно в этом примере? Нет, не то, что там элементарная ошибка. А то, что компилятор из этого вполне себе создаст исполняемый файл. Варнинг, конечно, будет, и для этого примера отлично виден. Только вот когда проект большой, поймать новое сообщение в летящей простыне вывода практически невозможно.
#include <iostream>
#include <algorithm>
#include <vector>
std::vector<int> make(size_t size)
{
std::vector<int> res;
for (size_t i; i<size; i++)
res.push_back(size-i);
std::sort(res.begin(), res.end());
}
int main(int argc, char *argv[])
{
std::vector<int> data = make(5);
for (int i:data)
std::cout<<i<<" ";
std::cout<<"\n";
return 0;
}
Угу, это по результатам сегодняшних приключений, когда час бился головой об стенку, не понимая, с какого перепуга обращение к std::string даёт ошибку генерала Фолта.
CPP PEDIBUS DESTRUE!
|
April 5th, 2018
10:47 pm - Пых-пых И чего все так PHP ругают? Пришлось тут на днях переписывать старую веб-мордочку в более приличный вид, так на удивление без боли и отвращения пошло. Подгорало, конечно, когда без явно выраженного квалификатора переменная просто считалась новой локальной и теряла данные, что порождало необъяснимы глюки, но это мелочи.
С другой стороны, мне достался уже PHP5, а что там в ранних версиях было, того не ведаю. Может действительно сплошной мрак и псоглавцы бегают.
|
February 16th, 2018
December 19th, 2017
11:47 pm - Марафон бесполезного софта В кои-то веки написал что-то общеупотребительное, а не очередной хак над говноAPI или тупой вызов функционала.
В общем, для таких же идиотов, как я, которым приходится работать с сишными строками, а тащить STL или ещё какие жирные библиотеки неохота. Или же для тех, у кого употребление стандартных аллокаторов может вызывать несварение в программе:
string_buider
Простейший класс, который аппендит кучу строк в свой буфер, а потом отдаёт это одной нормальной строкой. Это даже не библиотека, просто кидаете файлы в проект и наслаждаетесь.
Если случится странное, и кому-то захочется посмотреть на код, то это здесь: https://bitbucket.org/steinkrauz/string_builder
|
April 21st, 2017
10:33 pm - Фрагментация памяти Навеяло дискуссией в бложике у Кетмара. Дескать, что будет, если мы начнём память дёргать туда-сюда тупо в лоб, не задумываясь о последствиях? Отсюда родилась тупенькая программа, которая это дело исследует. Алгоритм совершенно дубовый: читаем файл в массив строк, дальше треть этого массива рандомно заменяем рандомно сгенерированными строками, и повторяем это тыщщу раз, с новыми строками для каждого раза, естественно. А в процессе замеряем, сколько времени потребовалось в каждый подход потратить на выделение памяти.
[stk@testarch memfrag]$ ./memfrag -m c test.txt
Char test selected
Total lines: 531
Max string length: 205
Allocation in load: 104ms
VM: 4256; RSS: 1268
Replace table has 177 entries
First ten allocations mean time: 35ms
Last ten allocations mean time: 39ms
Allocation time changes by 111%
VM: 9792.00; RSS: 8344.00
На маленьком файле вроде всё в порядке, ну память подросла, так и строки размером побольше могли вставиться, а по времени так вообще в пределах флюктуаций. Теперь берём файл побольше
[stk@testarch memfrag]$ ./memfrag -m c test2.txt
Char test selected
Total lines: 3181
Max string length: 205
Allocation in load: 639ms
VM: 4520; RSS: 2856
Replace table has 1060 entries
First ten allocations mean time: 225ms
Last ten allocations mean time: 421ms
Allocation time changes by 186%
VM: 46360.00; RSS: 44920.00
И тут так внезапно объём памяти вырастает на порядок, а время на её выделение почти в два раза. У-у-у-пс. Это чистые плюсы без излишеств, только выделение памяти под массивы. А теперь попробуем то же самое, но с контейнером и строками из STL:
[stk@testarch memfrag]$ ./memfrag -m s test2.txt
String test selected
Total lines: 3181
Max string length: 205
Allocation in load: 799ms
VM: 4572; RSS: 2988
Replace table has 1060 entries
First ten allocations mean time: 254ms
Last ten allocations mean time: 160ms
Allocation time changes by 62%
VM: 6156.00; RSS: 4820.00
А у них внезапно и с памятью, и со скоростью всё более, чем нормально. Но как говорится, и на старуху найдётся проруха: если вместо 1000 пустить 1300 итераций, что-то в аллокаторе всё-таки ломается.
First ten allocations mean time: 217ms
Last ten allocations mean time: 1341ms
Allocation time changes by 617%
VM: 6156.00; RSS: 4728.00
Если кто хочет сам поиграться, прога лежит на Bitbucket: https://bitbucket.org/steinkrauz/memfrag
|
February 19th, 2015
10:20 am - Pacepalm 40 аналитиков, 15 программистов, промышленная организация разработки ПО, ТЗ по ГОСТу...
Результат настолько прекрасен, что не нуждается в комментариях.
public static int getTypeOfDB(DMSession dmSession)
{
int type = 3;
try
{
if (dmSession.getCurrentSite().toString().startsWith("stidb1")) {
type = 0;
} else if ((getDB(dmSession) == 6) || (getDB(dmSession) == 7)) {
type = 2;
} else if ((getDB(dmSession) == 11) || (getDB(dmSession) == 12)) {
type = 3;
} else if (((getDB(dmSession) == 4) || (getDB(dmSession) == 5)) && (getDB(dmSession) != 13) && (getDB(dmSession) != 14)) {
type = 3;
} else if (getDB(dmSession) == 9) {
type = 4;
} else if (getDB(dmSession) == 8) {
type = 1;
} else if ((getDB(dmSession) == 0) || (getDB(dmSession) == 1) ||
(getDB(dmSession) == 13) || (getDB(dmSession) == 14)) {
type = 5;
} else if ((getDB(dmSession) == 2) || (getDB(dmSession) == 3) ||
(getDB(dmSession) == 10) ||
(getDB(dmSession) == 0) || (getDB(dmSession) == 1)) {
type = 6;
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
public static int getDB(DMSession dmSession)
{
int type = -1;
try
{
dmSession.getCurrentSite().toString();
String value_list = dmSession.getPreferenceService().getString(0, "KUPD_DB_type");
if ((list_t == null) || (list_t.size() == 0))
{
list_t.put("DB_PC21_ZAI_WORK", Integer.valueOf(0));
list_t.put("DB_PC21_ZAI_TEST", Integer.valueOf(1));
list_t.put("DB_PC21_OKB_WORK", Integer.valueOf(2));
list_t.put("DB_PC21_OKB_TEST", Integer.valueOf(3));
list_t.put("DB_BISON_ZAI_WORK", Integer.valueOf(4));
list_t.put("DB_BISON_ZAI_TEST", Integer.valueOf(5));
list_t.put("DB_STIDB_ZAI_WORK", Integer.valueOf(6));
list_t.put("DB_STIDB_ZAI_TEST", Integer.valueOf(7));
list_t.put("DB_ICARUS_ZAI_TEST", Integer.valueOf(8));
list_t.put("DB_ICARUS_ZAI_WORK", Integer.valueOf(9));
list_t.put("DB_BISON144OKB_WORK", Integer.valueOf(10));
list_t.put("DB_SU_ZAI_WORK", Integer.valueOf(11));
list_t.put("DB_SU_ZAI_TEST", Integer.valueOf(12));
list_t.put("DB_BISON144ZAI_WORK", Integer.valueOf(13));
list_t.put("DB_BISON144ZAI_TEST", Integer.valueOf(14));
}
if (list_t.toString().contains(value_list)) {
type = ((Integer)list_t.get(value_list)).intValue();
} else if (!list_t.toString().contains(value_list)) {
com.dmcenter.rac.util.MessageBox.post(Desktop.getActiveDesktop(), "Не найдено соответствующего типа для базы!", "Предупреждение", 2);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return type;
}
return type;
}
static HashMap<String, Integer> list_t = new HashMap();
|
October 29th, 2014
12:09 am - Нытья псто Постиг новые бездны отчаяния — написание передачи данных из приблуды через уёб-фронтенд. Сначала эти несчастные данные проваливаются в клиентскую библиотеку, потом их пережёвывает веб-сервер, затем глотает интерпретатор, наконец переваривает скрипт и, ВНЕЗАПНО, на выходе получается хуй. А получить какой-то сигнал из кишок этой говноцепочки та ещё задача, сравнимая по трудоёмкости с исходной. Вот блядь бегаешь кругами по потолку, пытаясь понять, в каком месте всё портится, потом падаешь на пол, стукаешься башкой и видишь, что в Content-Type опечатка. Правишь её -- и заебись, всё работает. Где сломалось, почему сломалось -- хрен узнаешь, потому как никто такие тонкости нихрена не описывает.
|
September 24th, 2014
09:35 pm - Помощь зала Всплыла тут задача: обмениваться данными между программами. Причём программы пишутся на разных языках (C# и C/C++) и возможно будут работать на разных серверах. Подход в лоб: юзаем .NETовский XMLSerializer и гоняем что-то вроде такого:
<?xml version="1.0" encoding="UTF-8"?>
<Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>Пизда Ивановна</Name>
<Value>3.14159</Value>
<vectors>
<XZVector>
<x>1</x>
<y>0</y>
<z>0</z>
</XZVector>
<XZVector>
<x>0</x>
<y>1</y>
<z>0</z>
</XZVector>
<XZVector>
<x>0</x>
<y>1</y>
<z>0</z>
</XZVector>
</vectors>
</Data>
(вид упрощённый, но примерно так и будет: головная стуктура с атрибутами и несколькими коллекциями других структур. Дочерние структуры тоже имеют атрибуты и коллекции структур. Внучатые в основном просто с атрибутами).
Собственно, вопрос. XML, конечно, удобно, но жирно, медленно и уродливо. Кто что может предложить по поводу формата обмена данными?
|
September 12th, 2014
05:44 pm - Моск кончился
static bool diff(double a, double b) {
double d = Math.Abs(a - b);
double v = Math.Abs(a) + Math.Abs(b);
if (d < 0.001) return false;
if (v > 0) {
double z = d / v;
if (z > 0.002) return true;
}
return false;
}
Господа программисты! Кто может объяснить тупому мне сакральный смысл этого метода? (Про машинное эпсилон знаю, но тут мысль явно пошла глубже)
|
May 15th, 2014
May 12th, 2014
12:13 pm - Сру на степанова Вот почему блеать erase в контейнере должен обязательно сдвигать итератор вперёд, а не оставлять его инвалидным, а не рушить принцпип одна функция -- одно действие?
UPD: Возможно, что в С++11 это поправили, создав относительно нормальный foreach, но из-за совместимости есть только С++03. А в легкотрёпе это вообще было, чего бы сразу не слямзить.
|
|
|