Как делать патчи linux

Установка патчей в Linux – работа с утилитой patch

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

Что такое патч?

Говоря о патчах вкупе с утилитой patch, следует подразумевать, что это касается исключительно текстовых данных. Другими словами, происходит работа с исходными кодами проекта, будь то код C++, PHP, HTML и т. д. Вообще, все самые «суровые» программисты или разработчики в процессе своей совместной работы над проектом обмениваются исключительно отдельными правками, а не пересылают друг другу актуальные версии проектов целиком.

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

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

Синтаксис и основные опции команды patch

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

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

Здесь originalfile – это файл, который необходимо «пропатчить» до актуального состояния. А patchfile – файл-патч. Сразу возникает вопрос: а откуда берётся этот файл-патч? Ответ: он генерируется другой утилитой — diff, которая находит построчные различия между файлами. Либо же патч может быть составлен вручную, автором, если он знаком с соответствующим форматом. Но это бывает крайне редко, обычно прибегают к помощи diff или её аналогов.
В следующей таблице приведены опции команды patch, которые используются наиболее часто:

Читайте также:  Python для windows x64

Помещает неудавшиеся (отклонённые) изменения в отдельный файл rejecfile вместо файла .rej по-умолчанию.

Опция Значение
-i patchfile Читает информацию из патч-файла, указываемого параметром patchfile.
-r rejectfile, —reject-file=rejectfile
-N, —forward Когда патч не применяется, то утилита patch обычно пытается определить, выглядит ли ситуация так, как если бы патч уже был применён. Опция -N отключает такое поведение.
-pnum, strip=num Обрезает части пути к файлу, разделяемые символом косой черты до уровня, указанного в параметре num. Например: p0 оставит путь /u/john/src/blurfl/blurfl.cpp неизменным, а p4 обрежет тот же путь до blurfl/blurfl.cpp.
-o outputfile, —output=outputfile Отправляет вывод в указываемый в параметре outputfile файл. Не следует использовать эту опцию, если в качестве outputfile указывается файл, который должен быть пропатчен. Если в качестве outputfile указать символ дефиса «-», то вывод будет направляться в стандартный поток STD_OUT.
-E, —remove-empty-file Удаляет файлы, оказавшиеся пустыми после применения патча. Эта опция имеет смысл, когда используемые патчи имеют не контекстный формат.
—dry-run Печатает результаты применения патча без реальной модификации файлов. Полезно для быстрого и безопасного тестирования патчей.
-R, —reverse Откатывает все изменения (если они возможны), т. е. отменяет установку патча.
-c, —context Интерпретирует файл патча как обычный контекстный формат, генерируемый утилитой diff.
-b, —backup Создаёт резервную копию оригинального файла вместо его удаления.

Применение патчей к отдельным файлам

Прежде, чем начать рассмотрение практических примеров, необходимо сказать несколько слов о той самой утилите, которая и создаёт патчи — diff. Она может генерировать патчи трёх типов — простой, контекстный и контекстный унифицированный. Простой гораздо более компактный по размеру, чем контекстные, но последние гораздо более удобочитаемы и понятны для восприятия пользователем. Для того, чтобы сгенерировать простой патч, для команды diff никаких специальных опций не требуется. А для генерации контекстного или унифицированного контекстного патчей предназначены опции -с и -u соответственно:

Пусть имеется файл с кодом C++ ChildClass.cpp:

И пусть в этот файл было внесено следующее изменение: метод valueSqr() был переименован в calcSqr(). Тогда контекстный патч (файл contextpatch) будет выглядеть следующим образом:

Теперь, чтобы пропатчить старую версию ChildClass.cpp, нужно выполнить команду:

В результате будет получен файл ChildClass_new.cpp с актуальным содержимым.

Работа с проектами

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

Пусть имеется старый проект в каталоге base-project. Внутри него имеются подкаталоги include и src, в которых, в свою очередь находятся файлы с изменениями — ChildClass.h (в каталоге include) и ChildClass.cpp (в каталоге src). Сам изменённый (актуальный) проект был помещён в отдельный каталог new-project. Подготовка патча будет выглядеть следующим образом:

Сгенерированный файл-патч project-patch:

Следует обратить внимание, что в данных примерах указываются относительные пути. Файл-патч будет помещён в текущий активный каталог.
Чтобы применить патч нужно выполнить следующую команду:

Как видно, вместо ключа -i можно использовать символ «

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Источник

OTA обновление устройств с Linux

Вступление

Недавно у меня возникла задача обновлять удалённо некоторое двузначное число IoT-устройств с Linux через интернет. Задачу нужно было решить быстро. Времени и желания изобретать что-то своё не было совсем. Устанавливать обновления вручную по ssh — нереально. Автоматизировать установку по ssh тоже, устройства могут неожиданно выключаться или терять сеть.

Поиск решений по обновлению IoT устройств показал, что, судя по всему, мне больше всего подходит Mender:

end-to-end решение — клиент + сервер + инструменты

A/B обновление rootfs — на устройстве есть два одинаковых раздела rootfs A и B. Система загружается с активного раздела A, устанавливает обновление на раздел B, загружается с раздела B и в случае успеха делает его активным. В случае неудачного обновления активным остаётся раздел A.

работает с debian и yocto

не использует контейнеры

есть open-source версия под лицензией Apache v2

На сайте проекта можно почитать как всё это работает.

Кстати, Mender уже упоминался на хабре в статье @MooooM, но внедрить его тогда не получилось.

Workflow

Я продемонстрирую процесс обновления системы на примере Raspberry Pi 3B c Raspberry Pi OS. Карта памяти от 8 ГБ и выше.

В качестве сервера Mender будем использовать бесплатный демо-сервер hosted.mender.io (не более 10 устройств и 12 месяцев работы).

Подготовка образов будет производится на компьютере с Ubuntu 20.04.

В своей документации Mender рекомендуют примерно такой подход для устройств с debian:

Установка чистой ОС

Подготовка эталонного образа

Копирование эталонного образа на ПК

Преобразование эталонного образа при помощи mender-convert

Подготовка устройства (provisioning)

Подготовка пакета обновления (artifact)

Развёртывание обновления (deploy)

0. Сервер

1. Установка чистой ОС

Включаем последовательную консоль /boot/config.txt : enable_uart=1

2. Подготовка эталонного (golden) образа

Вставляем SD-карту в устройство(Raspberry Pi 3B) и подключаемся по uart при помощи minicom.

Меняем стандартный пароль при помощи raspi-config .

Настройте и проверьте подключение к интернету. Я подключил свою Raspberry по ethernet (не совсем over-the-air, знаю).

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

Положим туда текстовый файл important_file.txt содержащий одну строку hello_habr .

3. Копирование эталонного образа

Выключаем устройство и вставляем SD-карту в компьютер:

Выводим список разделов:

Считываем образ с карты:

4. Преобразование эталонного образа при помощи mender-convert

Для подготовки образа нам понадобится mender-convert и Docker.

Проверяем ёмкость SD-карты, на которую будем устанавливать систему:

Переводим байты в мегабайты: 7910457344 / 1024 / 1024 = 7544 MB

Создаём файл с конфигурацией mender-convert ./configs/raspberrypi3_custom_config с вот таким содержимым:

MENDER_STORAGE_TOTAL_SIZE_MB — общий размер SD полученный ранее

MENDER_DATA_PART_SIZE_MB — размер раздела /data

MENDER_ADDON_CONNECT_INSTALL — флаг установки mender-connect (позволит запускать командную строку на удалённом устройстве и передавать файлы)

Создаём директорию ./rootfs_overlay/etc/mender . Это оверлей файловой системы который будет записан на наш эталонный образ при запуске mender-convert.

Создаём файл конфигурации mender-client ./rootfs_overlay/etc/mender/mender.conf :

TenantToken — токен который нужно посмотреть в hosted.mender.io (Settings->Organization and billing->Organization token).

Создаём файл конфигурации mender-connect ./rootfs_overlay/etc/mender/mender-connect.conf :

Создаём директорию ./input и кладём в неё образ golden-image-1.img полученный ранее.

MENDER_ARTIFACT_NAME — название релиза, которое будет отображаться в hosted.mender.io.

Дальше происходит магия:

в образ устанавливается U-Boot

в Raspberry OS доустанавливается mender-client и mender-connect

создаются A и B разделы rootfs исходя из общего размера SD-карты, а также размера раздела data

данные из директории /data автоматически переносятся на новый раздел, который не будет перезаписываться при обновлении системы

Подробнее можно посмотреть в ./logs/convert.log.XXXX .

На выходе получаем:

./deploy/golden-image-1-raspberrypi3-mender.img — преобразованный образ системы для записи на SD-карту

./deploy/golden-image-1-raspberrypi3-mender.mender — архив с обновлением, который загружается на сервер Mender(519МБ) и потом скачивается устройствами.

5. Подготовка устройства (provisioning)

Записываем образ golden-image-1-raspberrypi3-mender.img на SD-карту:

Теперь разделы выглядят так:

Вывод uart при загрузке (у нас теперь есть U-Boot):

6. Авторизация устройства

Через пару минут после включения устройства на главной странице hosted.mender.io должно появиться сообщение о том, что новое устройство ожидает аутентификации. После подтверждения статус устройства поменяется с pending на accepted.

Через раздел Troubleshoot можно запустить удалённый терминал на устройстве (работает через mender-connect). При этом, на устройстве не включен ssh-сервер. Есть возможность передавать файлы через вкладку File transfer.

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

7. Подготовка пакета обновления (artifact)

Возвращаемся к нашему эталонному образу (golden-image-1.img). Загружаем ОС и вносим необходимые изменения. Я, например, сделаю MQTT-клиента, который шлёт на брокер температуру процессора (под спойлером).

Создаём systemd таймер: /etc/systemd/system/mqtt.timer

Создаём сервис, который будет вызываться таймером mqtt.timer: /etc/systemd/system/mqtt.service

Скрипт, который будет вызываться сервисом mqtt.service: /home/pi/mosquitto_pub.sh

Делаем скрипт исполняемым:

После внесения изменений снова считываем образ с карты:

Преобразовываем образ при помощи mender-convert:

Загружаем в hosted.mender.io новый релиз golden-image-2-raspberrypi3-mender.mender. Заодно можно загрузить первый релиз, если захотим откатиться.

8. Deploy

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

После обновления проверим, начало ли устройство отправлять сообщения по MQTT (под спойлером):

Создайте подключение к mqtt://test.mosquitto.org, порт: 1883.

Подпишитесь на топик habr/#

Наблюдаем, как устройство остывает после недавнего апдейта:

Выводы

Mender позволяет довольно легко добавить функцию обновления образа системы по воздуху в существующее устройство.

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

Open-source версия Mender позволяет делать намного больше, чем показано в этой статье, например, state-scripts, identy и inventory устройств и т.д. Open-source версию сервера Mender можно самостоятельно развернуть в облаке используя Kubernetes.

Рекомендую внимательно изучить список фич доступных в open-source и коммерческих версиях. Open-source версия вполне работоспособна, но её может не хватить для того чтобы закрыть все потребности крупного проекта (нет автоматического перезапуска процесса обновления, дельта-апдейтов, мониторинга сервисов и т.д.).

Было бы интересно сравнить Mender c RAUC или swupdate в качестве клиента и hawkBit в качестве бэкенда.

Источник

Читайте также:  Windows 10 все сломал
Оцените статью