определить систему инициализации с помощью оболочки

<p>это может иметь большее отношение к обнаружению операционных систем, но мне особенно нужна система инициализации, используемая в настоящее время в системе.</p><p>fedora 15 и ubuntu теперь используют systemd, ubuntu раньше использовал upstart (долгое время по умолчанию до 15.04), в то время как другие используют варианты system v.</p><p>у меня есть приложение, которое я пишу, чтобы стать кроссплатформенным демоном. сценарии инициализации генерируются динамически на основе параметров, которые могут быть переданы при конфигурировании.</p><p>то, что я хотел бы сделать, это только создать сценарий для конкретной системы инициализации, которую они используют. таким образом, скрипт установки может быть запущен без параметров в качестве пользователя root, а демон может быть автоматически "установлен".</p><p>вот что я придумал:</p><ul> <li>поиск systemd, upstart и т.д. в/bin</li> <li>сравните/proc/1/comm с systemd, upstart и т.д.</li> <li>спросите пользователя</li> </ul><p><strong>каков будет лучший кроссплатформенный способ сделать это?</strong></p><p>вид связанного, <strong>могу ли я рассчитывать на то, что bash будет работать на большинстве * nix или это зависит от дистрибутива/ос?</strong></p><p>целевые платформы:</p><ul> <li>mac os</li> <li>linux (все дистрибутивы)</li> <li>bsd (все версии)</li> <li>solaris, minix и другие * nix</li> </ul>

<p>для второго вопроса ответ <strong> нет </strong>, и вы должны взглянуть на <a href="https://unix.stackexchange.com/questions/9997/resources-for-portable-shell-programming">ресурсы для переносимого программирования на shell</a> .</p><p>что касается первой части - прежде всего, вам, безусловно, нужно быть осторожным. я бы сказал <em> выполнить несколько тестов, чтобы убедиться</em> - потому что тот факт, что кто-то <em> действительно имеет</em> systemd (например), не означает, что это на самом деле используется по умолчанию <code>init</code>. кроме того, глядя на <code>/proc/1/comm</code> может вводить в заблуждение, потому что некоторые установки различных программ инициализации могут автоматически сделать <code>/sbin/init</code> символическая ссылка или даже переименованная версия их основной программы.</p><p>возможно, самой полезной вещью может быть просмотр типа сценариев инициализации - потому что это то, что вы на самом деле будете создавать, независимо от того, что их запускает.</p><p>в качестве примечания вы также можете взглянуть на <a href="http://roy.marples.name/projects/openrc">openrc</a> , цель которого - предоставить структуру сценариев инициализации, совместимых как с системами linux, так и с системами bsd.</p>

<p>я сам вошел в эту проблему и решил сделать несколько тестов. я полностью согласен с ответом, что каждый пакет должен быть упакован отдельно, но иногда есть практические проблемы, которые мешают этому (не в последнюю очередь человеческие ресурсы).</p><p>так что для тех, кто хочет "автоматически определять", вот что я узнал <em> на ограниченном наборе дистрибутивов</em> (подробнее ниже):</p><ul> <li> <p>вы можете сказать выскочку из:</p> <pre><code>[[ `/sbin/init --version` =~ upstart ]] && echo yes || echo no </code></pre></li> <li> <p>вы можете сказать systemd из:</p> <pre><code>[[ `systemctl` =~ -\.mount ]] && echo yes || echo no </code></pre></li> <li> <p>вы можете сказать sys-v init из:</p> <pre><code>[[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]] && echo yes </code></pre></li> </ul><p>вот мои эксперименты со следующей командной строкой:</p><pre><code>if [[ `/sbin/init --version` =~ upstart ]]; then echo using upstart; elif [[ `systemctl` =~ -\.mount ]]; then echo using systemd; elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then echo using sysv-init; else echo cannot tell; fi </code></pre><p>в случае с ec2 (я включаю идентификатор ami с восточной стороны сша):</p><ul> <li>archlinux: использование systemd (начиная с <a href="https://www.archlinux.org/releng/releases/2012.10.06/">2012.10.06</a> )</li> <li>centos6.4 ami-52009e3b: использование upstart</li> <li>centos7 ami-96a818fe: использование systemd</li> <li>debian 6 ami-80e915e9: использование sysv-init</li> <li>debian 7.5 ami-2c886c44: использование sysv-init</li> <li>debian 7.6 gce container-vm: использование sysv-init</li> <li>rhel 6.5 ami-8d756fe4: использование upstart</li> <li>sles 11 ami-e8084981: использование sysv-init</li> <li>ubuntu 10.04 ami-6b350a02: использование upstart</li> <li>ubuntu 12.04 ami-b08b6cd8: использование upstart</li> <li>ubuntu 14.04 ami-a427efcc: использование upstart</li> <li>ubuntu 14.10 и младше: использование systemd</li> <li>aws linux 2014.3.2 ami-7c807d14: использование upstart</li> <li>fedora 19 ami-f525389c: использование systemd</li> <li>fedora 20 ami-21362b48: использование systemd</li> </ul><p>просто чтобы прояснить: <strong> я не утверждаю, что это надежно! </strong>, почти наверняка это не так. также обратите внимание, что для удобства я использую совпадения регулярных выражений bash, которые не везде доступны. выше это достаточно хорошо для меня прямо сейчас. однако, если вы обнаружите дистрибутив, где он не работает, пожалуйста, дайте мне знать, и я постараюсь это исправить, если есть ec2 ami, который воспроизводит проблему ...</p>

<h3>использование процессов</h3><p>рассмотрим выходные данные нескольких команд <code>ps</code>, которые могут обнаружить различные версии <code>systemd</code> & <code>upstart</code>, которые можно создать следующим образом:</p><p><em> выскочка </em></p><pre><code>$ ps -eaf|grep '[u]pstart' root 492 1 0 jan02 ? 00:00:00 upstart-udev-bridge --daemon root 1027 1 0 jan02 ? 00:00:00 upstart-socket-bridge --daemon  systemd $ ps -eaf|grep '[s]ystemd' root 1 0 0 07:27 ? 00:00:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 20 root 343 1 0 07:28 ? 00:00:03 /usr/lib/systemd/systemd-journald root 367 1 0 07:28 ? 00:00:00 /usr/lib/systemd/systemd-udevd root 607 1 0 07:28 ? 00:00:00 /usr/lib/systemd/systemd-logind dbus 615 1 0 07:28 ? 00:00:13 /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation </code></pre><p>обращение к названию процесса, который называется pid # 1, также может пролить свет на то, какая система инициализации используется. на fedora 19 (которая использует <code>systemd</code>, например:</p><pre><code>uid pid ppid c stime tty time cmd root 1 0 0 07:27 ? 00:00:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 20 </code></pre><p>обратите внимание, что это не <code>init</code>. в ubuntu с upstart это все еще <code>/sbin/init</code>.</p><pre><code>$ ps -efa|grep init root 1 0 0 jan02 ? 00:00:03 /sbin/init <strong>примечание:</strong> но используйте это с некоторой осторожностью. в камне нет ничего, что говорило бы о том, что конкретная система инициализации, используемая в данном дистрибутиве , имеет  для systemd в качестве pid # 1. общий $ (ps -eo "ppid,args" 2>/dev/null || echo "ps call error") \ | awk 'nr==1 || $1==1' | less ppid command 1 /lib/systemd/systemd-journald 1 /lib/systemd/systemd-udevd 1 /lib/systemd/systemd-timesyncd посмотрите на процессы с ppid 1 (потомки процесса init). (некоторые из) имен дочерних процессов могут указывать на используемую систему инициализации.файловая системаесли вы запросите исполняемый файл init, вы также сможете получить от него некоторую информацию. просто разбирая --version вывод. например: выскочка $ sudo /sbin/init --version init (upstart 1.5) copyright (c) 2012 scott james remnant, canonical ltd. this is free software; see the source for copying conditions. there is no warranty; not even for merchantability or fitness for a particular purpose. </code></pre><p><em> systemd </em></p><pre><code>$ type init init is /usr/sbin/init <strong>примечание:</strong> тот факт, что init не находится в своем стандартном расположении, является подсказкой. он всегда находится в /sbin/init по системам sysvinit. sysvinit $ type init init is /sbin/init </code></pre><p>также это:</p><pre><code>$ sudo init --version init: invalid option -- - usage: init 0123456ssqqaabbccuu выводытак что, похоже, нет единственного способа сделать это, но вы могли бы сформулировать набор проверок, которые бы точно указывали, какую систему инициализации вы используете с достаточно высокой степенью достоверности.

не так эффективно, но я, кажется, работает.<pre class="lang-bash prettyprint-override">strings /sbin/init | grep -q "/lib/systemd" && echo systemd strings /sbin/init | grep -q "sysvinit" && echo sysvinit strings /sbin/init | grep -q "upstart" && echo upstart он напечатает больше строк, если будет найдено более одной строки, что можно перевести как "не могу угадать". строки, используемые в grep, могут быть немного изменены, но при тестировании в следующей ос я всегда получал одну строку. rhel 6.4 [upstart] rhel es 4 (обновление nahant 7) [sysvinit] ubuntu 16.04.1 lts [systemd] ubuntu 14.04.2 lts [upstart] fedora релиз 23 (онлайн оболочка) [systemd] debian gnu/linux 7 (онлайн-оболочка) [systemd] centos 7,6 (vm) [systemd] более упрощенный подход к тому же решению (но он останавливается при первом совпадении)<pre class="lang-bash prettyprint-override">strings /sbin/init | awk 'match($0, /(upstart|systemd|sysvinit)/) { print toupper(substr($0, rstart, rlength));exit; }' 

иногда это так же просто, как использовать ls:$ ls -l /sbin/init lrwxrwxrwx 1 root root 20 juin 25 12:04 /sbin/init -> /lib/systemd/systemd </code></pre><p>я думаю, если <code>/sbin/init</code> не является символической ссылкой, вы должны проверить дальнейшие предложения в других ответах.</p>

<p>у меня тоже была такая же проблема, и я провел много тестов на некоторых машинах redhat/centos/debian/ubuntu/mint. это то, что я закончил, с хорошими результатами.</p><ol> <li> <p>найдите имя исполняемого файла с pid 1:</p> <pre><code>ps -p 1 </code></pre> <p>если это systemd или upstart, проблема решена. если это "init", это может быть символическая ссылка или что-то иное, чем предварительное имя. преуспевать.</p> </li> <li> <p>найдите реальный путь к исполняемому файлу (работает только как root):</p> <pre><code>ls -l `which init` </code></pre> <p>если <code>init</code> является символической ссылкой на upstart или systemd, проблема решена. в противном случае, <strong>почти</strong> уверен, что у вас есть sysv init. но это может быть ошибочно названный исполняемый файл. преуспевать.</p> </li> <li> <p>найдите пакет, который предоставляет исполняемый файл. к сожалению, это зависит от дистрибутива:</p> <pre><code>dpkg-query -s (executable real path) # debian rpm -qf (executable real path) # redhat </code></pre></li> </ol><p>затем, если вы хотите написать это (самая забавная часть, имхо), это мои однострочники (запускаются с правами root):</p><pre><code>ls -l $(which $(ps -p 1 o comm)) | awk '{ system("dpkg-query -s "$nf) }' # debian ls -l $(which $(ps -p 1 o comm)) | awk '{ system("rpm -qf "$nf) }' # redhat </code></pre>

<ol> <li> <p>для этого предназначены специальные дистрибутивные пакеты. правильная установка программного обеспечения - это гораздо больше, чем просто обнаружение системы инициализации. многие дистрибутивы используют sysvinit, но не все пишут свои сценарии инициализации одинаково. правильный способ решить эту проблему - включить все различные варианты, а затем объединить их, используя файлы спецификаций с именами зависимых от дистрибутивов зависимостей для дистрибутивов rpm, файлы deb для систем на основе apt и т.д. почти во всех дистрибутивах есть какая-то спецификация пакета, которую вы можно написать, что включает в себя зависимости, сценарии, сценарии инициализации и т. д. не изобретайте колесо здесь.</p> </li> <li> <p>нет. что возвращает нас к 1. если вам нужен bash, это должна быть зависимость. вы можете указать эту проверку как часть ваших скриптов configure, но она также должна быть в описании пакета.</p> </li> </ol><p><strong>edit:</strong> используйте флаги в вашем скрипте конфигурации, такие как <code>--with upstart</code> или <code>--without sysvinit</code>. выберите разумное значение по умолчанию, тогда сценарии, которые упаковывают ваше программное обеспечение для других дистрибутивов, могут выбрать запуск с другими параметрами.</p>

<p>также может помочь проверка файловых дескрипторов. и это из фактического запуска init (debian stretch в настоящее время позволяет устанавливать больше систем инициализации) :-)</p><pre><code>$ ls -l /proc/1/fd |grep systemd lrwx------ 1 root root 64 srp 14 13:56 25 -> /run/systemd/initctl/fifo lr-x------ 1 root root 64 srp 14 13:56 6 -> /sys/fs/cgroup/systemd $ ls -l /proc/1/fd |grep /run/initctl # sysvinit lrwx------ 1 root root 64 srp 14 14:04 10 -> /run/initctl $ ls -l /proc/1/fd |grep upstart l-wx------ 1 root root 64 srp 13 16:09 13 -> /var/log/upstart/mysql.log.1 (delete l-wx------ 1 root root 64 srp 13 16:09 9 -> /var/log/upstart/dbus.log.1 (deleted) $ ls -l /proc/1/fd # busybox total 0 lrwx------ 1 root root 64 jan 1 00:00 0 -> /dev/console lrwx------ 1 root root 64 jan 1 00:00 1 -> /dev/console lrwx------ 1 root root 64 jan 1 00:00 2 -> /dev/console </code></pre><p>возможно, более безопасный способ проверить занятость - это <code>check /proc/1/exe</code>, поскольку busybox обычно использует символические ссылки:</p><pre><code>$ ls -l /proc/1/exe lrwxrwxrwx 1 root root 0 jan 1 00:00 /proc/1/exe -> /bin/busybox так что проверка может быть:{ ls -l /proc/1/fd |grep -q systemd && echo "init: systemd"; } ||  { ls -l /proc/1/fd |grep -q /run/initctl && echo "init: sysvinit"; } ||  { ls -l /proc/1/fd |grep -q upstart && echo "init: upstart"; } ||  { ls -l /proc/1/exe |grep -q busybox && echo "init: busybox"; } ||  echo "unknown init" 

простой переход к процессу с pid 1 скажет вам:strings /proc/1/exe |grep -q sysvinit strings /proc/1/exe |grep -q systemd 

не знаю о других системах, кроме debian (wheezy)/или ubuntu (14.10.), но я тестирую такие проблемы с помощью простой старой команды file.file /sbin/init дать это:/sbin/init: symbolic link to 'upstart' системы debian с systemd (например, sid) показывают это:# file /sbin/init /sbin/init: symbolic link to /lib/systemd/systemd 

на debian/sbin/init есть символическая ссылка на ваш init по умолчанию, такls -l /sbin/init даст вам информацию, которую вы ищете.$ ls -l /sbin/init lrwxrwxrwx 1 root root 20 nov 18 13:15 /sbin/init -> /lib/systemd/systemd </code></pre>

<p>в gentoo взгляните на pid 1:</p><pre><code>user pid %cpu %mem vsz rss tty stat start time command root 1 0.0 0.0 4216 340 ? ss 2013 0:57 init [3] </code></pre><p>если это <code>init</code>, то система инициализации <code>openrc</code>. если это <code>systemd</code>, то система инициализации <code>systemd</code>.</p><p>вы можете обнаружить gentoo с помощью <code>[ -f /etc/gentoo-release ]</code>.</p><p>другой метод в gentoo - использовать <code>profile-config show</code>, который покажет, какой профиль по умолчанию используется. все профили, кроме двух, заканчивающихся на/systemd, используют инициализацию openrc. имейте в виду, что они являются только репрезентативными по умолчанию, и вполне возможно, что пользователь предпринял шаги для отмены этого по умолчанию и может не указывать на фактически используемый менеджер инициализации.</p>

<p>это действительно легко для некоторых систем инициализации. для systemd:</p><pre><code>test -d /run/systemd/system </code></pre><p>для выскочки:</p><pre><code>initctl --version | grep -q upstart </code></pre><p>для всего остального, вы можете просто предположить, основываясь на дистрибутиве (запущенный на os x, sysvinit на debian, openrc на gentoo).</p>

<p>для <code>systemd</code>:</p><pre><code>if [[ `systemctl is-system-running` =~ running ]]; then echo using systemd; fi </code></pre>

<p>мое решение: проверьте команду, запущенную как процесс, с id 1.</p><pre><code>case `cat /proc/1/comm` in init) echo init ;; systemd) echo systemd ;; # add here other patterns *) echo "unknown: '`cat /proc/1/comm`'" ;; esac </code></pre><p>на данный момент у меня есть доступ только к компьютерам init и systemd, поэтому я не могу сказать, как будет обнаружен upstart или macos (os x), но я продолжу поиск.</p>

<p>вот скрипт bash для обнаружения. на данный момент он проверяет только upstart и systemd, но его легко расширить. я взял это <a href="https://github.com/displaylink/evdi/issues/11#issuecomment-193877839">из кода, который я внес в скрипт установки драйвера displaylink</a> .</p><pre><code>detect\_distro() { # init process is pid 1 init=`ls -l /proc/1/exe` if [[ $init == "upstart" ]]; then systeminitdaemon=upstart elif [[ $init == *"systemd"* ]]; then systeminitdaemon=systemd elif [[ $init == "/sbin/init" ]]; then init=/sbin/init --version if [[ $init == *"upstart"* ]]; then systeminitdaemon=upstart elif [[ $init == "systemd" ]]; then systeminitdaemon=systemd fi fi if [ -z "$systeminitdaemon" ]; then echo "warning: unknown distribution, assuming defaults - this may fail." >&2 else echo "init system discovered: $systeminitdaemon" fi } 

при тестировании systemd vs initd существует множество ошибок совместимости. на самом деле это работает на opensuse 42.1: ps --pid 1 | grep -q systemd && echo 'systemd' || echo 'init'

как насчет этого:strings $(\ps -p 1 o cmd= | cut -d" " -f1) | egrep -o "upstart|sysvinit|systemd" | head -1 </code></pre><p>проверено только на тех системах, которые у меня есть: ubuntu и sailfishos.</p>

<pre><code>check(){ if hash systemctl 2>/dev/null; then echo "there is systemd" fi if hash initctl 2>/dev/null; then echo "there is upstart" fi if [ -f "/etc/inittab"]; then echo "there is systemv" fi } </code></pre>