Копать или не копать?
     Я хочу поговорить о казалось-бы простой, но важной части любой ОС — о регламенте или структуре размещения файлов или более просто — хочется поговорить о порядке и удобстве. Затрагивать я буду UNIX подобные ОС, а конкретно все многообразие дистрибутивов на базе ядра Linux.
     Итак, программист создал программу и хочет её распространять, теперь есть задача — установка программы пользователем. Для этого, вроде как существуют стандарты (FHS, LSB), однако многообразие средств установки ПО просто обескураживает и программист, в меру своих способностей, реализует средства по установке, от простого README до чего-то более сложного.
     Да, сегодня большинство дистрибутивов обладают системой установки программ, которая в пределах дистрибутива решает вопросы с установкой программ и в какой-то степени облегчает труд программисту. А когда понадобилось управлять большой группой разных дистрибутивов (стойки в ЦОД'ах, VM) были создали различные системы управления конфигурациями (CFEngine, Bcfg2 puppet, Chef, etc). И вроде-бы всё ok, но чувство костыльности, всего этого многообразия, вот уже лет 14 (с момента когда начал ковырять RedHat Linux 5.2) не покидает меня… Так в чём же дело?
Корни
     Попробую отбросить всё наносное, что сегодня применяется для установки/удаления программ в типичном дистрибутиве Linux — прикину что делать с программой, сложнее чем helloworld, убрав:
- системы управления конфигурациями;
- пакетный менеджер;
- все без исключения скрипты установки, в том числе те, которые производят предварительные действия типа создания каталогов для размещения библиотек, ресурсов, документации программы, скрипты компиляции и т.п.;
- и даже README/INSTALL файл.
Какие теперь надо выполнить действия что-бы установить, а затем удалить программу, вместе со всеми её компонентами?
     Допустим я сумел разобраться в том, где должны размещаться части программы, создал нужные каталоги, скопировал файлы, обновил окружение и программа теперь успешно выполняется.
     Но прошло время, мной уже давно забыто где и как размещались компоненты программы, я лишь помню название программы и мне нужно удалить её вместе со всеми компонентами и созданными ею файлами. И вот тут я становлюсь в тупик — что делать? Натравить на программу strace и ldd? Найти все одноимённые каталоги, что-бы удалить документацию? Ради того, что-бы просто удалить программу и все её компоненты? И ведь не факт, что всё будет найдено и удалено.
     Итак, если обойтись без реверансов, то наблюдаемая ситуация с установкой/удалением программ в Linux — хорошо замаскированный
И что с этим делать?
     Есть простая программа install, которая позволяет копировать и устанавливать нужные атрибуты на копируемые файлы, однако и результаты её работы следует удалять привычным rm.
     Что-бы запустить какую-либо программу и завершить её выполнение доступны функции execve() и _exit(), что-бы отрыть файл и закрыть его есть функции open()/create() и close(). При этом при выполнении/остановке программы имеет место быть идентификатор процесса (PID), а при открытии/закрытии файла — десктриптор файла. Так почему бы для установки/удаления программы не ввести, что-то похожее на идентификатор размещения программы, допустим назвать его Deploy ID (DID)/Install ID (IID), который на самом деле может являться стандартным UUID, предоставив несколько функции для оперирования с этим идентификатором типа _appinstall()/_appupdate()/_appuninstall()/etc и соответствующую команду типа ps, например app? И почему-бы не размещать программу с её компонентами, допустим не в
/etc/program.conf
/etc/program.conf.d/someone.conf
/usr/bin/program
/usr/lib/program/libprogram.so
/usr/share/doc/program/manual.txt
/var/program/database
а в
/etc/<did>/program.conf
/etc/<did>/program.conf.d/someone.conf
/usr/bin/<did>/program
/usr/lib/<did>/libprogram.so
/usr/docs/<did>/manual.txt
/var/<did>/database
Да, это будет ещё один пакетный менеджер, к нему могут появляться удобные GUI и системы управления конфигурациями никуда не денутся. Но! Все может ложиться в POSIX, размещение программы и её компонент может производиться строгим образом, а удаление становится простым даже в случае разрушения базы идентификаторов. Кроме-того, пройдясь по структуре размещения файлов можно будет восстановить все идентификаторы, пересоздав базу, так же как это делает updatedb строя индекс для locate. Появляется возможность, достаточно просто, производить установку нескольких версий или экземпляров одной программы и переключения между версиями.
     Если идти дальше, то резонно и конфигурационные файлы программ привести к какому-то общему знаменателю, унифицировав их содержимое используя, например, тот же xml или json, но предоставляя функции на манер _appconfigdeploy() с валидацией конфигурации на соответствие предопределённой, допустим в том же POSIX, схеме.
Сухари сушить или удочку забрасывать?
     Вообще есть дистрибутивы, которые вроде как двинулись в сторону унификации размещения программ их компонент и конфигурационных файлов, такие как GoboLinux (который похоже прекратили развивать), и на мой взгляд к этому подходу ближе всех пакетный менеджер nix и созданный на его базе дистрибутив NixOS. Но я думаю нужна масштабная инициатива, обсуждение, такое-же как в отношении FHS. Если конечно я вообще прав.
Вот такие пироги.
P.S. Как-то я поднимал обсуждение на эту тему в 2006 году, на форуме gentoo.ru.
UPD: Сходу минусующие, задайтесь вопросом:
устроило-бы программиста, скажем, появление различий в способе fork'а процесса, допустим в debian и redhat? Думаю, что нет (помятуя о коссплатформенном). Так почему костылизм в виде разных правил и подходов к установке программ является нормой?
UPD: Как видно по комментариям на LOR, неспособность бороться со стереотипами, свойственна не только прожженным пользователям windows, увы, добавлю некоторое пояснение:
Схема отличается простой логикой — один id для одного экземпляра пакета, развёрнутого в систему без разрушения уже сложившейся и стандартизированной базовой файловой иерархии (в отличии от nixos, в котором изменения чрезмерно тотальны, например /usr/ содержит только /usr/bin/env -> /nix/store/id-coreutils-version/bin/env, это при том, что сам пм nix позволяет под любым дистрибутивом свою помойку организовать в $HOME, не затрагивая основную систему вообще).
Собственно полученный id выступает в роли идентификатора для всех частей программы, добавляя всего один уровень в файловой иерархии для незначительной, но удобной изоляции компонент программ друг от друга (в том числе одной и той же программы в нескольких версиях), через этот же id проще контролировать права доступа к программе и её компонентам.
Конечно есть жертвы, основная из которых и которую не принимают, это human-readable path (hrp) — по сути всё недопонимание из-за того, что за hrp видят KISS и недоумевают — почему вдруг пути до программы и её компонент должны стать чем-то сложным, но нет тут ничего сложного, взгляните в теже fstab — почему для монтирования стали использовать label и uuid? Да потому, что захотелось удобства. Вот и я своим did пытаюсь получить удобства, хотя-бы:
- лучше контролировать программы, безотносительно наличия/отсутствия репы;
- иметь возможность вести несколько версий программы, не парясь c prefix и slot;
- иметь возможность получать доступ к частям программы через имя программы (так же как мы это делаем дёргая функцию звонить из звонилки смарта, не пытаясь каждый раз перед этим прописать в конфиг команды инициализации и дозвона для gsm модуля), на уровне shell, без необходимости дёргать различные тулзы, писать alias'ы и править переменные окружения.