pinguin_packet_dry

Широко известный в узких кругах легковесный менеджер пакетов opkg получил распространение в embedded Linux не случайно. Opkg используется во многих встраиваемых дистрибутивах и проектах, например, в OpenEmbedded, Yocto Project, OpenWRT, Angstrom, Arago Project и некоторых других. Менеджер прост в эксплуатации, для полноценной работы вполне достаточно встроенной справки, а на просторах всемирной паутины множество статей о том, как устроен сам пакет ipk (opkg работает с таким форматом): как его создать, как установить и т.д и т.п. Однако подавляющее большинство информации посвящено тому, как работать на уже установленной на целевую платформу (target) системе в online-режиме, но специфика Embedded подразумевает, что образ корневой файловой системы, а также ядро готовятся заранее на некоторой инструментальной платформе (host), отличной от целевой. Иными словами, собираем ядро и файловую систему на рабочем компьютере, упаковываем в образ, образ тиражируем на железо. Эта статья посвящена тому, как с помощью менеджера opkg установить пакеты в подготавливаемый образ rootfs.


Путь граблей и велосипедов


Много лет назад в бытность инженера одного небольшого завода, когда я запустил Linux на первой платке собственного производства, с помощью opkg установил из удаленного репозитория все требуемые пакеты, настроил все приложения, начальник лаборатории сказал: "Отлично! Теперь сделай то же самое на всех устройствах в партии". "Да не вопрос!" – ответил я. Система же есть, она запущена, она работает. Копируем все файлы из корня на внешний носитель, затем упаковываем в образ и радуемся жизни! В то время я не понимал, что при работе операционная система выполняет ряд локальных настроек, создает временные файлы, файлы конфигурации, генерирует какие-то ключи, а при первом запуске выполняет еще и скрипты инициализации. Хотя перенос файлов с одной работающей системы на другую методом тупого копирования с носителя и давал результат, но эффективность данного метода очень скоро для меня стала сомнительной. Получить "чистую" систему таким образом невозможно: система помнит свою предыдущую жизнь в другом аппаратном теле, и время от времени ее душат фантомные боли.
Еще одна бредовая идея
На target смонтировать внешний накопитель с rootfs, выполнить chroot и ставить пакеты. Комментировать не буду.

Следующим шагом для меня стало понимание структуры самого покета *.ipk. По сути вещей, пакет ipk является архивом, распаковать который можно легко с помощью команды:

ar -x *.ipk

В результате получим:

 .
+-- control.tar.gz
+-- data.tar.gz
L-- debian-binary 

В архиве data.tar.gz содержатся файлы, которые должны быть помещены в корневую директорию target'а.
В архиве control.tar.gz содержатся служебные файлы: файл с описанием и скрипты. Идея простая: так как ipk – это всего лишь архив со скриптами, то мы можем всегда руками распаковать его в директорию с файловой системой, а потом запустить (если есть в этом необходимость) скрипты. Вот только все зависимости пакета нам придется устанавливать также вручную.
А если зависимости имеют еще зависимости? Возникает идея, может быть написать скрипт для автоматизации процесса? Как это часто бывает в мире linux, если перед тобой возникла задача, то, скорее всего, такая задача возникла не перед тобой одним, и, скорее всего, ты в этом деле не первый.
Далеко ходить не пришлось, на самом деле в сам менеджер пакетов opkg заложен такой режим, когда пакеты устанавливаются в неактивную файловую систему rootfs. При этом, архитектура host-машины (где запускаются утилиты opkg) и target-машины могут быть отличными. Такой режим называется Offline mode. В таком режиме opkg становится мощнейшим инструментом кросс-разработки.

Собираем opkg для host


Для работы в режиме Offline opkg должен запускаться на host'е. С давних пор на моем рабочем компьютере обосновалась Ubuntu (сейчас стоит Ubuntu 14.04 LTS), на ней и будем строить наш инструментарий. Мне не удалось найти репозиторий с opkg для Ubuntu, потому собираем набор утилит из исходников.
Получить исходные коды можно с git репозитория Yocto Project:

    git clone git://git.yoctoproject.org/opkg.git
    cd opkg

Для тех кого пугает git
можно обойтись и без него. На момент написания статьи, актуальная версия утилиты – opkg-0.3.1. Качаем исходники с сайта и распаковываем:
tar xzf opkg-0.3.1.tar.gz
cd opkg-0.3.1/

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

./autogen.sh

На заметку: если запустить ./autogen.sh с параметром --clean, то удалятся все труды по конфигурации проекта.
После выполнения ./autogen.sh в директории с исходниками появляется скрипт configure, он выполнит настройку пакета, определит и задаст системозависимые переменные. В результате работы скрипта создается Makefile. Посмотреть все опции скрипта можно стандартным способом:

./configure --help

Собирать пакет будем под текущую платформу, потому опции настройки кросс-компиляции пропускаем. Озаботимся инсталляцией. По умолчанию, выполнив make install, скрипт раскидает все полезные файлы (бинарники, скрипты, документация) по корневой директории: /etc, /usr/local, а это нам совершенно ни к чему. Мы ведь не собираемся использовать opkg для настройки пакетов в текущей системе? Кроме того, установив менеджер в системные папки, для использования утилит потребуются права суперпользователя, на мой взгляд, это излишне при настройке образа embedded linux. Скрипт configure.sh позволяет задать префикс для директории установки пакета. Указав в качестве префикса любую рабочую директорию, мы сообщим инсталлятору куда ставить пакет. При необходимости можно отдельно задать префикс для архитектурозависимых (бинарники и библиотеки) и архитектуронезависимых (скрипты и документация) файлов.
С фантазией у меня всегда было слабовато, потому для инсталляции в домашнем каталоге создадим каталог opkg_offline.

mkdir ${HOME}/opkg_offline

Выполним конфигурацию:

./configure --prefix=${HOME}/opkg_offline

При необходимости доставляем требуемые зависимости. Так мне на Ubuntu 14.04 для успешной сборки понадобилось доставить libarchive-dev, libcurl4-gnutls-dev, libssl-dev, libgpgme11-dev.
А как это сделать?
sudo apt-get install libarchive-dev
sudo apt-get install libcurl4-gnutls-dev
sudo apt-get install libssl-dev
sudo apt-get install libgpgme11-dev


Компилируем и устанавливаем opkg:

make  
make install  

В результате в директории opkg_offline имеем:
opkg_offline
+-- bin
¦   +-- opkg
¦   +-- opkg-check-config
¦   L-- opkg-key
+-- lib
¦   +-- libopkg.a
¦   +-- libopkg.la
¦   +-- libopkg.so -> libopkg.so.1.0.0
¦   +-- libopkg.so.1 -> libopkg.so.1.0.0
¦   +-- libopkg.so.1.0.0
¦   L-- pkgconfig
¦       L-- libopkg.pc
L-- share
+-- man
¦   L-- man1
¦       +-- opkg.1
¦       L-- opkg-key.1
L-- opkg
L-- intercept
+-- depmod
+-- ldconfig
L-- update-modules


Менеджер пакетов собран и установлен. Исполняемые файлы находятся в директории opkg_offline/bin. Для работы с ними можно в переменную PATH прописать путь, либо для каждой сессии терминала вызывать экспорт (export), либо делать как я делаю – перейти в каталог opkg_offline и запустить непосредственно ./bin/opkg.

Краткий курс анатомии


Коротко рассмотрим как работает менеджер пакетов в стандартном режиме. После выполнения команды opkg update, утилита читает файлы конфигураций, которые по умолчанию расположены в /etc/opkg и имеют расширение .conf. Из этих файлов система определяет тип архитектуры, например armv5hf-vfp или armv5tehf-vfp (поддерживаемых архитектур может быть несколько, для каждой можно задать приоритет), список репозиториев и некоторые настройки самой программы. Далее для каждого репозитория из списка скачивается архив типа *_Packages.gz. Архивы по умолчанию помещаются в директорию var/cache/opkg/. После распаковки содержимое помещается в var/lib/opkg/lists. В каждом архиве лежит текстовый файл со списком пакетов в репозитории. Для каждого пакета помимо названия указана версия, архитектура, размер, краткое описание, лицензия, а самое главное – зависимости. На основании этих файлов менеджер пакетов по запросу может выдать информацию о требуемом пакете, а при его установке определить все зависимости и разрешить их.
Команда opkg list выдаст все доступные для установки пакеты; команда opkg list-installed покажет только установленные пакеты, команда opkg info покажет информацию об указаном пакете, а если он установлен, то и время установки.
Для установки пакета следует выполнить opkg install packname. В результате требуемый пакет из репозитория будет скачен во временную директрию и распокован. Все файлы из архива data.tar.gz разойдутся по своим местам в rootfs, а на основании содержимого control.tar.gz в каталоге var/lib/opkg/info будут созданы служебные файлы: packname.control – полная информация о пакете, packname.list — список директорий, по которым разошлись файлы из data.tar.gz (по этому списку пройдется opkg при удалении пакета), и файлы скриптов, типа packname.postinst, packname.preinst, packname.prerm, packname.postrm, назначения которых понятны из названия. Информация об установленном пакете будет добавлена в файле var/lib/opkg/status в виде (пример для популярного minicom):

Package: minicom
Version: 2.6.2-r0.2
Depends: libtinfo5 (>= 5.9), libc6 (>= 2.17)
Status: install ok installed
Architecture: armv7ahf-vfp-neon
Installed-Time: 1454529423

Важно обратить внимание на Status. Если пакет был установлен по всем правилам: все файлы скопированы на свое место, все скрипты выполнены, то статус будет Status: install ok installed. При работе в режиме offline все файлы будут скопированы, но скрипты не выполнятся, такие пакеты будут помечены как Status: install ok unpacked.
На этот случай в opkg предусмотрен специальный механизм пост конфигурации пакетов. Запускается он командой opkg configure <packname>. Если указать имя определенного пакета, то будут выполнены скрипты из var/lib/opkg/info для этого пакета; если имя опустить, то менеджер произведет конфигурацию для всех пакетов, у которых статус Status: install ok unpacked. Таким образом, при установке пакетов на host в режиме offline, при первой загрузке операционной системы на target следует выполнить opkg configure. Доверить это можно либо специальному скрипту, либо, если используется systemd, специальному сервису.

Работа с целевой rootfs


Настало время попробовать систему в деле. Для примера установим эмулятор терминала последовательного порта minicom.
Для установки пакетов нам понадобится распакованный образ корневой файловой системы целевой платформы rootfs. Предположим, что в rootfs установлен менеджер opkg, a в директории etc/opkg существуют файлы конфигурации *.conf. Если же его там нет, или по какой-то причине мы не хотим использовать конфигурацию из rootfs, мы можем через параметр указать какой файл настроек использовать: -f etc/opkg/opkg.conf. Путь к целевой файловой системе передаем через параметр --offline-root /path/to/rootfs.
Обновляем списки пакетов:

bin/opkg update --offline-root /path/to/rootfs

Просматриваем список доступных пакетов, ищем minicom.

bin/opkg list --offline-root ~/board/rootfs/angstrom/rootfs-v2015.10 | grep minicom

minicom - 2.7-r0.0 - Text-based modem control and terminal emulation program  Minicom is a
minicom-dbg - 2.7-r0.0 - Text-based modem control and terminal emulation program - Debugging files
minicom-dev - 2.7-r0.0 - Text-based modem control and terminal emulation program - Development
minicom-doc - 2.7-r0.0 - Text-based modem control and terminal emulation program - Documentation

Смотрим информацию о пакете:

bin/opkg info minicom --offline-root ~/board/rootfs/angstrom/rootfs-v2015.10

    Package: minicom
    Version: 2.7-r0.0
    Depends: libtinfo5 (>= 5.9), libc6 (>= linaro-2.20)
    Status: unknown ok not-installed
    Section: console/network
    Architecture: armv7at2hf-vfp-neon
    Maintainer: Angstrom Developers <angstrom-distro-devel@linuxtogo.org>
    MD5Sum: e4d11b7277fbc1c7db6bbd97ac52ca2c
    Size: 79354
    Filename: minicom_2.7-r0.0_armv7at2hf-vfp-neon.ipk
    Description: Text-based modem control and terminal emulation program  Minicom is a text-based modem control and terminal emulation program for Unix-like operating systems

Устанавливаем пакет:

bin/opkg install minicom --offline-root ~/board/rootfs/angstrom/rootfs-v2015.10

В файле var/lib/opkg появилась запись:

    Package: minicom
    Version: 2.7-r0.0
    Depends: libtinfo5 (>= 5.9), libc6 (>= linaro-2.20)
    Status: install user unpacked
    Architecture: armv7at2hf-vfp-neon
    Installed-Time: 1454594718

После того, как с созданного образа была запущена система и отработала команда opkg configure, запись в файле изменилась:

    Package: minicom
    Version: 2.7-r0.0
    Depends: libtinfo5 (>= 5.9), libc6 (>= linaro-2.20)
    Status: install user installed
    Architecture: armv7at2hf-vfp-neon
    Installed-Time: 1454594718

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

rm -rvf  ~/board/rootfs/angstrom/rootfs-v2015.10/var/cache/opkg/*  
rm -rvf  ~/board/rootfs/angstrom/rootfs-v2015.10/var/lib/opkg/lists/*

На заметку: опция --volatile-cache позволит очистить кэш автоматически при завершении работы.

Вместо заключения


Несмотря на работоспособность, у Offline mode есть некоторые недостатки. Дело в том, что команда opkg configure запускает на выполнение только \*.postinst, но остается нерешенным вопрос с выполнением скриптов \*.preinst. В силу того, что \*.preinst встречается достаточно редко в пакетах, для меня является приемлемым в ручном режиме просмотреть скрипты, и при необходимости отработать их при первом запуске целевой системы (специальны service для systemd). Буду благодарен за совет.

Почитать по теме:


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


  1. ValdikSS
    05.02.2016 12:10

    Не совсем понятно, по какой причине вам приходится вручную устанавливать пакеты. Не знаю насчет Yocto, но в OpenWRT Buildroot можно отметить, какие пакеты попадут в rootfs. Неужели Yocto умеет собирать только базовую ФС, и все приходится устанавливать вручную? Или все сложнее, и вы ставите таким образом софт, которого нет в стандартной поставке, или который не хочется компилировать?


    1. Shamrel
      05.02.2016 13:25
      +1

      Конечно не хочется. Речь идет о промышленном применении. Допустим имеется образ rootfs. Отлаженный образ, обросший прикладным ПО и специфичными настройками. Прикладное ПО разрабатывают, собирают и ведут люди в разных отделах дружного коллектива. Образ тиражируется на серийных устройствах. И тут понадобилось заказчику поставить туда стандартный minicom. Зачем ВСЕ перекомпилировать? А если учесть, что с последней компиляции сменилась версия компилятора, то придется тестировать все бинарники. Ну и, конечно же, время. Компиляция образа занимает не один час, требует дискового пространства, и, конечно же, на хосте должно быть установлено все окружение для кросс-компиляции.


      1. ValdikSS
        05.02.2016 14:51

        Э? А образ-то вы откуда взяли, не путем компиляции, что ли? В OpenWRT достаточно поставить нужную галку, и он пересоберет образ со включенным minicom, скомпилировав только его, а остальные пакеты просто скопировав, они же уже скомпилированные. И toolchain он вам сам соберет, если нужно. Разве сейчас кто-то собирает иначе, вручную?


        1. dlinyj
          05.02.2016 15:03
          +2

          Всё это прекрасно, когда собирает всё один отдел. Тот же опенврт обновляется. Вот собирал rootfs один отдел 4 года назад. Люди уже поувольнялись, сами исходники где-то в архивах лежат. Народ дальше пилит софт под эту сборку. И тут внезапно понадобилось поставить один пакетик.

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


          1. ValdikSS
            05.02.2016 15:38

            То, что собирают некие «отделы», а не система сборки, и что исходники в архивах лежат, говорит только о неорганизованности. У shamrel, судя по его ответу ниже, все лучше. Образ, в общем случае, не должен редактироваться вручную, его должна собирать система сборки до того состояния, которое вам нужно. В том же OpenWRT Buildroot это решается простым копированием нужных конфигурационных файлов в специальную директорию, которая она потом включает в конечный образ. Как правило, пересборка образа занимает несколько десятков секунд.


            1. dlinyj
              05.02.2016 15:54

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

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

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

              З.Ы. Иной раз, программисты не кидают свои драйвера в общую сборку, а переписывают их ручками, думая что «все догадаются». Ну забыли включить в конечную сборку. Ты берёшь такую сборку, не думая что дрова у прогера лежат отдельно. Собираешь, и почему-то у тебя не работает какое-то важное устройство, а то и вообще вся сборка не пашет. И тут начинаешь курить. Да, программера бить бичём, но толку?


            1. Shamrel
              05.02.2016 17:14

              А если не использовать OpenWRT или другой проект «под ключ»? А образ больше чем на половину состоит из кастомного ПО, которого по определению нет в открытых репозиториях? А если аппаратная платформа тоже своя, и в ней железо, поддержку которого только предстоит включить в ядро? Не раз бывало, что в прототип входят чипы с пометкой «Release Engineer», а производитель его еще только допиливает. А еще можно представить, что на производстве не одно изделие, и для каждого своя прошивка. А еще, если предприятие коммерческое и выпускает конкурентный продукт, то задержка выхода на рынок продукта может обернутся боком. И просто, иногда банально, нет времени на допил системы сборки.
              Допустим, ты знаешь, что образ для изделия B должен отличается от образа да изделия A только несколькими пакетами, причем отлаженными уже в изделии С, то проще и быстрее взять образ изделия А, запустить opkg, удалить лишнее и установить недостающее.
              К сожалению, инженерия в современных реалиях — это искусство компромиссов.


              1. ValdikSS
                05.02.2016 17:36

                А если не использовать OpenWRT или другой проект «под ключ»?
                Так вы же сами Yocto используете, к чему такой вопрос?
                А образ больше чем на половину состоит из кастомного ПО, которого по определению нет в открытых репозиториях?
                Как он устанавливался, в обход пакетного менеджера? Там же есть какая-то система сборки? В этом случае, он добавляется в меню buildroot в течение 15 минут.
                А если аппаратная платформа тоже своя, и в ней железо, поддержку которого только предстоит включить в ядро?
                А сейчас как оно работает?
                К сожалению, инженерия в современных реалиях — это искусство компромиссов.
                Пока я только вижу, что у вас, по какой-то причине, нежелание использовать готовые и быстрые инструменты. Я с buildroot разобрался за день, зато потом смог генерировать любой образ с любыми нужными настройками в течение секунд, а не вспоминать, где у меня что лежит, где какие пакеты, и что мне нужно добавить вручную. Причем считаю себя новичком в embedded.


                1. dlinyj
                  08.02.2016 11:40
                  +1

                  >>Причем считаю себя новичком в embedded.

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


  1. dlinyj
    05.02.2016 14:27

    Неплохо. И смысл задумки понятен, но почему-то он всё равно отдаёт граблями. Почему автор не пересобирает систему мне тоже понятно. Лично я сталкивался с тем, что «работает — не трогай». Иногда, чтобы заново пересобрать систему требуется месяцы работы, тогда как поставить пакетики — пару дней.

    У меня вопрос, почему нельзя сделать образ rootfs, который бы имел разовый скрипт для установки этих пакетов. Что берёшь, заливаешь в систему и при первом запуске идёт установка пакетов, а потом автоматическая конфигурация? Я давно не брал в руки шашек, но помню что в /etc есть загрузочный файлик, которому можно доверить этот геммор. Да, я понимаю что это тоже велосипед, но он более надёжен.

    И если можно, распишите подробнее о «фантомных болях», т.к. если железо идентично, то по идее всё должно работать как часы. Тем более, если rootfs находится в режиме ro.


    1. Shamrel
      05.02.2016 15:08
      +1

      У меня вопрос, почему нельзя сделать образ rootfs, который бы имел разовый скрипт для установки этих пакетов. Что берёшь, заливаешь в систему и при первом запуске идёт установка пакетов, а потом автоматическая конфигурация? Я давно не брал в руки шашек, но помню что в /etc есть загрузочный файлик, которому можно доверить этот геммор. Да, я понимаю что это тоже велосипед, но он более надёжен.

      Действительно, systemd обладает таким механизмом. И, признаться честно, те пакеты, которые требуют выполнение preinst я доверил устанавливать ему при первом запуске системы. Но с большинством пакетов, так поступать для нас оказалось нецелесообразно:

      1. Каждая версия «прошивки», читай образ NAND, имеет свой номер и жестко контролируется (в некоторых случаях, md5 образа прописана в сертификате соответствия). Должна быть полная уверенность, что все то, что стоит на опытном образце, пойдет в серийный прибор: не больше и не меньше. Поэтому ни о какой «доустановки» пакетов из вне после прошивки не может быть и речи. Прибор может оперировать только теми данными, которые в нем есть после прошивки. Потому исходные пакеты должны быть уже включены в образ. А раз они уже в образе, то почему бы их не инсталлировать ДО старта системы, а не ПОСЛЕ? Отсюда второй аргумент:
      2. При серийном производстве время, проведенное прибором на сборочном участке отражается на его себестоимости. Установка большого количества пакетов при первом запуске увеличит это время. (практика показала, что значительно)
      3. При включении исходных пакетов в образ, необходимо в ручном режиме следить, что бы все зависимые пакеты были тоже включены. Не проще ли это доверить opkg?

      А по поводу «фантомных болях»: например, ключи шифрования (а их у нас их много разных), неочищенные кэши, временные файлы и lock файлы процессов. Спору нет, оно все лечится.


      1. dlinyj
        05.02.2016 15:12
        +1

        Я снимаю шляпу за подход. Он неплох, но я бы не показывал никому сей велосипед :))). Сам работал, знаю, что порой изобретаешь такие страшные костыли, от которых стыдно. Но «правильно» сделать порой сильно дороже.