Embedded Sphinx, или поиск на роутере

    Поисковый сервер Sphinx (sphinxsearch) позиционируется как система, весьма неплохо масштабируемая под высокие нагрузки и большие объёмы индексов. В целом это неплохо — но иногда под рукой нет машины с 16-ядерным процессором и 256Гб оперативки. А что делать, если ядро всего одно? А если и с объёмом памяти не очень? А если это не сервер и даже не средний PC, а вообще роутер на SoC, с далеко не самым быстрым «камнем», и где всего 32Мб оперативки, да и ту нужно делить с другими процессами и системой? Взлетит ли в таком случае поисковик? Будет ли работать? Оправдано ли?
    Да, взлетит. Да, будет работать. Да, вполне оправдано.


    Предыстория
    Я работаю в одной небольшой учебной библиотеке. Нет, это не файл .dll или .so, а самая обычная — помещение со стеллажами и множеством книг, примерно 30 тысяч. Библиотеке нет и двадцати лет; когда-то она начиналась с пары стеллажей, а книг было всего несколько десятков и известны они были все наперечёт. Потом библиотека стала развиваться, книг стало всё больше и больше; в старом помещении места для них не находилось и состоялся переезд. На тот момент, когда я стал там работать, в фонде насчитывалось около 15 тыс. книг (или, более официально, «единиц хранения»), а суммарная длина всех полок на всех стеллажах приближалась к километру.
    Хотя… нет, это была не библиотека. Это была огромная свалка!
    Книги по непонятным признакам были расставлены по разным стеллажам; никаких «официальных» библиотечных систем управления не использовалось, всё делалось «на коленке». Мне в наследство досталась эта свалка, а также «база данных» — файл MS Access в котором гордо красовалась единственная таблица со всем-всем-всем (с тем же успехом она могла бы существовать в виде таблицы excel или даже списка в word — никаких «базоданных» фичей вроде нормализаций или связей между таблицами в ней и в помине не было). Беря книгу, читатель должен был на разлинованном бланке написать свою фамилию и название книги.
    Поиск книг в «базе» осуществлялся путём открытия таблицы и нажатия Ctrl+F.
    Поиск реальной книги в «свалке»… ох, это и была основная работа, занимающая, порой, весь рабочий день. Прямым перебором, чо… Через несколько месяцев работы я уже примерно представлял, в каком месте искать ту или иную книгу, но это лишь незначительно ускоряло процесс. Это бессмысленное занятие мне в конце концов надоело, и я взялся за переделку «всего».
    Спустя примерно год (бОльшая часть времени ушла непосредственно на инвентаризацию книг) у меня получилась приличная база, где все «единицы хранения» были пронумерованы, снабжены штрихкодами и привязаны к конкретным полкам. Выдача книг свелась к поиску книги в базе, чтобы узнать, на какой она полке, а затем внесение номера книги (сканером, со штрихкода) в виртуальную карточку читателя. Сдача — опять же, сканирование штрихкода, после чего книга автоматически удаляется из карточки читателя, а база любезно предлагает отнести её на конкретную полку. Занятие, которое раньше занимало полный рабочий день, теперь стало занимать считанные минуты. Ура!
    Чтобы дальше? Нормализовать базу данных? Да, сделано.
    О! А ну-ка перетащим её из MS Access куда-нибудь в «более свободный» софт? Ок, сделано, перетащил в mysql. И оригинальная база не пострадала — в ней просто поставил ODBC-драйвер и заменил внутренние таблицы на связи с теперь уже внешними.
    Теперь ничто не мешает дать читателям самим искать книги! (до этого тоже никто не мешал — но это осуществлялось путём создания копии базы .mdb, удаления оттуда чувствительных данных вроде списка кому и что выдано, и последующее распространение желающим в виде архива. Внёс сотню новых книг — и будь добр, всю рутину делай заново...). К базе было приписано скромное приложение на php с простейшей поисковой формой — и вот, теперь даже книги самому искать не надо :). Читатели находят их сами, берут с найденной полки и несут мне только чтобы отсканировать код и получить в руки. Вкалывают роботы, счастлив человек!
    Каковы издержки? Ну как же, база, форма, поиск — полный LAMPS-стек (=LAMP + S[phinx]), всё крутится на домашнем десктопе с внешним айпишником.
    НЕНАДЁЖНО!
    Ну ладно, пускай. В конце концов это не банковский сайт для обслуживания в реал-тайме транзакций половины страны.
    Но блин, включенный 24/7 компьютер ещё и электричество жрёт. И шумит… А если подумать, то кроме библиотечного сайта других причин держать его постоянно включенным и нет…


    Что если задействовать роутер?


    Настало время, когда мне совсем-совсем надоели периодические подвисания старенького роутера (DI-624) и я сменил его на WRT-160NL. Пощупал интернеты, поискал описания возможностей обновки… И уже на следующий день заменил стоковую прошивку на dd-wrt. Подцепил внешний hdd, пошаманил — и получил скромный NAS-файлопомойку. Пошарился ещё в интернетах, прочитал про optware, попробовал — и на том же внешнем hdd завёлся дополнительный софт уже для самого роутера. Transmission — пожалуйста; lighttpd — запросто! php? Без проблем! Хм… а библиотечный сайт взлетит? Да, взлетел. А если его базу mysql прямо в роутер засунуть? Ух ты, получилось! Правда, сборка mysql сильно обгрызенная; из таблиц только MyISAM — но всё равно, работает!

    А поиск?

    Это что ж выходит — торренты теперь качает сам роутер; файлопомойку и даже сайт обслуживает он же… Единственное, чего не хватает — поиск sphinx. Он по-прежнему крутится на десктопе. А что если и его на роутер?..
    Да. Как выяснилось — собирается, запускается, работает. Хорошо работает!
    Роутер я позже сменил на Asus RT-N16, а теперь на NetGear WNDR-4300. Прошивку с dd-wrt сменил на openwrt. Но sphinx по-прежнему на ней живёт.

    Как собрать свой софт под роутер?


    Конечно, покуда есть консоль и возможность собирать скрипты, создать прогамму можно многими разными способами. Некоторые из них — весьма твердолобые. Примерно как устанавливать программу из исходников на deb или rpm-based дистрибутиве с помощью ./configure && make && make install: возможно, соберётся и даже взлетит — но масштабируемость и приспособленность к поддержке практически нулевая. Поэтому используются более цивилизованные способы. Я имел дело с двумя системами сборки/публикации под роутер. Их и опишу.

    Optware

    С него началось моё знакомство с альтернативным софтом для сторонних прошивок.
    Если вкратце, optware — это софт, целиком сосредоточенный в ветке /opt. Отсюда и название. Например, демон, устанавливаемый в optware, будет лежать где-нибудь в /opt/usr/sbin, пошаренные библиотеки закинет в /opt/usr/lib, конфиги будет ожидать в /opt/etc, а скрипты запуска кинет в /opt/etc/init.d. Если на целевой системе всего 4мб флэш-памяти, и все забиты прошивкой — не беда! Нужно всего лишь смонтировать каким-либо образом /opt из другого места — и мы в шоколаде! Можем ставить optware и работать! Этим другим местом может быть, например, внешняя флэшка (вставленная в имеющийся порт usb). Или даже карточка SD, припаянная «на коленке» к любым контактам внутри роутера, состоянием которых мы способны программно управлять (например, светодиоды питания, активности, статуса — на вывод; кнопки wps и reset — на ввод. Дальше всё примерно как в ардуине или другом МК — мы «мигаем» указанными контактами в нужном порядке, изображая работу шины SPI, и считываем «нажатия кнопки». А карточка SD это понимает, и так мы получаем доступ к её содержимому буквально "через светодиоды и кнопку". ). Ну или даже внешняя сетевая ФС, подмонтированная в роутер из работающей Windows по протоколу SMB. Правда, в этом случае пропадает смысл роутера как небольшого автономного компьютера, умеющего полностью обслуживать свои сервисы.
    Optware когда-то было частью разработки свободной прошивки openwrt, однако позже отделилось в независимый проект, ввиду того, что собираемые там пакеты — полностью независимы и самодостаточны. Нужные для их работы стандартные библиотеки (libc в виде uclibc) также собираются и располагаются в optware. Это позволяет запускать очень многие приложения, практически не заботясь о возможностях и особенностях прошивки конкретного устройства.
    Минусами данного подхода является то, что заработает всё же не всё. Например, так нельзя собрать и подключить модули ядра. Также привязка к собственным стандартным библиотекам подразумевает их загрузку в память вместе с уже имеющимися стоковыми библиотеками прошивки, а значит — повышенные требования к оперативной памяти, которой на роутере и так не очень много. Наконец, мы получаем не заточенную под конкретную железку сборку, а пакет для «универсального среднестатистического роутера» — это примерно как программа под i386 (которую вы запускаете при этом на своём крутом core i7): многие возможности конкретного процессора/платформы игнорируются и откатываются к legacy. И тем не менее, optware — вполне работающее решение.
    Optware обычно собирается кросс-компилятором на linux-based хосте (я, например, собираю на ubuntu 12.04). Зачекаутив svn-репозиторий проекта, вы сразу получите и исходники, и файл README с дальнейшими инструкциями (кому любопытно — вперёд!).

    Openwrt

    Это уже целая отдельная прошивка, которая полностью заменяет оригинальную стоковую. С OpenWrt мне пришлось связаться, когда стало ясно, что возможностей ddwrt+optware недостаточно. Начитавшись хабровских статей про «умный дом» я решил понемногу автоматизировать разную домашнюю электрику. Выбрал z-wave, купил «свисток» и присоединился к облаку z-wave.me. Однако держать включённым десктоп ради связи домашней автоматизации с облаком — слишком расточительно. Тем более, что в моём роутере был не один, а целых два usb-порта! Но… свисток не завёлся. Нужного модуля ядра (cp21xx) в прошивке не оказалось. Собрать софт такого рода в optware оказалось невозможно. Пересобрать всю прошивку dd-wrt — тоже совсем нетривиальная задача (ужасная система! Моего терпения так и не хватило, чтобы настроить тулчайн, удовлетворить все зависимости и добиться-таки успешной сборки!). Openwrt для моего роутера (rt-n16) изначально не поддерживался, однако в тот момент уже нашлись патчи, позволившие сделать вполне рабочую прошивку. Так и распрощался с dd-wrt в пользу «ещё более свободной» прошивки.
    Чем хорош openwrt? Да тем, что настраивается всё. Смотрим на минусы optware, и выкидываем их из головы. Никаких дублирующихся библиотек (собираем софт прямо под имеющуюся прошивку). Никаких проблем с модулями ядра (обычный полноценный linux. Захотел экзотический «свисток»? Пожалуйста! Захотел воткнуть 3G-модем и взять из него интернет? Запросто!). Софт можно оптимизировать под имеющуюся платформу (и компилятор при сборке задействует те возможности, которые есть в чипе конкретно вашего роутера, а не откатится к legacy).
    Наконец, стОит сказать, что openwrt на самом деле никак не противоречит optware. Просто он гибче и удобнее. Однако если с предыдущей прошивки сохранился раздел /opt, то имеющийся там софт скорее всего отлично запустится в новом openwrt. Пересобирать ничего не нужно! (правда, все минусы его изолированности optware при этом останутся).

    Embedded Sphinx — что от него нужно?


    Масштабируемость сфинкса впечатляет! Достаточно рассмотреть и оценить ачивки на страничке 'powered by' проекта. Индексы на миллиарды документов; нагрузка в полмиллиарда запросов в день — вполне рабочие числа. Но это всё High Load на серьёзных машинах или даже кластерах, с шустрыми процессорами и морем оперативной памяти. А как насчёт масштабируемости в другом направлении? Мало памяти, непонятный процессор и диск? Оказывается, не так плохо. На нативной не x86 платформе сфинкс вполне нормально собирается (и проходит внутренние тесты) на Raspberry Pi. Однако если рассмотреть детали, то кроме другой архитектуры всё остальное в «Малинке» очень даже неплохо. Целых 256-512Мб оперативки! А сфинксу это и нужно! Ведь практически весь его «секрет успеха» заключается в максимальном использовании доступного железа. Он вовсе не ориентирован на философию «640Кб хватит всем», а совсем наоборот; стремится максимально следовать «модным» конфигурациям и использовать их возможности на полную катушку.
    Подешевела память? Забенчим и упихаем в неё весь индекс!
    Вошли в моду в продакшне новые модные SSD с впечатляющим временем доступа? Окей, поиграемся, попробуем — и, может, снова перетрясём внутренние стратегии и форматы, «шоб всё летало».
    Причём, это вовсе не расточительство вида «раз у нас новый шустрый проц — значит можем себе позволить эти три гига строчек сортировать пузырьком, из скрипта, написанного на бейсике», а совсем наоборот; разумные оптимизации, реализованные опытными (экс)геймдев-разработчиками и проверенные бенчами.
    И куда, спрашивается, тут соваться со своим допотопным «недокомпьютером», который только и умеет байтики пересылать из одного кабеля в другой? Ну, собственно, а почему бы и нет?
    Разумеется, подразумевается кросс-компиляция (некоторые умудряются собирать и запускать нативный тулчайн прямо на роутере — но я не очень готов к такому подходу). Раз кросс-компиляция — значит, многие внутренние тесты автотулзов (сфинкс конфигурируется именно ими) останутся в стороне и нужно будет как-то подсказать тулчейну, что мы имеем на борту железки.
    Итак, чтобы запустить сфинкс на небольшой железке нужно сперва обдумать некоторые моменты, связанные с «миниатюрностью» целевой платформы.

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

    Как удовлетворить прожорливость (вернее, «масштабное отношение») сфинкса к оперативной памяти? Тут всё достаточно просто. Сфинкс не расходует ресурсы «просто так»; его «прожорливость» напрямую зависит от поставленной задачи и от объёма индекса. Именно он, по большей части, занимает место в памяти. Значит, чем меньше индекс — тем меньше требования к памяти.
    В оперативную память загружается словарь (файл .spi) и блобы атрибутов (файлы .spa, .sps, .spm). Оценивая размеры этих файлов, можно сделать достаточно точный прогноз будущей «прожорливости».
    Если будет совсем туго — можно избавиться от атрибутов и включить опцию «словарь-на-диске». Тогда всё будет медленно и печально, зато целиком на диске. Впрочем, можно ничего не отключать, а просто подключить swap-раздел. Влезет в память — будет «летать». Не влезет — автоматом уйдёт в своп и само придёт к варианту «медленно и печально, зато работает!»

    Тип индекса?

    Я брал обычный. Со словарём keywords (просто crc уже устарел). RT не нужен хотя бы потому, что сами данные не нужно менять в real-time; достаточно периодической переиндексации (а так-то да, при надобности «взлетит» и rt). Это значит, что одного демона searchd недостаточно, а потребуется ещё и индексатор (indexer).

    Связь с источником данных?

    Что ещё? Нужна поддержка нужного источника данных (в нашем случае — mysql). До недавнего времени с этой поддержкой были определённые «грабли», связанные с архитектурой самого сфинкса — а именно, то что он состоит из «мегабиблиотеки» libsphinx и небольших исходников, реализующих непосредственные тулзы indexer, searchd и т.д. При этом практически весь функционал работы с индексами (включая их создание из источников данных) находится в libsphinx. «Грабли» поддержки источников заключаются в том, что собрав сфинкс с поддержкой mysql, все бинари сфинкса через общую мегалибу оказываются зависимыми от libmysqlclient. На серьёзной машине это не проблема, но вот на роутере иметь в бинаре зависимость от библиотеки, которая прилинкована «просто так», но совершенно не нужна для работы — расточительно! Тут есть два варианта: сделать две сборки; одну с поддержкой mysql, вторую — вообще без поддержки индексации. Из первой взять indexer, из второй — всё остальное. Второй вариант появился недавно — он просто загружает все нужные внешние библиотеки явно (через dlopen). В этом случае о лишних зависимостях можно забыть: indexer подгрузит библиотеку, когда ему будет надо; searchd вообще не будет её трогать.

    Как запускать демона?

    Классический способ реализован в самом сфинксе. Это двойной форк — сперва отцепляемся от активного терминала, потом создаём новую рабочую сессию — и вот мы уже живём в фоновом режиме, не имеем консоли и не реагируем на нажатия Ctrl+C в терминале. Записываем свой pid в pid-файл — и всё, мы стали полноценным демоном. Вроде всё ок, но это приходится реализовывать КАЖДОМУ демону в системе. А зачем дублировать код на open-source системе? Вот и умные мужики подумали-подумали и придумали upstart — он вообще не требует от «подопытного» процесса уметь становиться демоном, а просто сам держит его в фоне, хранит pid-файл в общепринятом месте и заодно реализует «сторожевую собачку», перезапускающую демона в результате внезапного падения. К слову, в своё время из-за этого возникли проблемы запуска сфинкса через upstart — две слишком умные программы всё никак не могли договориться. В итоге правильный вариант из-под upstart — запускать с опцией '--nodetach' и следить за запущенным процессом (а не его форками). При этом сфинкс не считает себя демоном, а всё фоновое управление падает на upstart).
    В случае с embedded, однако, никаких проблем с upstart нет, покуда что optware, что openwrt пользуются классическими rc.d-скриптами. Иными словами — никаких спец.флагов не нужно; своей жизнью сфинкс будет управлять сам.

    Куда девать логи?

    Изначально сфинкс ведёт общий лог демона (упал-встал-ротировался), а также отдельный лог всех запросов. Ясно, что на роутере писать что-то во флэш-память, да ещё и без контроля размера — будет подобно самоубийству. Поэтому в качестве логов указываем в конфиге 'syslog', а также не забываем добавить --with-syslog (впрочем, он теперь и так включён по дефолту) при конфигурировании сборки. В результате весь вывод посыплется в системный лог, которым мы уже будем рулить на уровне системы самого роутера. А дальше есть несколько вариантов, в зависимости от возможностей прошивки. На некоторых (вроде старого DIR-300 с очень маленьким флэшем) логи проще вообще не вести. На других кусочек хранится в кольцевом буфере прямо в памяти, и может быть прочитан из консоли командой logread. На третьих перенаправляется по udp на сервер в локалке «который может». Но в любом случае — это больше не проблема сфинкса!

    Конфигурирем и собираем!


    Все вопросы работы демона на роутере вроде решили. Теперь настало время сделать сборку!

    Обманываем autotools

    Сфинкс конфигурируется с помощью autotools. ./configure-скрипт, запускаемый перед сборкой, честно проутюжит нашу сборочную систему и узнает, какой у нас компилятор, какие функции поддерживаются, какова архитектура, каков порядок байт (LSB или MSB). Проблема в том, что практически все эти тесты затронут не целевой роутер, а ту систему, где мы запускаем тулчейн. Поэтому путь только один — подсказать конфигурации «правильные ответы». Делается это с помощью переменных окружения. Например, если ./configure проверяет доступность функции qsort, то можно дать нужную «подсказку», определив переменную ac_cv_func_qsort перед запуском ./configure. Например, выполнив перед сборкой export ac_cv_func_qsort=no — ./configure будет считать, что функции qsort у вас нет. Соответственно, сконфигурированный таким образом пакет будет либо использовать собственную реализацию, либо вообще сломается при сборке (ха-ха!).
    Для сборки embedded sphinx «явных подсказок» нужно совсем немного. Вот они все…
    • sphinx_cv_unaligned_ram_access (yes/no) — название говорит само за себя. Вкратце — внутренний формат индексов у сфинкса — сжат, выровнен по одному байту. Если вдруг потребуется взять DWORD из такого файла (загруженного в память), а он окажется лежащим по нечётному адресу — на некоторых архитектурах (например, sparc) это приведёт к крешу! Распознать ситуацию можно либо читая даташиты на целевую платформу; либо методом «научного тыка» — собрав все возможные (целых два!) варианты и попробовав запустить на целевой системе. (Технические детали: если unaligned access невозможен, то вместо того, чтобы просто взять DWORD с нужного указателя, будет задействован вызов memcpy, чтобы скопировать нужные 4 байта с произвольного адреса). На практике — на моих mips(el) unaligned работает.
    • sphinx_cv_interlocked (yes/no) — как раз издержки сборки под «legacy i386». Например, в optware для dd-wrt приходится ставить 'no', иначе сборка не удаётся. В более тюнингованной сборке openwrt тот же флажок можно не трогать; дефолтный 'yes' даёт вполне рабочий вариант. (Технические детали: в зависимости от флажка внутри используется либо атомарная lock-free операция __sync_fetch_and_add, либо структурка из переменной+мьютекса, которая делает ту же работу, но уже locked)
    • ac_cv_c_bigendian (yes/no) — самая забавная из переменных. Если не публиковать индексы никуда «во внешний мир» — данную переменную можно проигнорировать. Как там у себя внутри демон хранит блобы — неважно; главное, чтобы правильно отвечал на запросы. А общение по сети, как положено, обставлено вызовами hton/ntoh и подобными, и потому влиянию endianess не подвержено. Однако как только вы попробуете, скажем, создать индекс на своём десктопе, а потом закинуть его в роутер «шоб работал» — тут-то и вылезают различия. Вкратце — загрузить индекс с другим endianess в настоящий момент НЕВОЗМОЖНО. В принципе, можно проблему и починить, но особого смысла в этом нет (покуда нет «огромной толпы пользователей», которые загружают индексы между платформами). Разницу между индексами легко увидеть, открыв файл .sph для просмотра (прямо дампом, F3 в mc). На индексе, созданном на платформе little-endian в заголовке вы увидите magic-подпись «SPHX». На big-endian — «XHPS».
      Узнать целевое правильное значение можно несколькими способами. Во-первых — уже упомянутый «научный тык». В сфинкс встроена runtime-проверка, которая при несовпадении фактических возможностей с заданными при компиляции вежливо выругается и подскажет правильное значение. Во-вторых, можно посмотреть харатеристики собранного бинаря с помощью команды file (неявно — нажав F3 на файле в mc на убунте). Увидав там «ELF 32-bit MSB executable» — понимаем, что имеем дело с big endian. А «ELF 32-bit LSB executable» говорит о little-endian. В этом случае вам повезло — можете создавать индексы на десктопе :). Наконец, третий и самый простой способ — посмотреть имя компилятора. 'mips' — это big-endian, 'mipsel' — little endian. Критическое значение правильный endianess имеет, если вы надумаете использовать лемматизатор aot (включается в конфиге как stemmer = lemmatize_aot_ru, lemmatize_aot_en, lemmatize_aot_de или всё вместе). Блобы словарей aot созданы на «обычном» PC c little-endian, поэтому на «вражеской» архитектуре требуют конвертации (которая осуществляется «на лету» прямо при загрузке, но для этого endianess должен быть правильно выставлен при конфигурации).
    • ac_cv_func_realloc_0_nonnull=yes
    • ac_cv_func_malloc_0_nonnull=yes — упомяну просто для полноты. «Бзик» автотулзов, в сфинксе никак не используются. Но без них сборка выдаёт ошибку, которая прямым гуглением приводит к такому решению-«затычке».

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

    Sphinx в optware

    В папке целевой платформы (я собирал для ddwrt) перейдём в папку make и в ней скопируем template.mk в sphinxsearch.mk. Затем отредактируем полученную копию, руководствуясь инструкциями в самом файле. Это, собственно, и есть единственный необходимый (и зачастую достаточный) скрипт для добавления своего софта в optware. Вот ключевой кусок этого скрипта, который конфигурирует основную сборку:

    (SPHINXSEARCH_BUILD_DIR)/.configured: sphinxsearch-source make/sphinxsearch.mk
    	$(MAKE) libstdc++-stage
    	$(MAKE) expat-stage
    	$(MAKE) mysql5-stage
    	rm -rf $(BUILD_DIR)/$(SPHINXSEARCH_DIR) $(@D)
    	$(SPHINXSEARCH_UNZIP) $(DL_DIR)/$(SPHINXSEARCH_SOURCE) | tar -C $(BUILD_DIR) -xvf -
    	$(LIBSTEMMER_UNZIP) $(DL_DIR)/$(LIBSTEMMER_SOURCE) | tar -C $(BUILD_DIR)/$(SPHINXSEARCH_DIR) -xvf -
    	if test -n "$(SPHINXSEARCH_PATCHES)" ; \
    		then cat $(SPHINXSEARCH_PATCHES) | \
    		patch -d $(BUILD_DIR)/$(SPHINXSEARCH_DIR) -p0 ; \
    	fi
    	if test "$(BUILD_DIR)/$(SPHINXSEARCH_DIR)" != "$(@D)" ; \
    		then mv $(BUILD_DIR)/$(SPHINXSEARCH_DIR) $(@D) ; \
    	fi
    	(cd $(@D); \
    		export ac_cv_func_realloc_0_nonnull=yes; \
    		export ac_cv_func_malloc_0_nonnull=yes; \
    		export sphinx_cv_unaligned_ram_access=yes; \
    		export ac_cv_c_bigendian=no; \
    		export sphinx_cv_interlocked=no; \
    		$(TARGET_CONFIGURE_OPTS) \
    		CPPFLAGS="$(STAGING_CPPFLAGS) $(SPHINXSEARCH_CPPFLAGS)" \
    		LDFLAGS="$(STAGING_LDFLAGS) $(SPHINXSEARCH_LDFLAGS)" \
    		./configure \
    		--build=$(GNU_HOST_NAME) \
    		--host=$(GNU_TARGET_NAME) \
    		--target=$(GNU_TARGET_NAME) \
    		--prefix=/opt \
    		--sysconfdir=/opt/etc/sphinxsearch \
    		--with-libstemmer \
    		--with-mysql=$(STAGING_PREFIX) \
    		--without-unixodbc \
    		--with-syslog \
    		--enable-dl \
    	)
    	touch $@
    


    Остальная часть сборки тривиальна (make, strip, упаковка). Кроме исходников сфинкса также требуются исходники libstemmer (будут утянуты при сборке прямо с их сайта). Также требуются собранные expat и mysql (покуда зависим от них; они будут собраны как зависимости). Помимо основного make-файла (лежащего в ./make) в структуре папок исходников optware также используется папка ./sources/sphinxsearch. В ней лежит init-скрипт для демона, который будет упакован в пакет и впоследствии при установке окажется в /opt/etc/init.d. Сборка осуществляется из корневой папки, при этом к имени пакета добавляются определённые суффиксы:
    • make sphinxsearch — просто соберёт сфинкса в кросс-тулчайне. Полезно для отладки.
    • make sphinxsearch-ipk — соберёт сфинкса, после чего создаст пакет .ipk
    • make sphinxsearch-clean — убираем за собой мусор (после сборки).

    В результате сборки c суффиксом -ipk мы получим «готовый к употреблению» пакет sphinxsearch.ipk, который установим в optware с помощью ipkg. Дальше всё как обычно — пишем конфиг, индексируем (а покуда для dd-wrt сборка по дефолту LSB — можно индексировать и на десктопе) — и взлетаем. Вуаля! При желании можно убрать зависимость от mysql (см. директиву SPHINXSEARCH_DEPENDS в полном конфиге) — покуда для запуска, собственно, демона эта библиотека не нужна (а если создавать индекс на стороне — то ВООБЩЕ не нужна на роутере). Возможно, понадобится чуть поправить переменные ac_cv_… и sphinx_cv_… В приведённом скрипте они поставлены исходя из сборки под optware для dd-wrt (а это архитектура mipsel, т.е. little-endian, и при этом максимально урезанный до legacy набор возможностей чипа)

    Sphinx в openwrt

    Если целевой openwrt на mips — запустить optware от ddwrt не удастся (там mipsel). Если же тоже на mipsel — можно запустить sphinx из optware, однако и в этом случае гораздо оправданнее в плане бережного использования ресурсов будет воспользоваться полной открытостью openwrt и сделать сборку именно под целевую систему — этим избавимся от ограничений legacy, а также получим более оптимальный код, который не будет грузить в память ещё один «чуть-чуть другой» экземпляр стандартной библиотеки.
    Для сборки в openwrt также используются make-скрипты, только их формат и местоположение иные. Нужные файлы размещаются в отдельной папке, созданной где-нибудь в дереве package от корня сборочной среды. Например, я выбрал package/network/services/sphinx. В указанной папке минимально необходим файл Makefile, а также могут быть другие файлы и папки с предопределёнными именами. Я помимо Makefile использую ещё Config.in (он создаёт подменюшку в основном menuconfig), а также папку file/ где лежит образец конфига и init-скрипт (к слову, гораздо более короткий чем в optware). Ключевая часть конфигурации для openwrt выглядит вот так:
    CONFIGURE_VARS += \
    	ac_cv_func_realloc_0_nonnull=yes \
    	ac_cv_func_malloc_0_nunnul=yes \
    	ac_cv_c_bigendian=yes \
    	sphinx_cv_unaligned_ram_access=yes
    
    CONFIGURE_ARGS += \
    	--prefix=/ \
    	--sysconfdir=/etc/sphinx \
    	$(if $(CONFIG_SPHINX_MYSQL_SUPPORT),--with-mysql,--without-mysql) \
    	$(if $(CONFIG_SPHINX_PGSQL_SUPPORT),--with-pgsql,--without-pgsql) \
    	$(if $(CONFIG_SPHINX_UNIXODBC_SUPPORT),--with-unixodbc,--without-unixodbc) \
    	$(if $(CONFIG_SPHINX_EXPAT_SUPPORT),--with-libexpat,--without-libexpat) \
    	$(if $(CONFIG_SPHINX_DYNAMIC_LOAD),--enable-dl,,) \
    	--with-syslog \
    	--with-libstemmer
    


    Даже визуально она короче, чем в optware (сам скрипт целиком тоже существенно короче — всего 84 строки, бОльшая часть из которых — шаблонные). При этом задействованы переменные, которые конфигурируются из меню. В этой версии, возможно, потребуется подправить под свои нужны ac_cv_c_bigendian (я собирал под роутер NetGear WNDR4300, который MIPS, т.е. big-endian). Ещё в этой версии я не стал грузить при сборке отдельно libstemmer. Вместо этого его необходимо скачать заранее самостоятельно и запаковать в тарболл с исходниками (и поправить PKG_MD5SUM на ту, что будет у полученного тарболла).
    Сборка пакета в openwrt осуществляется в два этапа. Во-первых, запускаем menuconfig в корневой папке и настраиваем прошивку в целом. Там НУЖНО зайти в раздел Network/Web Servers/Proxies — и уже там выбрать sphinx в виде модуля (M). Там же можно зайти в подменю конфигурации и поставить сборку с поддержкой нужных источников данных. Затем выходим из menuconfig, сохраняя изменения — и наконец, запускаем сборку всей прошивки:
    make
    либо только одного сфинкса (естественно, с зависимостями, если нужны)
    make package/network/services/sphinx/compile
    Полученные пакеты складываются в папку ./bin/ARCH/packages (в моём случае это ./bin/ar71xx/packages). Установить пакет можно либо прямо разыскав его и скопировав на роутер (а там натравив на него opkg), либо опубликовав сборочную папку на локальном веб-сервере и прописав путь к ней в /etc/opkg.conf на роутере, а затем — opkg update; opkg install sphinx.
    Для индексации на роутере возможно отдельно понадобится установить клиентскую библиотеку mysql (я не стал прописывать её в зависимости установки в случае конфигурирования с динамической загрузкой нужных либ). И ещё — вручную создать для неё симлинк (командой ln -s libmysqlclient.so.16.0.0 libmysqlclient.so в папке где лежит либа). В остальном всё работает из коробки. Моё приложение использует сфинкс по протоколу sphinxql (т.е. та же libmysqlclient используется не только сфинксом для индексации mysql, но и клиентами для работы уже с самим сфинксом), однако legacy-протокол сфинкса (sphinx api, который всячески рекомендуется больше не трогать из клиентских приложений) также вполне функционален.

    Напоследок — конфиги и скрипты.

    Для optware:
    Сборочный скрипт
    Помещается в файл sphinxsearch.mk и кладётся в папку make платформы optware
    ###########################################################
    #
    # sphinxsearch
    #
    ###########################################################
    
    # You must replace "sphinxsearch" and "SPHINXSEARCH" with the lower case name and
    # upper case name of your new package.  Some places below will say
    # "Do not change this" - that does not include this global change,
    # which must always be done to ensure we have unique names.
    
    #
    # SPHINXSEARCH_VERSION, SPHINXSEARCH_SITE and SPHINXSEARCH_SOURCE define
    # the upstream location of the source code for the package.
    # SPHINXSEARCH_DIR is the directory which is created when the source
    # archive is unpacked.
    # SPHINXSEARCH_UNZIP is the command used to unzip the source.
    # It is usually "zcat" (for .gz) or "bzcat" (for .bz2)
    #
    # You should change all these variables to suit your package.
    # Please make sure that you add a description, and that you
    # list all your packages' dependencies, seperated by commas.
    # 
    # If you list yourself as MAINTAINER, please give a valid email
    # address, and indicate your irc nick if it cannot be easily deduced
    # from your name or email address.  If you leave MAINTAINER set to
    # "NSLU2 Linux" other developers will feel free to edit.
    # http://sphinxsearch.com/files/sphinx-2.0.5-release.tar.gz
    #SPHINXSEARCH_SITE=http://sphinxsearch.com/files
    SPHINXSEARCH_SITE=http://192.168.1.5:65080/r/sphinxsearch
    SPHINXSEARCH_VERSION=2.2.2-4470
    SPHINXSEARCH_SOURCE=sphinx-$(SPHINXSEARCH_VERSION).tar.gz
    SPHINXSEARCH_DIR=sphinx-$(SPHINXSEARCH_VERSION)
    SPHINXSEARCH_UNZIP=zcat
    SPHINXSEARCH_MAINTAINER=NSLU2 Linux <nslu2-linux@yahoogroups.com>
    SPHINXSEARCH_DESCRIPTION=Sphinx is free open-source SQL full-text search engine.
    SPHINXSEARCH_SECTION=misc
    SPHINXSEARCH_PRIORITY=optional
    SPHINXSEARCH_DEPENDS=libstdc++, expat, mysql5
    SPHINXSEARCH_SUGGESTS=
    SPHINXSEARCH_CONFLICTS=
    LIBSTEMMER_SITE=http://snowball.tartarus.org/dist
    LIBSTEMMER_SOURCE=libstemmer_c.tgz
    LIBSTEMMER_UNZIP=zcat
    
    #
    # SPHINXSEARCH_IPK_VERSION should be incremented when the ipk changes.
    #
    SPHINXSEARCH_IPK_VERSION=2
    
    #
    # SPHINXSEARCH_CONFFILES should be a list of user-editable files
    SPHINXSEARCH_CONFFILES=/opt/etc/sphinxsearch/sphinx.conf
    
    #
    # SPHINXSEARCH_PATCHES should list any patches, in the the order in
    # which they should be applied to the source code.
    #
    #SPHINXSEARCH_PATCHES=$(SPHINXSEARCH_SOURCE_DIR)/configure.patch
    SPHINXSEARCH_PATCHES=
    
    #
    # If the compilation of the package requires additional
    # compilation or linking flags, then list them here.
    #
    SPHINXSEARCH_CPPFLAGS=
    SPHINXSEARCH_LDFLAGS=
    
    #
    # SPHINXSEARCH_BUILD_DIR is the directory in which the build is done.
    # SPHINXSEARCH_SOURCE_DIR is the directory which holds all the
    # patches and ipkg control files.
    # SPHINXSEARCH_IPK_DIR is the directory in which the ipk is built.
    # SPHINXSEARCH_IPK is the name of the resulting ipk files.
    #
    # You should not change any of these variables.
    #
    SPHINXSEARCH_BUILD_DIR=$(BUILD_DIR)/sphinxsearch
    SPHINXSEARCH_SOURCE_DIR=$(SOURCE_DIR)/sphinxsearch
    SPHINXSEARCH_IPK_DIR=$(BUILD_DIR)/sphinxsearch-$(SPHINXSEARCH_VERSION)-ipk
    SPHINXSEARCH_IPK=$(BUILD_DIR)/sphinxsearch_$(SPHINXSEARCH_VERSION)-$(SPHINXSEARCH_IPK_VERSION)_$(TARGET_ARCH).ipk
    
    .PHONY: sphinxsearch-source sphinxsearch-unpack sphinxsearch sphinxsearch-stage sphinxsearch-ipk sphinxsearch-clean sphinxsearch-dirclean sphinxsearch-check
    
    #
    # This is the dependency on the source code.  If the source is missing,
    # then it will be fetched from the site using wget.
    #
    $(DL_DIR)/$(SPHINXSEARCH_SOURCE):
    	$(WGET) -P $(@D) $(SPHINXSEARCH_SITE)/$(@F) || \
    	$(WGET) -P $(@D) $(SOURCES_NLO_SITE)/$(@F)
    
    $(DL_DIR)/$(LIBSTEMMER_SOURCE):
    	$(WGET) -P $(@D) $(LIBSTEMMER_SITE)/$(@F) || \
    	$(WGET) -P $(@D) $(SOURCES_NLO_SITE)/$(@F)
    
    #
    # The source code depends on it existing within the download directory.
    # This target will be called by the top level Makefile to download the
    # source code's archive (.tar.gz, .bz2, etc.)
    #
    
    sphinxsearch-source: $(DL_DIR)/$(SPHINXSEARCH_SOURCE) $(DL_DIR)/$(LIBSTEMMER_SOURCE) $(SPHINXSEARCH_PATCHES)
    
    #
    # This target unpacks the source code in the build directory.
    # If the source archive is not .tar.gz or .tar.bz2, then you will need
    # to change the commands here.  Patches to the source code are also
    # applied in this target as required.
    #
    # This target also configures the build within the build directory.
    # Flags such as LDFLAGS and CPPFLAGS should be passed into configure
    # and NOT $(MAKE) below.  Passing it to configure causes configure to
    # correctly BUILD the Makefile with the right paths, where passing it
    # to Make causes it to override the default search paths of the compiler.
    #
    # If the compilation of the package requires other packages to be staged
    # first, then do that first (e.g. "$(MAKE) <bar>-stage <baz>-stage").
    #
    # If the package uses  GNU libtool, you should invoke $(PATCH_LIBTOOL) as
    # shown below to make various patches to it.
    #
    $(SPHINXSEARCH_BUILD_DIR)/.configured: sphinxsearch-source make/sphinxsearch.mk
    	$(MAKE) libstdc++-stage
    	$(MAKE) expat-stage
    	$(MAKE) mysql5-stage
    	rm -rf $(BUILD_DIR)/$(SPHINXSEARCH_DIR) $(@D)
    	$(SPHINXSEARCH_UNZIP) $(DL_DIR)/$(SPHINXSEARCH_SOURCE) | tar -C $(BUILD_DIR) -xvf -
    	$(LIBSTEMMER_UNZIP) $(DL_DIR)/$(LIBSTEMMER_SOURCE) | tar -C $(BUILD_DIR)/$(SPHINXSEARCH_DIR) -xvf -
    	if test -n "$(SPHINXSEARCH_PATCHES)" ; \
    		then cat $(SPHINXSEARCH_PATCHES) | \
    		patch -d $(BUILD_DIR)/$(SPHINXSEARCH_DIR) -p0 ; \
    	fi
    	if test "$(BUILD_DIR)/$(SPHINXSEARCH_DIR)" != "$(@D)" ; \
    		then mv $(BUILD_DIR)/$(SPHINXSEARCH_DIR) $(@D) ; \
    	fi
    	(cd $(@D); \
    		export ac_cv_func_realloc_0_nonnull=yes; \
    		export ac_cv_func_malloc_0_nonnull=yes; \
    		export sphinx_cv_unaligned_ram_access=yes; \
    		export sphinx_cv_interlocked=no; \
    		$(TARGET_CONFIGURE_OPTS) \
    		CPPFLAGS="$(STAGING_CPPFLAGS) $(SPHINXSEARCH_CPPFLAGS)" \
    		LDFLAGS="$(STAGING_LDFLAGS) $(SPHINXSEARCH_LDFLAGS)" \
    		./configure \
    		--build=$(GNU_HOST_NAME) \
    		--host=$(GNU_TARGET_NAME) \
    		--target=$(GNU_TARGET_NAME) \
    		--prefix=/opt \
    		--sysconfdir=/opt/etc/sphinxsearch \
    		--with-libstemmer \
    		--with-mysql=$(STAGING_PREFIX) \
    		--without-unixodbc \
    		--with-syslog \
    		--enable-dl \
    	)
    #	$(PATCH_LIBTOOL) $(@D)/libtool
    	touch $@
    
    sphinxsearch-unpack: $(SPHINXSEARCH_BUILD_DIR)/.configured
    
    #
    # This builds the actual binary.
    #
    $(SPHINXSEARCH_BUILD_DIR)/.built: $(SPHINXSEARCH_BUILD_DIR)/.configured
    	rm -f $@
    	$(MAKE) -C $(@D)
    	touch $@
    
    #
    # This is the build convenience target.
    #
    sphinxsearch: $(SPHINXSEARCH_BUILD_DIR)/.built
    
    #
    # If you are building a library, then you need to stage it too.
    #
    $(SPHINXSEARCH_BUILD_DIR)/.staged: $(SPHINXSEARCH_BUILD_DIR)/.built
    	rm -f $@
    	$(MAKE) -C $(@D) DESTDIR=$(STAGING_DIR) install
    	touch $@
    
    sphinxsearch-stage: $(SPHINXSEARCH_BUILD_DIR)/.staged
    
    #
    # This rule creates a control file for ipkg.  It is no longer
    # necessary to create a seperate control file under sources/sphinxsearch
    #
    $(SPHINXSEARCH_IPK_DIR)/CONTROL/control:
    	@install -d $(@D)
    	@rm -f $@
    	@echo "Package: sphinxsearch" >>$@
    	@echo "Architecture: $(TARGET_ARCH)" >>$@
    	@echo "Priority: $(SPHINXSEARCH_PRIORITY)" >>$@
    	@echo "Section: $(SPHINXSEARCH_SECTION)" >>$@
    	@echo "Version: $(SPHINXSEARCH_VERSION)-$(SPHINXSEARCH_IPK_VERSION)" >>$@
    	@echo "Maintainer: $(SPHINXSEARCH_MAINTAINER)" >>$@
    	@echo "Source: $(SPHINXSEARCH_SITE)/$(SPHINXSEARCH_SOURCE)" >>$@
    	@echo "Description: $(SPHINXSEARCH_DESCRIPTION)" >>$@
    	@echo "Depends: $(SPHINXSEARCH_DEPENDS)" >>$@
    	@echo "Suggests: $(SPHINXSEARCH_SUGGESTS)" >>$@
    	@echo "Conflicts: $(SPHINXSEARCH_CONFLICTS)" >>$@
    
    #
    # This builds the IPK file.
    #
    # Binaries should be installed into $(SPHINXSEARCH_IPK_DIR)/opt/sbin or $(SPHINXSEARCH_IPK_DIR)/opt/bin
    # (use the location in a well-known Linux distro as a guide for choosing sbin or bin).
    # Libraries and include files should be installed into $(SPHINXSEARCH_IPK_DIR)/opt/{lib,include}
    # Configuration files should be installed in $(SPHINXSEARCH_IPK_DIR)/opt/etc/sphinxsearch/...
    # Documentation files should be installed in $(SPHINXSEARCH_IPK_DIR)/opt/doc/sphinxsearch/...
    # Daemon startup scripts should be installed in $(SPHINXSEARCH_IPK_DIR)/opt/etc/init.d/S??sphinxsearch
    #
    # You may need to patch your application to make it use these locations.
    #
    $(SPHINXSEARCH_IPK): $(SPHINXSEARCH_BUILD_DIR)/.built
    	rm -rf $(SPHINXSEARCH_IPK_DIR) $(BUILD_DIR)/sphinxsearch_*_$(TARGET_ARCH).ipk
    	$(MAKE) -C $(SPHINXSEARCH_BUILD_DIR) DESTDIR=$(SPHINXSEARCH_IPK_DIR) install-strip
    	install -d $(SPHINXSEARCH_IPK_DIR)/opt/etc/sphinxsearch
    	install -m 644 $(SPHINXSEARCH_BUILD_DIR)/sphinx-min.conf.dist $(SPHINXSEARCH_IPK_DIR)/opt/etc/sphinxsearch/sphinx.conf
    	install -d $(SPHINXSEARCH_IPK_DIR)/opt/doc/sphinxsearch
    	install -m 644 $(SPHINXSEARCH_BUILD_DIR)/doc/sphinx.txt $(SPHINXSEARCH_IPK_DIR)/opt/doc/sphinxsearch/sphinx.txt
    	rm $(SPHINXSEARCH_IPK_DIR)/opt/etc/sphinxsearch/sphinx.conf.dist
    	rm $(SPHINXSEARCH_IPK_DIR)/opt/etc/sphinxsearch/example.sql
    	rm $(SPHINXSEARCH_IPK_DIR)/opt/etc/sphinxsearch/sphinx-min.conf.dist
    	install -d $(SPHINXSEARCH_IPK_DIR)/opt/etc/init.d
    	install -m 755 $(SPHINXSEARCH_SOURCE_DIR)/rc.sphinxsearch $(SPHINXSEARCH_IPK_DIR)/opt/etc/init.d/S90sphinxsearch
    	ln -s S90sphinxsearch $(SPHINXSEARCH_IPK_DIR)/opt/etc/init.d/K70sphinxsearch
    #	sed -i -e '/^#!/aOPTWARE_TARGET=${OPTWARE_TARGET}' $(SPHINXSEARCH_IPK_DIR)/opt/etc/init.d/SXXsphinxsearch
    	$(MAKE) $(SPHINXSEARCH_IPK_DIR)/CONTROL/control
    #	install -m 755 $(SPHINXSEARCH_SOURCE_DIR)/postinst $(SPHINXSEARCH_IPK_DIR)/CONTROL/postinst
    #	sed -i -e '/^#!/aOPTWARE_TARGET=${OPTWARE_TARGET}' $(SPHINXSEARCH_IPK_DIR)/CONTROL/postinst
    #	install -m 755 $(SPHINXSEARCH_SOURCE_DIR)/prerm $(SPHINXSEARCH_IPK_DIR)/CONTROL/prerm
    #	sed -i -e '/^#!/aOPTWARE_TARGET=${OPTWARE_TARGET}' $(SPHINXSEARCH_IPK_DIR)/CONTROL/prerm
    #	if test -n "$(UPD-ALT_PREFIX)"; then \
    		sed -i -e '/^[ 	]*update-alternatives /s|update-alternatives|$(UPD-ALT_PREFIX)/bin/&|' \
    			$(SPHINXSEARCH_IPK_DIR)/CONTROL/postinst $(SPHINXSEARCH_IPK_DIR)/CONTROL/prerm; \
    	fi
    	echo $(SPHINXSEARCH_CONFFILES) | sed -e 's/ /\n/g' > $(SPHINXSEARCH_IPK_DIR)/CONTROL/conffiles
    	cd $(BUILD_DIR); $(IPKG_BUILD) $(SPHINXSEARCH_IPK_DIR)
    	$(WHAT_TO_DO_WITH_IPK_DIR) $(SPHINXSEARCH_IPK_DIR)
    
    #
    # This is called from the top level makefile to create the IPK file.
    #
    sphinxsearch-ipk: $(SPHINXSEARCH_IPK)
    
    #
    # This is called from the top level makefile to clean all of the built files.
    #
    sphinxsearch-clean:
    	rm -f $(SPHINXSEARCH_BUILD_DIR)/.built
    	-$(MAKE) -C $(SPHINXSEARCH_BUILD_DIR) clean
    
    #
    # This is called from the top level makefile to clean all dynamically created
    # directories.
    #
    sphinxsearch-dirclean:
    	rm -rf $(BUILD_DIR)/$(SPHINXSEARCH_DIR) $(SPHINXSEARCH_BUILD_DIR) $(SPHINXSEARCH_IPK_DIR) $(SPHINXSEARCH_IPK)
    #
    #
    # Some sanity check for the package.
    #
    sphinxsearch-check: $(SPHINXSEARCH_IPK)
    	perl scripts/optware-check-package.pl --target=$(OPTWARE_TARGET) $^
    
    


    init-скрипт
    Помещается в файл rc.sphinxsearch и кладётся в папку sources/sphinxsearch платформы optware (папку сперва нужно создать). При сборке он будет скопирован в пакет.
    #!/bin/sh
    
    NAME=sphinxsearch
    DAEMON=searchd
    
    # only used for virgin run
    DATA_PART=/mnt
    [ -d /mnt/C ] && DATA_PART=/mnt/C
    
    prefix="/opt"
    export PATH=${prefix}/bin:${prefix}/sbin:/bin:/usr/bin:/sbin:/usr/sbin:${PATH}
    
    DAEMON=${prefix}/bin/${DAEMON}
    SCRIPT="`basename $0`"
    
    test -x $DAEMON || exit 0
    
    if [ -z "$1" ] ; then
        case `echo "$0" | sed 's:^.*/\(.*\):\1:g'` in
            S??*) rc="start" ;;
            K??*) rc="stop" ;;
            *) rc="usage" ;;
        esac
    else
        rc="$1"
    fi
    
    case "$rc" in
        start)
            if [ -n "`pidof $DAEMON`" ]; then
              echo "$NAME is already running"
            else
      	  echo "Starting SphinxSearch daemon: $NAME"
              export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH
              pth=`pwd`
              $DAEMON
              cd "$pth"
              export LD_LIBRARY_PATH=$OLD_LIBRARY_PATH
    	fi
            ;;
        stop)
            if [ -n "`pidof $DAEMON`" ]; then
                echo "Stopping SphinxSearch daemon: $NAME"
                pth=`pwd`
                n=1
                while true; do
          	      $DAEMON --stop
                  sleep 1
    	      [ ! -n "`pidof $DAEMON`" ] && break
    	      sleep 5
    	      [ $n -gt 3 ] && break
    	      let n+=1
    	    done
                n=1
                while true; do
                  killall -9 $NAME 2>/dev/null
                  sleep 1
    	      [ ! -n "`pidof $DAEMON`" ] && break
    	      sleep 2
    	      [ $n -gt 10 ] && break
    	      let n+=1
    	    done
    	    if [ -n "`pidof $DAEMON`" ]; then
    	      echo "Termination of $NAME was not successful, it keeps running"
                  sleep 1
                fi
                cd "$pth"
            else
                echo "$NAME already stopped"
            fi
            ;;
        status)
            if [ -n "`pidof $DAEMON`" ]; then
    	    echo "$NAME is running"
            else
              echo "$NAME is not running"
            fi
            ;;
        restart)
            "$0" stop
            "$0" start
            ;;
        *)  
            echo "Usage: $0 (start|stop|restart|usage)"
            ;;
    esac
    
    exit 0
    



    Для openwrt:
    Сборочный скрипт
    находится в файле Makefile и размещается в папке package/network/services/sphinx (папку сперва нужно создать).
    include $(TOPDIR)/rules.mk
    
    PKG_NAME:=sphinx
    PKG_VERSION:=2.2.2
    PKG_REVISION:=4470
    PKG_SUFFIX:=stemmer
    PKG_RELEASE:=2
    #PKG_MD5SUM:=3119bbeafc9e32637339c6e95a3317ef
    
    PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_REVISION)-$(PKG_SUFFIX).tar.gz
    PKG_MAINTAINER:=Aleksey Vinogradov <klirichek@sphinxsearch.com>
    PKG_SOURCE_URL:=http://192.168.1.5:65080/r/sphinxsearch
    PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_REVISION)-$(PKG_SUFFIX)
    PKG_BUILD_PARALLEL:=1
    
    PKG_DPNDS:= +SPHINX_MYSQL_SUPPORT:libmysqlclient +SPHINX_PGSQL_SUPPORT:libpq +SPHINX_UNIXODBC_SUPPORT:unixodbc +SPHINX_EXPAT_SUPPORT:libexpat
    
    ifeq ($(CONFIG_SPHINX_DYNAMIC_LOAD),y)
        PKG_BUILD_DEPENDS:= $(PKG_DEPENDS)
    endif
    
    include $(INCLUDE_DIR)/package.mk
    
    define Package/sphinx
      SECTION:=net
      CATEGORY:=Network
      SUBMENU:=Web Servers/Proxies
      TITLE:=sphinxsearch - fast FT search engine server
      DEPENDS:=+libstdcpp +librt +libpthread +zlib
      ifneq ($(CONFIG_SPHINX_DYNAMIC_LOAD),y)
    	DEPENDS+= $(PKG_DPNDS)
      endif
      MENU:=1
    endef
    
    define Package/sphinx/config
      source "$(SOURCE)/Config.in"
    endef
    
    define Package/sphinx/conffiles
      /etc/sphinx/sphinx.conf
    endef
    
    # лень было заполнять :)
    define Package/sphinx/description
     This is placeholder for sphinxsearch description
    endef
    
    CONFIGURE_VARS += \
    	ac_cv_func_realloc_0_nonnull=yes \
    	ac_cv_func_malloc_0_nunnul=yes \
    	ac_cv_c_bigendian=yes \
    	sphinx_cv_unaligned_ram_access=yes
    
    CONFIGURE_ARGS += \
    	--prefix=/ \
    	--sysconfdir=/etc/sphinx \
    	$(if $(CONFIG_SPHINX_MYSQL_SUPPORT),--with-mysql,--without-mysql) \
    	$(if $(CONFIG_SPHINX_PGSQL_SUPPORT),--with-pgsql,--without-pgsql) \
    	$(if $(CONFIG_SPHINX_UNIXODBC_SUPPORT),--with-unixodbc,--without-unixodbc) \
    	$(if $(CONFIG_SPHINX_EXPAT_SUPPORT),--with-libexpat,--without-libexpat) \
    	$(if $(CONFIG_SPHINX_DYNAMIC_LOAD),--enable-dl,,) \
    	--with-syslog \
    	--with-libstemmer
    
    define Package/sphinx/install
    	$(INSTALL_DIR) $(1)/etc/sphinx
    	$(INSTALL_DATA) ./files/sphinx.conf $(1)/etc/sphinx/sphinx.conf
    	$(INSTALL_DIR) $(1)/etc/init.d
    	$(INSTALL_BIN) ./files/sphinx.init $(1)/etc/init.d/sphinx
    	$(INSTALL_DIR) $(1)/usr/sbin
    	$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/searchd $(1)/usr/sbin/
    	$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/indexer $(1)/usr/sbin/
    # если нужны оставшиеся тулзы - раскомментировать строчки ниже.
    #	$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/indextool $(1)/usr/sbin/
    #	$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/spelldump $(1)/usr/sbin/
    endef
    
    $(eval $(call BuildPackage,sphinx))
    

    Конфигурация меню
    находится в файле Config.in и размещается вместе с Makefile в папке package/network/services/sphinx
    # sphinx config
    menu  "Configuration"
    	depends on PACKAGE_sphinx
    
    	config SPHINX_DYNAMIC_LOAD
    		bool "Load all client libs for accessing sources dynamically"
    		default y
    		help
    			This will force the sphinx to load necessary db libs
    			only when actually using db sources (otherwize they will
    			be linked statically and will be dependencies for the
    			sphinx package)
    
    	config SPHINX_MYSQL_SUPPORT
    		bool "Enable indexing of mysql databases"
    		select PACKAGE_libmysqlclient
    		default n
    		help
    		  This will build the sphinx with supporting of mysql db indexing.
    		  It will allow to use source type=mysql, and also need
    	 	  libmysqlclient library in order to work.
    
    	config SPHINX_PGSQL_SUPPORT
    		bool "Enable indexing of posgresql databases"
    		select PACKAGE_libpq
    		default n
    		help
    		  This will build the sphinx with supporting of posgresql db indexing.
    		  It will allow to use source type=pgsql, and also need
    	 	  libpq library in order to work.
    
    	config SPHINX_UNIXODBC_SUPPORT
    		bool "Enable indexing of odbc sources"
    		select PACKAGE_unixodbc
    		default n
    		help
    		  This will build the sphinx with supporting of indexing odbc sources.
    		  It will allow to use source type=odbc, and also need
    		  unixodbc library in order to work.
    
    	config SPHINX_EXPAT_SUPPORT
    		bool "Enable indexing of xmlpipe sources"
    		select PACKAGE_libexpat
    		default n
    		help
    		  This will build the sphinx with supporting of indexing xmlpipes.
    		  It will allow to use source type=xmlpipe2, and also need
    		  libexpat library in order to work.
    
    endmenu
    

    Init-скрипт
    находится в файле sphinx.init и размещается в папке package/network/services/sphinx/files (папку сперва нужно создать)
    #!/bin/sh /etc/rc.common
    # Copyright (C) 2010-2011 OpenWrt.org
    
    START=95
    STOP=10
    
    SERVICE_STOP_TIME=9
    
    #PREFIX=/opt
    PREFIX=""
    
    error() {
    	echo "${initscript}:" "$@" 1>&2
    }
    
    start() {
    	$PREFIX/usr/sbin/searchd
    }
    
    stop() {
    	$PREFIX/usr/sbin/searchd --stop
    }
    

    Пример конфига
    находится в файле sphinx.conf и размещается вместе со sphinx.init в папке package/network/services/sphinx/files
    Конфиг у вас будет свой; тут я в качестве примера даю конфиг индекса своей библиотеки.
    #
    # Sphinx index for library (clean, simple, functional)
    #
    
    source ltslibrary_src
    {
    	type			= mysql
    
    	sql_host		= 127.0.0.1
    	sql_user		= #wiped
    	sql_pass		= #wiped
    	sql_db			= my_lib
    	sql_query_pre		= SET NAMES utf8
    	sql_query		= SELECT * FROM sphinx_main_index
    	sql_joined_field	= title FROM QUERY; SELECT * FROM all_titles_sphinx_un
    	sql_attr_timestamp	= entered
    	sql_attr_uint		= pages
    	sql_attr_float		= price
    	sql_attr_float		= thickness
    	sql_attr_uint		= crcyear
    	sql_attr_string		= year
    }
    
    index ltslib
    {
    	source			= ltslibrary_src
    	path			= /mnt/sphinx/index/ltsidx
    	preopen			= 1
    	morphology		= lemmatize_ru_all, lemmatize_en_all, lemmatize_de_all, libstemmer_fr
    	expand_keywords		= 1
    	index_exact_words	= 1
    	min_prefix_len		= 2
    	min_word_len		= 2
    	dict			= keywords
    	stopwords		= /mnt/sphinx/stopwords-en.txt
    	wordforms		= /mnt/bigstore/library/sphinx/wordforms.txt
    }
    
    indexer
    {
    	mem_limit		= 32M
    }
    
    common
    {
    	lemmatizer_base		= /mnt/sphinx/aot
    }
    
    searchd
    {
    	listen			= localhost:9306:mysql41
    	log			= syslog
    	query_log		= syslog
    	read_timeout		= 5
    	max_children		= 30
    	pid_file		= /mnt/sphinx/searchd.pid
    	max_matches		= 1000
    	seamless_rotate		= 1
    	preopen_indexes		= 0
    	unlink_old		= 1
    	workers			= threads # for RT to work
    	binlog_path		=
    	subtree_docs_cache	= 1M
    	subtree_hits_cache	= 1M
    }
    


    Ну и пара готовых сборок.
    sphinxsearch_2.2.2-4470-2_mipsel.ipk для optware в dd-wrt, платформа mipsel (LSB)
    sphinx_2.2.2-2_ar71xx.ipk под openwrt на NetGear WNDR4300, платформа mips (MSB).
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 11

      +2
      Конечно, впечетляет проделанная работа, но блин, использовать для этого роутер… Какой-нибудь hp microserver как библиотечный механизм для обслуживания библиотеки, мне кажется подошел бы больше. Или самосборная машина на mini itx плате.
        +5
        А почему бы и нет, покуда он полностью справляется с нагрузкой?
        Тем более, что в противном случае собрать пакет (deb или rpm) под классический x86 — это вообще не вопрос.
        +6
        Вы меня простите.
        Но у меня есть один вопрос, почему вы работаете еще в библиотеке?
          +5
          Не только там. Но для удалёнщика это идеальный вариант для коворкинга: тихо, практически не отвлекают.
            0
            Теперь все стало понятно.
          +1
          Здорово!
          Скажите пожалуйста, а не встречалось ли Вам свободного ПО для библиотек? У меня в доме «подшефная» библиотека, сейчас уже около 15 тысяч книг, так они там даже без штрих-кодов. У меня руки чешутся их автоматизировать, но с нуля все писать — страшновато.

            0
            Нет. Я смотрел на несвободное ПО (покуда есть бюджет) — но там тоже не те масштабы. Либо что-нибудь слишком глобальное вроде «ирбис» (считай — из пушки по воробьям). Либо домашние поделки, которые не бесплатны, но при этом работают как-то совсем уж грустно (книги может и покажут «красиво», но про разные электронные форматы карточек вроде MARC они понятия не имеют)
              0
              Интересный пост. Надо будет попробовать что то собрать на роутере, не думал что там так относительно просто.

              А про библиотеки заинтересовало, нарыл www.rusmarc.ru/spf/ по наводке с MARC, может Kitsok, что подойдет или появятся мысли по этому поводу в разрезе на поддержки RUSMARC
            0
            А вы, автор, маньяк ))
              0
              Не хватает результатов — какой объём текста индексируется, как быстро ищется.
                0
                Индекс вот такой:
                -rw-r--r--    1 root     root        471072 Jan  8 23:28 ltsidx.spa
                -rw-r--r--    1 root     root       2236929 Jan  8 23:28 ltsidx.spd
                -rw-r--r--    1 root     root          4629 Jan  8 23:28 ltsidx.spe
                -rw-r--r--    1 root     root           967 Jan  8 23:28 ltsidx.sph
                -rw-r--r--    1 root     root        809931 Jan  8 23:28 ltsidx.spi
                -rw-r--r--    1 root     root             0 Jan  8 23:28 ltsidx.spk
                -rw-------    1 root     root             0 Jan 10 19:16 ltsidx.spl
                -rw-r--r--    1 root     root             0 Jan  8 23:28 ltsidx.spm
                -rw-r--r--    1 root     root        136454 Jan  8 23:28 ltsidx.spp
                -rw-r--r--    1 root     root         68326 Jan  8 23:28 ltsidx.sps
                

                Проиндексировано порядка 15 тыс. документов. «Холодный» select на пару ключевиков (экспандятся в 6 слов, применяется лемматизатор, в индексе на них итого порядка 700 хитов) выполняется за 0,4 секунды. «Горячий» — в 20 раз быстрее (т.е. 0.02).

                Only users with full accounts can post comments. Log in, please.