Pull to refresh

Fakeroot, XCode и PackageMaker

Reading time5 min
Views4.6K
Хочу поделиться своим опытом адаптации fakeroot для использования на маке в связке с XCode. Fakeroot запускает программы в особом окружении, которое эмулирует сессию супер-пользователя. Права root могут потребоваться при сборке инсталлятора с помощью PackageMaker.

Исторически на UNIX-like системах, к которым относится и Mac OS X, сложился определенный подход к распространению софта. В основе любого пакета, будь то deb, rpm или макосевый pkg, лежит архив, который разворачивается непосредственно на целевую файловую систему. Например если пакет litware.pkg разворачивается в папку /usr/local/bin, а в архиве лежит исполняемый файл lit c маской доступа 0755 и владельцем/группой root:wheel, то в системе появится программа /usr/local/bin/lit (маска доступа 0755, владелец/группа root:wheel, т.е. такие же, как в архиве).

Пакет создается с помощью специальных утилит (на Mac OS X это консольный packagemaker). Утилита помещает в пакет содержимое папки, которая указывается при запуске. Содержимое папки включаются в пакет как есть. В частности, чтобы изготовить правильный litware.pkg, нeобходимо выставить файлу lit владельцем root:wheel. К сожалению, для успеха данной операции необходимы права супер-пользователя.

В Linux эту проблему обходят с помощью fakeroot. На самом деле программы запущены от имени текущего пользователя, но за счет перехвата ряда системных вызовов создается видимость работы от имени root. Например перехватываются chown и stat. Chown успешно меняет owner:group файла, при этом вместо реальной модификации этих полей на файловой системе (что потребовало бы настоящего root), информацию запоминает демон faked. Перехваченный stat незаметно модифицирует параметры файла, полученные от файловой системы, на основе сохраненной информации о предшествующих вызовах chown.

Fakeroot-mini


Fakeroot поддерживает MacOS X до версии 10.7 включительно. К сожалению, fakeroot отсутствует в обоих популярных менеджерах портов для Mac OS X (MacPorts, Fink). Для корректной работы необходима инсталляция. Кроме того, классический fakeroot неудобен в связке с XCode, так как нет возможности объединить все скрипты, требующие fakeroot, в одну fakeroot сессию. Разные fakeroot сессии совершенно независимы: изменения, сделанные в рамках одной сессии, не видны в другой.

На основе кода fakeroot я сделал модифицированную версию fakeroot-mini со следующими ключевыми особенностями:
  1. Не требует инсталляции. Распространяется единственная динамическая библиотека. Эту библиотеку можно положить непосредственно в репозиторий что снимает необходимость в установке дополнительного софта для сборки проекта.
  2. Хранит информацию в расширенных аттрибутах файловой системы. Это решение устраняет проблему независимых fakeroot сессий.

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

/usr/bin/env DYLD_INSERT_LIBRARIES=<имя-библиотеки> command [args]


Демонстрационный XCode проект


По ссылке можно скачать демонстрационный проект для XCode. Проект собирает простое консольное приложение + инсталлятор.



Настройки проекта:

DEPLOYMENT_LOCATION=YES
DEPLOYMENT_POSTPROCESSING=YES
INSTALL_OWNER=root
INSTALL_GROUP=wheel
STRIP_INSTALLED_PRODUCT=NO
CHOWN=$(SRCROOT)/chown
LIBFAKER=$(SRCROOT)/libfakermini-1.0.0.dylib
PACKAGE=$(BUILT_PRODUCTS_DIR)/litware.pkg

DEPLOYMENT_LOCATION=YES  заставляет XCode раскладывать файлы по установочным путям. Например для программы lit указан установочный путь /usr/local/bin/lit. С включенной настройкой DEPLOYMENT_LOCATION XCode поместит lit по пути $(DSTROOT)/usr/local/bin/lit. Значение $(DSTROOT) настраивается, по-умолчанию это /tmp/<имя-проекта>.dst. Во временной папке будет построено следующее дерево файлов:

/tmp/litware.dst/
└── usr
    ├── local
    │   └── bin
    │       └── lit
    └── share
        └── man
            └── man1
                └── lit.1

DEPLOYMENT_POSTPROCESSING=YES  включает дополнительную обработку файлов в $(DSTROOT). Среди прочего, устанавливается владелец $(INSTALL_OWNER) и группа $(INSTALL_GROUP) файлов.

STRIP_INSTALLED_PRODUCT=NO  отключает удаление отладочной информации в собранных бинарниках. Из-за ошибки в XCode, иногда при повторной сборке проекта отладочная информация копируется уже после того, как отработал strip и все удалил.

CHOWN=$(SRCROOT)/chown  указывает XCode использовать скрипт в папке с исходными текстами проекта вместо стандартной утилиты /usr/sbin/chown. Скрипт настраивает fakeroot-окружение и вызывает стандартный chown.

Переменные LIBFAKER и PACKAGE определены для удобства, на поведение XCode они не влияют.

Инсталлятор изготавливается с помощью следующего скрипта:

/usr/bin/env "DYLD_INSERT_LIBRARIES=${LIBFAKER}" \
    /usr/sbin/mtree -f "${SRCROOT}/mtree.spec" -p "${DSTROOT}" -U && \
rm -f "${PACKAGE}" && \
/usr/bin/env "DYLD_INSERT_LIBRARIES=${LIBFAKER}" \
    /Developer/usr/bin/packagemaker --id org.example.litware \
    --root "${DSTROOT}" --no-recommend --target 10.5 \
    --out "${PACKAGE}"

Сначала запускается mtree в fakeroot-окружении для настройки маски доступа и владельца/группы для папок в $(DSTROOT). Необходимо это потому, что хотя XCode и настраивает маску доступа и владельца/группу для $(DSTROOT)/usr/local/bin/lit, промежуточные папки (ex: $(DSTROOT)/usr/local/bin) не обрабатываются.

По неизвестной причине, packagemaker не перезаписывает существующие файлы при генерации инсталлятора. Поэтому мы явно удаляем существующий файл инсталлятора в случае его наличия перед запуском packagemaker в fakeroot-окружении.

Заключение


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

Информация может быть полезна разработчикам системных утилит и демонов для MacOS. Инсталлятор для обычных пользовательских приложений, как правило, можно собрать без fakeroot.

Ссылки
  1. Демонстрационный проект для XCode
  2. Оригинальный fakeroot
  3. Fakeroot-mini
  4. Использование xcconfig файлов в XCode
  5. Перехват системных вызовов в Mac OS X

Tags:
Hubs:
Total votes 15: ↑14 and ↓1+13
Comments0

Articles