<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Erinome Lane &#187; linux</title>
	<atom:link href="https://tt.erinome.net/tag/linux/feed" rel="self" type="application/rss+xml" />
	<link>https://tt.erinome.net</link>
	<description>a bit of this, a bit of that...</description>
	<lastBuildDate>Mon, 23 Mar 2026 12:51:51 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.38</generator>
	<item>
		<title>qemu и эмуляция ARM</title>
		<link>https://tt.erinome.net/2020/02/912</link>
		<comments>https://tt.erinome.net/2020/02/912#comments</comments>
		<pubDate>Tue, 25 Feb 2020 07:52:13 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Софт]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[грабли]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=912</guid>
		<description><![CDATA[QEMU позволяет запускать программы, собранные для архитектур, отличающихся от архитектуры хост-машины, как известно. Чуть менее известно то, что с его помощью можно запустить эмулятор всей ЭВМ целиком и установить в него целую операционную систему иной архитектуры. И если с запуском &#8230; <a href="https://tt.erinome.net/2020/02/912">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>QEMU позволяет запускать программы, собранные для архитектур, отличающихся от архитектуры хост-машины, как известно. Чуть менее известно то, что с его помощью можно запустить эмулятор всей ЭВМ целиком и установить в него целую операционную систему иной архитектуры. И если с запуском x86_64 никаких проблем не возникнет (а на обычных PC по факту это просто виртуализация, а не эмуляция), то при попытке запустить эмулятор для ARM может обнаружиться, что систему туда так просто не установить: у эмулятора ARM нет ничего похожего на BIOS, который любезно организует старт установки с виртуального дисковода или флешки, а после установки &#8211; будет передавать загрузку в grub или его аналоги.<span id="more-912"></span></p>
<p>Попробуем организовать эмулятор для ARMv7 при помощи QEMU 4.20 и установим какой-нибудь Linux.</p>
<p>Для начала нам понадобится скачать так называемый netboot образ системы для armhf, состоящий из двух компонент: vmlinuz и initrd.gz. После ряда экспериментов выяснилось, что современные инсталляторы Ubuntu по умолчанию непригодны для этих целей, так что возьмем Debian Buster<a href="http://ftp.debian.org/debian/dists/buster/main/installer-armhf/current/images/netboot/" target="_blank" rel="noopener">[1]</a>.</p>
<p>Подготовим файл образа &#8220;диска&#8221;, в который будем устанавливать систему:</p>
<pre class="console">qemu-img create -f qcow2 debian.qcow2 8G</pre>
<p>В ряде инструкций рекомендуется подготовить сеть, подняв на хост-машине tap-интерфейс, что позволит иметь возможность достучаться с хост-машины в гостевую, но для упрощения ситуации запустим сеть в usermode &#8211; она работает достаточно быстро и для ряда задач её хватит.</p>
<p>Собираем файлы в одном месте и запускаем эмулятор со следующими параметрами:</p>
<pre class="console">qemu-system-arm -smp 4 -m 1024 -M virt \
   -kernel vmlinuz \
   -initrd initrd.gz \
   -append "root=/dev/ram" \
   -netdev user,id=n1
   -device virtio-net-device,netdev=n1 \
   -drive if=none,file=debian.img,format=qcow2,id=hd \
   -device virtio-blk-device,drive=hd \
   -no-reboot</pre>
<p>Параметры:</p>
<ul>
<li>smp &#8211; количество ядер;</li>
<li>m &#8211; объем памяти;</li>
<li>M &#8211; тип машины, virt &#8211; абстрактно-виртуальный ARM;</li>
<li>netdev &#8211; тип сети, user &#8211; самый простой вариант, не требующий настройки хост-машины;</li>
<li>device virtio-net-device &#8211; устройство &#8220;сетевой карты&#8221;, и выбирать следует именно это, а не упомянутое в различных инструкциях, т.к. иначе с огромной вероятностью вы наткнетесь на сообщение о том, что инсталлятор системы не обнаружил никаких сетевых устройств в системе;</li>
<li>drive if=none,file=debian.img,format=qcow2 &#8211; образ жесткого диска;</li>
<li>device virtio-blk-device &#8211; устройство &#8220;жесткого диска&#8221;, аналогично сети, этот вариант обнаруживается установщиком, многие другие &#8211; нет;</li>
<li>no-reboot &#8211; перезапуск системы приведет к остановке эмулятора.</li>
</ul>
<p>Если запускаем qemu-system-arm в консоли без графического интерфейса, то дописываем &#8220;-nographic&#8221;.</p>
<p>Устанавливаем Debian как обычно. На количестве ядер не следует экономить, т.к. в процессе установки есть этапы, которые, по всей видимости, могут неплохо параллелиться, а однопоточная производительность эмулятора далека от производительности хост-машины: в среднем на установку системы уходило около часа на четырехъядерном Intel Xeon 3.5 GHz.</p>
<p>Ближе к концу установки инсталлятор ругнется на невозможность установки grub &#8211; так и должно быть в этом эмуляторе.</p>
<p>Далее, когда инсталлятор сообщит о завершении, выберите &#8220;Go back&#8221; и из главного меню установщика выберите пункт про вход в консоль. В консоли целевой диск, куда производилась установка, будет доступен по пути /target. Поскольку у эмулятора нет загрузчика, нам понадобится вытащить уже из установленной системы соответствующие ей vmlinuz и initrd.img. Для этого можно сделать <em>chroot /target</em> и, используя набор стандартных утилит &#8211; например, scp &#8211; откопировать файлы, находящиеся в /target/boot (а после chroot так просто в /boot) куда-нибудь вовне эмулятора. Если установленных утилит не хватает, то уже в chroot-е можно легко использовать apt и apt-get для установки недостающего. После копирования выходим из консоли и в меню инсталлятора выбираем пункт завершения установки. Когда установщик выполнит перезагрузку, эмулятор просто выключится согласно опции no-reboot.</p>
<p>Конечно, в качестве альтернативы можно вынуть vmlinuz и initrd из установленной системы и при помощи средств конвертации и распаковки qcow2 файлов &#8211; таких инструкций в интернете уйма. В этом случае прежде, чем что-то сделать с образом диска, не забывайте остановить эмулятор, чтобы не повредить образ.</p>
<p>Причина же, по которой изначально не удалось использовать современные образы Ubuntu, состоит в том, что в них после установки по малопонятной причине не генерируется файл initrd.img &#8211; в директории /target/boot есть битые симлинки на него, тогда как сам initrd отсутствует. [<strong>Update</strong>: Способ исправления был найден в комментариях на gist.github.com<a href="https://gist.github.com/takeshixx/686a4b5e057deff7892913bf69bcb85a#gistcomment-2876366" rel="noopener" target="_blank">[2]</a>]</p>
<p>Итак, после описанных действий у вас должен оказаться на руках образ debian.img с установленной ОС и файлы vmlinuz и initrd.img, взятые из этого же образа. Старые vmlinuz и initrd.gz с netboot-инсталлятором можно удалить &#8211; они больше не понадобятся. Теперь для запуска системы нужно переписать аргументы qemu-system-arm на следующие:</p>
<pre class="console">qemu-system-arm -smp 4 -m 1024 -M virt \
   -kernel vmlinuz \
   -initrd initrd.img \
   -append "root=/dev/vda2" \
   -netdev user,id=n1
   -device virtio-net-device,netdev=n1 \
   -drive if=none,file=debian.img,format=qcow2,id=hd \
   -device virtio-blk-device,drive=hd \
   -no-reboot</pre>
<p>Вот, в общем-то, и всё.</p>
<p>Для того, чтобы зайти извне в такую гостевую машину по SSH, можно заменить <em>-netdev user,id=n1</em> на <em>-netdev user,hostfwd=tcp::10022-:22,id=n1</em>, и тогда при подключении к порту 10022 на хост-машине соединение будет перенаправлено на 22 порт гостевой машины. Либо воспользоваться ssh-туннелем, подняв из гостевой машины подключение к удаленному ssh-серверу с дополнительным параметром <em>-R 10022:localhost:22</em> &#8211; в этом случае порт 10022 на удаленном сервере будет перенаправлен на порт 22 гостевой машины. </p>
<p>Теперь можно пробовать собирать программы нативным компилятором нужной архитектуры, а не кросс-компиляцией. Ну или зачем там вы ещё запускали этот эмулятор.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2020/02/912/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bitrix, opcache и утечки памяти</title>
		<link>https://tt.erinome.net/2019/09/904</link>
		<comments>https://tt.erinome.net/2019/09/904#comments</comments>
		<pubDate>Sun, 22 Sep 2019 16:23:07 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Софт]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[грабли]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=904</guid>
		<description><![CDATA[Появилась задача разобраться, отчего некий самописный компонент битрикса, где в цикле выполняется охренеллиард однотипных запросов к базе данных, после обновления PHP с 7.1 на 7.2 внезапно начал падать то в пустой экран, то в классический Fatal error: Allowed memory size &#8230; <a href="https://tt.erinome.net/2019/09/904">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Появилась задача разобраться, отчего некий самописный компонент битрикса, где в цикле выполняется охренеллиард однотипных запросов к базе данных, после обновления PHP с 7.1 на 7.2 внезапно начал падать то в пустой экран, то в классический <em>Fatal error: Allowed memory size of &#8230; bytes exhausted</em>.<span id="more-904"></span></p>
<p>После изучения означенного вопроса оказалось, что суть проблемы сводится к битриксовому API CIBlockElement::GetList() и соответствующему ему GetNext(), которые при <em>некоторых</em> условиях переставали высвобождать память после <em>каждого</em> своего вызова. Очевидно, что после попытки выполнения их по нескольку десятков тысяч раз внутри одного скрипта (который по расписанию перегенерирует карту сайта), разрешенный объем памяти в PHP довольно быстро заканчивался.</p>
<p>Тот факт, что ранее тот же самый код работал без подобных проблем, со временем вывел расследование на opcache, а конкретно &#8211; к оптимизации <em>ZEND_OPTIMIZER_PASS_6 (1&lt;&lt;5) /* DFA based optimization */</em>. По счастью, настройки opcache позволяют выбирать активные оптимизации и отключать те, что могут вызывать проблемы. Соответственно, в конфигурацию была добавлена следующая опция, отключающая только проблемную оптимизацию и оставляющая все остальные:</p>
<pre class="console">opcache.optimization_level=0x7FFEBFDF</pre>
<p>Понятно, что это могло привести к какой-то деградации производительности в opcache, но при выполнении означенных ранее циклов полностью перестала утекать память.</p>
<p>Для справки, полный список опциональных оптимизаций:</p>
<pre class="console">#define ZEND_OPTIMIZER_PASS_1		(1&lt;&lt;0)   /* CSE, STRING construction     */
#define ZEND_OPTIMIZER_PASS_2		(1&lt;&lt;1)   /* Constant conversion and jumps */
#define ZEND_OPTIMIZER_PASS_3		(1&lt;&lt;2)   /* ++, +=, series of jumps      */
#define ZEND_OPTIMIZER_PASS_4		(1&lt;&lt;3)   /* INIT_FCALL_BY_NAME -&gt; DO_FCALL */
#define ZEND_OPTIMIZER_PASS_5		(1&lt;&lt;4)   /* CFG based optimization       */
#define ZEND_OPTIMIZER_PASS_6		(1&lt;&lt;5)   /* DFA based optimization       */
#define ZEND_OPTIMIZER_PASS_7		(1&lt;&lt;6)   /* CALL GRAPH optimization      */
#define ZEND_OPTIMIZER_PASS_8		(1&lt;&lt;7)   /* SCCP (constant propagation)  */
#define ZEND_OPTIMIZER_PASS_9		(1&lt;&lt;8)   /* TMP VAR usage                */
#define ZEND_OPTIMIZER_PASS_10		(1&lt;&lt;9)   /* NOP removal                 */
#define ZEND_OPTIMIZER_PASS_11		(1&lt;&lt;10)  /* Merge equal constants       */
#define ZEND_OPTIMIZER_PASS_12		(1&lt;&lt;11)  /* Adjust used stack           */
#define ZEND_OPTIMIZER_PASS_13		(1&lt;&lt;12)  /* Remove unused variables     */
#define ZEND_OPTIMIZER_PASS_14		(1&lt;&lt;13)  /* DCE (dead code elimination) */
#define ZEND_OPTIMIZER_PASS_15		(1&lt;&lt;14)  /* (unsafe) Collect constants */
#define ZEND_OPTIMIZER_PASS_16		(1&lt;&lt;15)  /* Inline functions */

#define ZEND_OPTIMIZER_IGNORE_OVERLOADING	(1&lt;&lt;16)  /* (unsafe) Ignore possibility of operator overloading */</pre>
<p>PS: Всё это не было бы особо мозголомно, если бы не тот факт, что вся эта проблема наблюдалась на одном сайте и не наблюдалась на другом &#8211; в разных пулах PHP-FPM в рамках одного и того же сервера. Объяснение тому оказалось прямолинейным: там, где проблема не наблюдалась, opcache отчего-то не функционировал. Нет, не был выключен в настройках &#8211; он был включен, но просто перестал работать. После перезапуска FPM проблема начала одинаково воспроизводиться на обоих сайтах. Впоследствии, к слову, это же вынудило переустановить PHP из репозитория Remi, раз в официальном и версия PHP получала лишь обновления безопасности к старому релизу, а не обновления к более новым, и происходили подобные вещи. К сожалению, начальную проблему с DFA это обновление не исправило.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2019/09/904/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>FTP, passive mode и RHEL8</title>
		<link>https://tt.erinome.net/2019/08/901</link>
		<comments>https://tt.erinome.net/2019/08/901#comments</comments>
		<pubDate>Thu, 15 Aug 2019 08:36:36 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Софт]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[грабли]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=901</guid>
		<description><![CDATA[Мамонтовый протокол FTP предусматривает два режима работы для передачи данных &#8211; так называемые активный и пассивный режимы. Для активного режима требуется, чтобы у клиента был публичный IP-адрес и открытые порты, для пассивного &#8211; аналогичное должно быть сделано со стороны сервера. &#8230; <a href="https://tt.erinome.net/2019/08/901">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Мамонтовый протокол FTP предусматривает два режима работы для передачи данных &#8211; так называемые активный и пассивный режимы. Для активного режима требуется, чтобы у клиента был публичный IP-адрес и открытые порты, для пассивного &#8211; аналогичное должно быть сделано со стороны сервера. Удивительно то, что большинство современных инструкций по настройке vsftpd или proftpd предлагают просто выделить конкретный диапазон портов, прописать их в конфигурации FTP-сервера и разрешить к ним доступ в файрволе. Но зачем это делать, когда долгие годы существуют изощренные системы полностью автоматического разрешения доступа к портам при работе с FTP? А потому что по умолчанию они не работают.<span id="more-901"></span></p>
<p>Наглядный пример &#8211; RHEL8. Для чистого iptables и аналогов есть модули nf_conntrack_helper и, в частности, nf_conntrack_ftp. Интернет подсказывает, что в ядрах версии 4.7 и выше они по умолчанию отключены и требуют или ручного включения, или отдельной маркировки соединений. Но вот только у RHEL8 предлагается использовать надстройку FirewallD, мануал которого ещё в 2016 году <a href="https://firewalld.org/2016/10/automatic-helper-assignment" rel="noopener" target="_blank">упоминал автоматическое использование</a> соответствующего helper-а, если в конфигурации файрвола активирован стандартный сервис ftp, открывающий доступ к 21-му порту. Так, утверждается, что в выбранном по умолчанию режиме <em>AutomaticHelpers = system</em> должно проверяться значение <em>/proc/sys/net/netfilter/nf_conntrack_helper</em> (которое в >4.7 выключено) и, в зависимости от него, будут предприниматься необходимые меры для автоматического открывания портов для передачи данных через FTP. Но проблема в том, что этого не происходит.</p>
<p>Практический же опыт показывает, что помимо активации в файрволе сервиса ftp всё же необходимо в firewalld.conf дописать <em>AutomaticHelpers = on</em>, после чего никаких отдельных настроек портов для passive mode FTP не понадобится. Отчего это не работает в режиме <em>system</em> в релизной версии RHEL8 &#8211; неизвестно.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2019/08/901/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Статическая сборка pdns_server</title>
		<link>https://tt.erinome.net/2019/08/898</link>
		<comments>https://tt.erinome.net/2019/08/898#comments</comments>
		<pubDate>Sat, 10 Aug 2019 20:11:47 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Софт]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[грабли]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=898</guid>
		<description><![CDATA[При ручной сборке в ./configure у PowerDNS Authoritative Server есть ключи &#8211;enable-static и &#8211;enable-static-boost, позволяющие собрать бинарники статически, поскольку не всегда есть время и возможности угадывать, что и, главное, каких именно версий окажется установлено или доступно к установке на целевой &#8230; <a href="https://tt.erinome.net/2019/08/898">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>При ручной сборке в <em>./configure</em> у <strong>PowerDNS Authoritative Server</strong> есть ключи <em>&#8211;enable-static</em> и <em>&#8211;enable-static-boost</em>, позволяющие собрать бинарники статически, поскольку не всегда есть время и возможности угадывать, что и, главное, каких именно версий окажется установлено или доступно к установке на целевой операционке. Если вам не повезло иметь операционку, где готового пакета с pdns не существует, а готовый к сборке чего попало сервер уже налажен.</p>
<p>Вот только при запуске успешно собранного бинарника можно увидеть ошибку <em>pdns_server: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.26&#8242; not found (required by pdns_server)</em><span id="more-898"></span></p>
<p>Она говорит о том, что указанная библиотека ни черта не прилинковалась статически.</p>
<p>Заставить ее это сделать можно дописыванием <em>LDFLAGS</em> перед запуском <em>./configure</em>:</p>
<pre class="console">LDFLAGS="-static-libstdc++" ./configure --with-modules="gmysql" --without-lua --enable-static --enable-static-boost --prefix=/</pre>
<p>Это отвяжет зависимость от версии <em>libstdc++</em> на целевой машине.</p>
<p>Говорят, для другого софта может пригодиться вбивание <em>-static-libgcc</em>, если он собирается CC, а общий <em>-static</em> попытается собрать всё остальное статически.</p>
<p>А так-то понятно, что лучше не собирать на коленке вручную, а использовать готовые пакеты требуемого софта для имеющейся операционной системы. Когда они есть в природе. Правда ведь, RHEL8?</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2019/08/898/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Рандомизация фраз на Roborock S50/S55</title>
		<link>https://tt.erinome.net/2019/05/885</link>
		<comments>https://tt.erinome.net/2019/05/885#comments</comments>
		<pubDate>Wed, 15 May 2019 16:12:00 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Софт]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mobile-tech]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=885</guid>
		<description><![CDATA[Roborock S5 &#8211; серия недорогих роботов-пылесосов с лидаром, входящая в категорию товаров для &#8220;умного дома&#8221; от Xiaomi. Дистанционное управление со смартфона и root-доступ ко встроенной операционке на базе Linux прилагаются. Наличие root-доступа предоставляет возможность немного поиграться с системой. Например, по &#8230; <a href="https://tt.erinome.net/2019/05/885">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Roborock S5 &#8211; серия недорогих роботов-пылесосов с лидаром, входящая в категорию товаров для &#8220;умного дома&#8221; от Xiaomi. Дистанционное управление со смартфона и root-доступ ко встроенной операционке на базе Linux прилагаются.<span id="more-885"></span></p>
<p>Наличие root-доступа предоставляет возможность немного поиграться с системой. Например, по умолчанию на каждое конкретное действие робот отвечает одной единственной фразой. И хорошо, если не по-китайски. Не очень весело, правда?</p>
<p>Попробуем сделать так, чтобы вместо этого робот отвечал случайной репликой из соответствующей категории фраз. Для этих целей будет использоваться самописная пользовательская файловая система fuse, сделанная на базе <a href="https://www.cs.nmsu.edu/~pfeiffer/fuse-tutorial/" target="_blank" rel="noopener">bbfs</a>. Смысл состоит в том, чтобы при обращении к какому-либо файлу со звуком &#8211; например, <em>sound.wav</em> &#8211; модифицированная ФС проверяла наличие рядом файлов с именами <em>sound_1.wav</em>, <em>sound_2.wav</em> и т.д. и случайно выбирала один из них. Все прочие же действия bbfs просто пробрасывает в нижележащую файловую систему без изменений.</p>
<h2>Ручная установка</h2>
<p>Инструкция далее предполагает минимальное умение пользоваться linux-терминалом через putty и (win)scp, а так же уже рутированное ПО на роботе. Если это не для вас &#8211; смело пропускайте этот раздел и переходите ниже.</p>
<ol>
<li>Сначала установим нужную локализацию, на базе которой будет строиться наша &#8211; например, при помощи WIN-MIROBO. В результате на роботе по пути <em>/mnt/data/rockrobo/sounds</em> появится каталог с набором звуковых wav-файлов.</li>
<li>Делаем копию этого каталога в соседний, например: <em>/mnt/data/rockrobo/sounds_multi</em>, не забываем сделать <em>chown</em> на <em>ruby</em> (или другого пользователя, не <em>root</em>) как каталога <em>sounds</em>, так и <em>sounds_multi</em> (для удобной заливки новых wav-файлов через <em>winscp</em>).</li>
<li>Установим поддержку файловых систем <em>fuse</em>, используя стандартные пакеты к <em>Ubuntu 14.04</em> для armhf: понадобятся libfuse2_2.9.2-4ubuntu4.14.04.1_armhf.deb и fuse_2.9.2-4ubuntu4.14.04.1_armhf.deb с <a href="https://launchpad.net/~ubuntu-security/+archive/ubuntu/ppa/+build/7431580" target="_blank" rel="noopener">launchpad.net</a>. Для установки забрасываем файлы в робота и используем <em>dpkg -i имя_файла</em>.</li>
<li>Забрасываем в робота собранный бинарник файловой системы <a href="https://fs.erinome.net/roborock/" target="_blank" rel="noopener">bbfs</a> и кладем его, к примеру, в <em>/usr/local/bin</em>, делаем его запускаемым (<em>chmod +x</em>)</li>
<li>В файле <em>/etc/fuse.conf</em> раскомментируем опцию <em>user_allow_other</em>, т.к. в противном случае из-под root-а не будут видны файлы в смонтированных пользователем каталогах, делаем этот файл читаемым для всех (<em>chmod +r</em>).</li>
</ol>
<p>Теперь мы готовы запустить <em>bbfs</em>. Для этого заходим из-под <em>ruby</em> (или другого пользователя, главное &#8211; не <em>root</em>) и набираем следующую команду: <code>bbfs -o nonempty,allow_root /mnt/data/rockrobo/sounds_multi /mnt/data/rockrobo/sounds</code>. После этого в каталоге <em>sounds</em> будут показаны все файлы из <em>sounds_multi</em>, но при выборе основного файла <em>sounds/sound.wav</em> в действительности будет запрошен случайным образом либо <em>sounds_multi/sound_N.wav</em>, либо исходный <em>sounds_multi/sound.wav</em>.</p>
<p>Отметим, что N ∈ [1;20] &#8211; и только в порядке возрастания (т.о. если файла <em>sound_1.wav</em> нет, то <em>sound_2.wav</em> даже проверяться не будет).</p>
<p>Для размонтирования обратно используем команду <code>fusermount -u /mnt/data/rockrobo/sounds</code>, и в <em>/mnt/data/rockrobo/sounds</em> возвращается его исходное содержимое.</p>
<p>Если вручную все завелось корректно, то для автоматического старта при запуске робота можно добавить в <em>/etc/rc.local</em> следующую строку: <code>su ruby -c "bbfs -o nonempty,allow_root /mnt/data/rockrobo/sounds_multi /mnt/data/rockrobo/sounds"</code>. Не забываем указывать правильного пользователя после <em>su</em> и не теряем кавычки.</p>
<h2>Предсобранные прошивки</h2>
<p>На сайте <a href="https://github.com/rand256/valetudo/releases" target="_blank" rel="noopener">github.com/rand256</a> есть предсобранные прошивки с рут-доступом, Valetudo RE и предустановленной в том числе и системой рандомизации фраз. После установки такой прошивки необходимо только создать на роботе директории /mnt/data/rockrobo/sounds и /mnt/data/rockrobo/sounds_multi и положить в последнюю набор файлов озвучки, включающих разные перефразированные реплики под каждое событие: например, start.wav, start_1.wav, start_2.wav.</p>
<p>Таким образом, после перезагрузки робот станет использовать для одного и того же события разнообразные слова вместо постоянного повторения единственной фразы &#8211; и это звучит намного живее и естественнее.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2019/05/885/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>linux: несколько IP на один NIC</title>
		<link>https://tt.erinome.net/2019/04/879</link>
		<comments>https://tt.erinome.net/2019/04/879#comments</comments>
		<pubDate>Mon, 15 Apr 2019 13:18:47 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Сеть и интернет]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=879</guid>
		<description><![CDATA[Заметка, чтобы не забыть. Варварство без специфичных скриптов того или иного дистрибутива, с циничным пользованием deprecated утилит. Заведение IP: ifconfig eth0:1 &#60;ip-address&#62; netmask &#60;netmask&#62; up Если это роутер с NAT или просто нужна возможность организовать внешние соединения с этого IP &#8230; <a href="https://tt.erinome.net/2019/04/879">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Заметка, чтобы не забыть. Варварство без специфичных скриптов того или иного дистрибутива, с циничным пользованием deprecated утилит.<span id="more-879"></span></p>
<p>Заведение IP:</p>
<pre class="console">ifconfig eth0:1 &lt;ip-address&gt; netmask &lt;netmask&gt; up</pre>
<p>Если это роутер с NAT или просто нужна возможность организовать внешние соединения <em>с</em> этого IP (а не <em>на</em> него), то требуется новая таблица в <em>/etc/iproute2/rt_tables</em>. Далее:</p>
<pre class="console">ip route add &lt;network&gt;/29 dev eth0:1 src &lt;ip-address&gt; table &lt;table&gt;
ip route add default via &lt;network gateway&gt; table &lt;table&gt;
ip route add 192.168.0.0/24 dev eth1 table &lt;table&gt;
ip rule add from &lt;network&gt; table &lt;table&gt;</pre>
<p>Посмотреть, что получилось:</p>
<pre class="console">ip rule show
ip route list table &lt;table&gt;</pre>
<p>Задать маршруты по умолчанию тем же <code>ip route</code>, но без <code>table</code>. Правила iptables для NAT типовые.</p>
<p>Если удобоваримый результат достигнут, далее можно искать специфичные скрипты того или иного дистрибутива для менее варварской настройки. Если их нет, то rc.local.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2019/04/879/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PPTP-клиент за NAT на основе Linux-сервера</title>
		<link>https://tt.erinome.net/2015/10/839</link>
		<comments>https://tt.erinome.net/2015/10/839#comments</comments>
		<pubDate>Thu, 29 Oct 2015 09:49:04 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Сеть и интернет]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[грабли]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=839</guid>
		<description><![CDATA[Небольшая локальная сеть на несколько компьютеров получала доступ в интернет через сервер на базе CentOS 6 &#8211; обычным NAT маскарадингом через iptables. Обнаружилось, что в такой конфигурации по умолчанию невозможно поднять VPN-туннель PPTP (протокол &#8211; GRE) с компьютеров, находящихся за &#8230; <a href="https://tt.erinome.net/2015/10/839">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Небольшая локальная сеть на несколько компьютеров получала доступ в интернет через сервер на базе CentOS 6 &#8211; обычным NAT маскарадингом через iptables. Обнаружилось, что в такой конфигурации по умолчанию невозможно поднять VPN-туннель PPTP (протокол &#8211; GRE) с компьютеров, находящихся за таким шлюзом.<span id="more-839"></span></p>
<p>Симптомы: на виндовых машинах подключение к VPN виснет на &#8220;проверке имени пользователя и пароля&#8221;, затем выдается ошибка 619. В логах PPTP-сервера видно, что он не получает отклика от клиента и сбрасывает подключение по таймауту, а в трафике видны только исходящие GRE-пакеты в сторону PPTP-клиента. На NAT-шлюзе в трафике при этом присутствуют и входящие, и исходящие GRE-запросы.</p>
<p>Спустя время было выявлено, что затык именно на стороне клиента, а именно на NAT-шлюзе: для работы PPTP на нем нужно подгрузить следующие модули (для CentOS):</p>
<pre class="console">modprobe ip_nat_pptp
modprobe ip_conntrack_pptp
modprobe ip_gre</pre>
<p>Несмотря на то, что, казалось бы, каких-либо открываний портов, или разрешений протоколов в файрволе, или других настроек на NAT-шлюзе для использования на стоящих за ним компьютерах PPTP-клиента не требуется, все-таки кое-что сделать придется. Так как с настройками по умолчанию (по крайней мере, для CentOS 6) GRE-трафик пропускаться не будет.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2015/10/839/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>icecast relay и редиректы</title>
		<link>https://tt.erinome.net/2015/09/830</link>
		<comments>https://tt.erinome.net/2015/09/830#comments</comments>
		<pubDate>Fri, 25 Sep 2015 08:24:29 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Сеть и интернет]]></category>
		<category><![CDATA[c/c++]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[грабли]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=830</guid>
		<description><![CDATA[Сервер icecast может использоваться не только для организации вещания новых сетевых медиапотоков, но и для ретрансляции уже существующих. Источник исходного потока при этом может являться распределенной структурой других icecast-подобных серверов, в которых для распределения нагрузки на головном сервере может использоваться &#8230; <a href="https://tt.erinome.net/2015/09/830">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Сервер icecast может использоваться не только для организации вещания новых сетевых медиапотоков, но и для ретрансляции уже существующих. Источник исходного потока при этом может являться распределенной структурой других icecast-подобных серверов, в которых для распределения нагрузки на головном сервере может использоваться простой http-редирект (код 302) на дополнительные узлы.</p>
<p>Хотя relay-функциональность icecast и позволяет разбирать этот код и реагировать на него соответствующим образом, работает это не всегда.<span id="more-830"></span></p>
<p>Можно столкнуться со следующей ситуацией: головной source-сервер перегружен и выдает редиректы на дополнительные серверы, при проверке браузером все корректно, но наш icecast-ретранслятор их будто &#8220;не замечает&#8221; и выдает клиентам ошибку 404. В логах нашего icecast&#8217;а при этом можно увидеть следующие записи:</p>
<pre class="console">[2015-09-25  10:58:40] INFO slave/start_relay_stream Starting relayed source at mountpoint "/&lt;mountname>"
[2015-09-25  10:58:40] INFO slave/open_relay_connection connecting to &lt;main.source>:80
[2015-09-25  10:58:40] INFO slave/open_relay_connection redirect received HTTP://&lt;fallback.source>
</pre>
<p>После этого клиенту выдается ошибка 404 и более ничего содержательного в лог не попадает. Так в чем же дело?</p>
<p>В исходном коде icecast можно найти следующие строки:</p>
<pre class="console">            ICECAST_LOG_INFO("redirect received %s", uri);
            if (strncmp (uri, "http://", 7) != 0)
                break;
</pre>
<p>Так выясняется, что если полученная ссылка из 302-го редиректа не начинается на &#8220;http://&#8221; (в НИЖНЕМ регистре!), то наш icecast с ней не будет делать вообще ничего и сделает вид, что никакого редиректа не было. А как видно из лога, наш источник рассылает редиректы, начинающиеся на HTTP:// в верхнем регистре.</p>
<p>Очевидно, надо научить icecast не обращать внимание на разницу регистров в протоколе. Это можно сделать разными способами, оптимальность и красота которых зависит от качества вашего владения <del>кунг-фу</del> Си. Можно, например, сделать как-то так:</p>
<pre class="console">--- icecast-2.4.2/src/slave.c   2015-04-08 11:06:13.000000000 +0300
+++ icecast-2.4.2-mod/src/slave.c       2015-09-25 10:55:29.495738535 +0300
@@ -238,13 +238,19 @@
         if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0)
         {
             /* better retry the connection again but with different details */
-            const char *uri, *mountpoint;
-            int len;
+            const char *uri, *mountpoint, *proc = "http://";
+            int i, len;

             uri = httpp_getvar (parser, "location");
+
             ICECAST_LOG_INFO("redirect received %s", uri);
-            if (strncmp (uri, "http://", 7) != 0)
-                break;
+            for (i = 0; i < 7 &#038;&#038; uri[i]; i++) {
+                if ((tolower(uri[i]) - tolower(proc[i])) != 0) {
+                    i = 10;
+                    break;
+                }
+            }
+            if (i == 10) break;
             uri += 7;
             mountpoint = strchr (uri, '/');
             free (mount);
</pre>
<p>Пересобираем, подключаемся, смотрим лог:</p>
<pre class="console">[2015-09-25  11:08:40] INFO slave/start_relay_stream Starting relayed source at mountpoint "/&lt;mountname>"
[2015-09-25  11:08:40] INFO slave/open_relay_connection connecting to &lt;main.source>:80
[2015-09-25  11:08:40] INFO slave/open_relay_connection redirect received HTTP://&lt;fallback.source>
[2015-09-25  11:08:40] INFO slave/open_relay_connection connecting to &lt;fallback.source>:80
[2015-09-25  11:08:40] WARN format/format_get_type Unsupported or legacy stream type: "audio/mpeg". Falling back to generic minimal handler for best effort.
[2015-09-25  11:08:40] INFO source/source_main listener count on /&lt;mountname> now 1</pre>
<p>И все работает.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2015/09/830/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Резервное копирование в WebDav на OpenWrt</title>
		<link>https://tt.erinome.net/2014/12/742</link>
		<comments>https://tt.erinome.net/2014/12/742#comments</comments>
		<pubDate>Mon, 22 Dec 2014 13:18:13 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Сеть и интернет]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[openwrt]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=742</guid>
		<description><![CDATA[Рассмотрим возможность создания резервного копирования в облачное хранилище WebDav на примере Яндекс.Диска при помощи виртуальной файловой системы davfs2, утилиты cadaver и небольшого PHP-скрипта. Может возникнуть закономерный вопрос: зачем изобретать велосипед и почему бы не использовать обычный rsync для копирования данных &#8230; <a href="https://tt.erinome.net/2014/12/742">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Рассмотрим возможность создания резервного копирования в облачное хранилище <strong>WebDav</strong> на примере Яндекс.Диска при помощи виртуальной файловой системы <strong>davfs2</strong>, утилиты <strong>cadaver</strong> и небольшого <strong>PHP</strong>-скрипта.<span id="more-742"></span></p>
<p>Может возникнуть закономерный вопрос: зачем изобретать велосипед и почему бы не использовать обычный <strong>rsync</strong> для копирования данных напрямую в <strong>davfs2</strong>? Ответ простой: это&#8230; не работает как надо. Во всяком случае, с Яндекс.Диском, и если запускать процедуру на роутере под управлением <strong>OpenWrt</strong>.</p>
<p>То ли из-за особой неторопливости Яндекс.Диска, то ли из-за особенностей <strong>davfs2</strong>, но при попытке копирования большого количества файлов (а иногда такое случается и с единичными файлами!), большая их часть оказывалась в каталоге <em>lost+found</em>, служащем для локальных копий файлов, загрузка которых в <strong>WebDav</strong> провалилась. И всё это несмотря на достаточный объем выделенного кэша, стандартные настройки <strong>davfs2</strong> для повторной загрузки файлов в случае ее неудачи, и довольно широкого канала связи.</p>
<p>Итак, для самописного и более надежного бэкапера в WebDav нам понадобятся:</p>
<ol>
<li><strong>davfs2</strong> &#8211; стандартный пакет из репозитория OpenWrt;</li>
<li><strong>cadaver</strong> &#8211; модифицированная версия утилиты для прямого копирования файлов в WebDav <a href="http://fs.erinome.net/openwrt/packages/cadaver" target="_blank">из нашего архива</a>;</li>
<li><strong>php-cli</strong> &#8211; стандартный интерпретатор PHP, при помощи которого мы сгенерируем набор команд для cadaver&#8217;а.</li>
</ol>
<p>Добавленные в <strong>cadaver </strong>патчи позволяют при помощи нового ключа <strong>-F</strong> пропускать проверку подлинности сертификатов (а под <strong>OpenWrt </strong>у нас все используемые в WebDav SSL-сертификаты считаются подозрительными и требуют ручного подтверждения), а также указывать с помощью ключа <strong>-n</strong> нестандартный путь к хранилищу паролей для автоматической авторизации на WebDav-сервере. Внесенные изменения позволяют создать для <strong>cadaver</strong> файл с набором команд, которые будут выполняться автоматически без участия человека.</p>
<p>Теперь обратим внимание на скрипт <a href="http://fs.erinome.net/openwrt/scripts/phpsync.php" target="_blank">phpsync.php</a>.</p>
<p>У этого скрипта две задачи:<br />
Во-первых, он отвечает за &#8220;обратную синхронизацию&#8221; &#8211; в скрипте производится проверка, все ли находящиеся в WebDav-хранилище файлы продолжают находиться на локальном диске. В случае обнаружения в удаленном хранилище файлов, которые были локально удалены, скрипт удалит их и из WebDav &#8211; обычным вызовом <em>unlink()</em> на смонтированном разделе davfs2.<br />
Во-вторых, скрипт генерирует набор команд для <strong>cadaver</strong>&#8216;а для загрузки локально измененных или отсутствующих в WebDav файлов в удаленное хранилище.<br />
В скрипте есть ряд параметров, которые нужно настроить. Так, параметром <em>$outfile</em> определяется путь к файлу, в который будет записан сгенерированный набор команд, в параметре <em>$remotebase</em> необходимо указать точку монтирования <strong>davfs2</strong>. Также в нем указывается адрес WebDav сервера, к которому должен подключаться <strong>cadaver</strong> &#8211; по умолчанию это <em>https://webdav.yandex.ru</em>. Затем следует указать каталоги, которые требуется синхронизировать, например:</p>
<pre class="console">phpsync("/srv/content","/backup/content");
phpsync("/srv/otherdir","/backup/otherdir");</pre>
<p>Так будут сгенерирован набор команд для отражения локальных каталогов <em>/srv/content</em> и <em>/srv/otherdir</em> в удаленные WebDav-каталоги <em>/backup/content</em> и <em>/backup/otherdir</em> соответственно. Обратите внимание &#8211; удаленные пути указываются относительно корня <strong>WebDav</strong>-диска, без привязки к локальной точке монтирования <strong>davfs2</strong>!</p>
<p>После этого следует создать парольный файл для <strong>cadaver</strong>. Это обычный текстовый документ, содержающий строку следующего вида:</p>
<pre class="console">machine webdav.yandex.ru login вашлогин password вашпароль</pre>
<p>Наконец, создаем <strong>ash</strong>-скрипт, который мы будем вызывать через кронтаб:</p>
<pre class="console">#!/bin/ash
/usr/bin/php-cgi -f /srv/phpsync.php

if [ -e "/var/.phpsync.list" -a $(wc -l "/var/.phpsync.list" | awk '{print $1;}') -gt 2 ]
then
        /usr/bin/cadaver -F -r "/var/.phpsync.list" -n "/srv/auth.rc"
fi
</pre>
<p>Подставляем ваши значения:</p>
<ul>
<li><strong>/srv/phpsync.php</strong> &#8212; путь к PHP-скрипту;</li>
<li><strong>/srv/auth.rc</strong> &#8212; путь к парольному файлу;</li>
<li><strong>/var/.phpsync.list</strong> &#8212; путь к файлу с набором команд для cadaver.</li>
</ul>
<p>Не забываем сделать скрипт исполняемым, выполнив команду <strong>chmod +x</strong> на данном файле. Теперь мы можем проверить работоспособность копирования, запустив этот скрипт. В терминал при этом будет выведен результат работы PHP-генератора, а также будет показан процесс выполнения задания cadaver&#8217;ом.</p>
<p>Если все завершилось успешно &#8211; теперь можем добавить автоматический вызов данного скрипта через кронтаб в ночное время и более не беспокоиться о создании резервных копий.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2014/12/742/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>PHP, SQLite и регистронезависимость</title>
		<link>https://tt.erinome.net/2014/10/719</link>
		<comments>https://tt.erinome.net/2014/10/719#comments</comments>
		<pubDate>Sat, 18 Oct 2014 10:11:26 +0000</pubDate>
		<dc:creator><![CDATA[root]]></dc:creator>
				<category><![CDATA[Сеть и интернет]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[openwrt]]></category>
		<category><![CDATA[грабли]]></category>

		<guid isPermaLink="false">http://tt.erinome.net/?p=719</guid>
		<description><![CDATA[Несмотря на объявленную поддержку UTF, встраиваемая СУБД SQLite3 по умолчанию не умеет делать регистронезависимую сортировку, сравнение и операции по преобразованию строк над буквами, не входящими в английский алфавит. Для решения этой проблемы разработчики SQLite рекомендуют использовать расширение ICU, с подключением &#8230; <a href="https://tt.erinome.net/2014/10/719">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Несмотря на объявленную поддержку UTF, встраиваемая <a href="http://www.sqlite.org/" target="_blank"> СУБД SQLite3</a> по умолчанию не умеет делать регистронезависимую сортировку, сравнение и операции по преобразованию строк над буквами, не входящими в английский алфавит. Для решения этой проблемы разработчики SQLite рекомендуют использовать расширение <strong>ICU</strong>, с подключением которого станет возможным выбирать различные кодировки и иметь возможность пользоваться регистронезависимыми операциями сравнения любых символов кроме латиницы. Вот только описаний, как это сделать для php-mod-sqlite3, найти толком нельзя, да и объем ICU превышает размер самой библиотеки SQLite! Для embedded-применений &#8211; например, в <strong>OpenWrt</strong> &#8211; такое не годится.<span id="more-719"></span></p>
<p>Давным-давно, в далекой-далекой галактике&#8230; оказалось, что много лет назад некий товарищ по имени <strong>ioannis</strong> в рамках решения означенной проблемы создал небольшое расширение SQLite, по сути представляющее собой урезанную версию ICU, из которой был выброшен код, который в 95% случаев никогда не потребуется. Таким образом удалось сократить размер расширения с мегабайт до символических 70-90 кбайт (в зависимости от архитектуры и компилятора). Копия исходного сообщения осталась доступна на <a href="http://www.mail-archive.com/sqlite-users@sqlite.org/msg30403.html" target="_blank">mail-archive.com</a>, и хотя оригинальная ссылка на исходный код расширения sqlite3_unicode.c неработоспособна, его все еще можно найти <a href="https://www.google.ru/search?q=sqlite3_unicode.c" target="_blank">в интернете</a>, а также мы добавили его в <a href="http://fs.erinome.net/openwrt/packages/libsqlite3_unicode/files/sqlite3_unicode.c">наш архив</a>.</p>
<h3><strong>Возможности расширения</strong></h3>
<p><strong>#define SQLITE3_UNICODE_FOLD</strong> &#8211; поддержка нелатинских символов функцией FOLD() &#8211; +10КБ<br />
<strong>#define SQLITE3_UNICODE_LOWER</strong> &#8211; поддержка нелатинских символов функцией LOWER() &#8211; +10КБ<br />
<strong>#define SQLITE3_UNICODE_UPPER</strong> &#8211; поддержка нелатинских символов функцией UPPER() &#8211; +10КБ<br />
<strong>#define SQLITE3_UNICODE_TITLE</strong> &#8211; поддержка нелатинских символов функцией TITLE() &#8211; +10КБ<br />
<strong>#define SQLITE3_UNICODE_UNACC</strong> &#8211; преобразование accented-символов в обычные, для русского языка не очень актуально &#8211; +30КБ<br />
<strong>#define SQLITE3_UNICODE_COLLATE</strong> &#8211; заменяет COLLATION типа NOCASE на новый, в котором при сравнении не учитывается регистр в т.ч. и для нелатинских символов.<br />
<strong>#define SQLITE3_UNICODE_UNACC_AUTOMATIC</strong> &#8211; попытаться применять UNACC в функции LIKE и операциях сравнения с COLLATE NOCASE.</p>
<p>Что характерно, функциональность, которая не требуется, может быть отключена путем убирания соответствующих #define для экономии места и увеличения производительности (вряд ли, впрочем, существенного).</p>
<p>Теперь разберемся, как собрать это расширение и задействовать его в cli-версии SQLite и в модуле для PHP.</p>
<h3><strong>Сборка под Linux</strong></h3>
<p>Для начала, скачиваем и распаковываем исходный код SQLite &#8211; нам потребуются заголовочные файлы из него. Можно использовать любую версию: я брал amalgamation без autoconf &#8211; она меньше. В тот же каталог копируем sqlite3_unicode.c.</p>
<p>Далее, корректности ради, открываем sqlite3_unicode.c текстовым редактором и вписываем вверху директиву:</p>
<pre class="console">/* we want shared library. only. */
#define SQLITE_ENABLE_UNICODE 1</pre>
<p>Этим мы указываем, что собираемся компилировать shared-библиотеку. С другой стороны &#8211; соответствующий #if в коде расширения написан таким своеобразным образом, что этого можно и не делать.</p>
<p>Затем следует непосредственно процесс компиляции. Предполагается, что пакеты из набора build-essentials уже установлены в системе &#8211; во всяком случае, здесь потребуется gcc:</p>
<pre class="console">gcc -shared -o sqlite3_unicode.so sqlite3_unicode.c</pre>
<p>Закрывая глаза на ряд warning&#8217;ов, получаем готовую библиотеку.</p>
<p>При работе с cli-версией потребуется разместить полученный sqlite3_unicode.so в каталоге с остальными системными библиотеками, либо же выполнить export следующего вида:</p>
<pre class="console">export LD_LIBRARY_PATH="/path/to/library/dir:$LD_LIBRARY_PATH"</pre>
<p>Затем открываем cli и пишем команду <strong>.load sqlite3_unicode</strong>. Если ошибок не возникло &#8211; значит, расширение успешно загрузилось.</p>
<p>Для загрузки расширения в PHP потребуется в php.ini выставить корректный путь к месту расположения библиотеки в перараметре <strong>sqlite3.extension_dir</strong>. Затем в скрипте используем команду вида <strong>$db->loadExtension(&#8216;sqlite3_unicode.so&#8217;)</strong>;</p>
<p>Вот и всё &#8211; теперь сравнения с COLLATE NOCASE будут регистронезависимыми не только для латиницы, но и для русских букв.</p>
<h3><strong>Сборка под OpenWrt</strong></h3>
<p>Теперь попробуем побороть Buildroot от OpenWrt и кросс-компилировать это расширение в toolchain&#8217;е для запуска на роутере. В конечном счете, именно ради этого весь процесс и затевался &#8211; на ПК-то нет проблемы установить ICU, в отличие от встраиваемых систем. Надо признать, что борьба с Buildroot&#8217;ом для меня оказалась намного сложнее, чем все поиски решения проблемы с регистрозависимостью в целом и попытки разобраться, как его собирать в обычных условиях, в частности.</p>
<p>В конечном счете, после бессчетных неудачных попыток, я остановился на варианте формирования отдельного пакета, содержащего только это расширение с выставленным параметром DEPENDS на libsqlite3 и включенным в его состав С-файле с исходным кодом. Скачать готовый пакет можно в <a href="http://fs.erinome.net/openwrt/packages/libsqlite3_unicode" target="_blank">нашем архиве</a>.</p>
<p>Его можно распаковать в каталог &#8220;package&#8221; корневой директории Buildroot&#8217;а, а затем нужно в <strong>make menuconfig</strong> выбрать пакет <strong>libsqlite3_unicode</strong> и запустить его сборку при помощи команды <strong>make package/libsqlite3_unicode/compile</strong>.</p>
<p>После установки полученного ipk на роутер подключение данного расширения производится аналогичным ранее описанному способом. Обратите внимание, что имя расширения для OpenWrt &#8211; <strong>libsqlite3_unicode.so</strong>, его размещение по умолчанию &#8211; <strong>/usr/lib</strong>.</p>
]]></content:encoded>
			<wfw:commentRss>https://tt.erinome.net/2014/10/719/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
