Методы автозагрузки приложений в Mac OS X. LaunchAgents и LaunchDaemons.

Ну вот, наконец, я добрался и до второй части этой темы. В прошлый раз я кратко рассказал о самом простом способе автозапуска приложений в Mac OS X. Сегодня же заметка будет в первую очередь касаться продвинутых юзеров (или тех кто хочет таковыми стать) так как речь пойдёт об «агентах» и «демонах» %) Конечно же, в данной заметке я не смогу охватить все возможные варианты, но сделать краткий экскурс в данную тематику просто обязан, так как русскоязычные «яблочно»-ориентированные интернет-ресурсы в последнее время что-то подзабыли, что кроме новостей и слухов есть более интересные и полезные темы для статей 😉

В Mac OS X, как и в других UNIX-подобных операционных системах, есть множество скрытых «рычагов» для запуска, управления и обслуживания различных системных процессов. Одними из таких «рычагов» являются системные службы: Services (сервисы) и Daemons (демоны). По своей сути что «сервисы», что «демоны» – это системные службы обеспечивающие внутреннюю работу всей системы, но предназначены немного для разных целей, ну и работают они тоже немного по-разному. Что же делают эти «сервисы» и «демоны»? – А делают они практически всё, начиная от запуска встроенного веб-сервера и монтирования файловых систем, и кончая запуском графического интерфейса вместе с системными и пользовательскими приложениями. Сами сервисы представляют из себя конфигурационные XML файлы с расширением .plist, находящиеся в директориях LaunchAgents или LaunchDaemons (в зависимости от их предназначения). В системе имеется несколько директорий LaunchAgents и несколько LaunchDaemons, каждая из которых обрабатывается согласно определённых привилегий:

~/Library/LaunchAgents

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

/Library/LaunchAgents

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

/Library/LaunchDaemons

– Это так же системная директория, но все конфиги исполняются от лица администратора (root’a) на уровне системы (то есть сценарий будет исполнен вне зависимости от того, вошёл какой-либо пользователь в систему или нет).

/System/Library/LaunchAgents

– Системная директория зарезервированная OS X. Все конфиги находящиеся в ней являются системными, запускаются при логине любого пользователя.

/System/Library/LaunchDaemons

– Системная директория зарезервированная OS X. Все конфиги, находящиеся в ней, являются системными и запускаются при загрузке системы с правами администратора (root’a).

Нужно понимать назначение директорий LaunchAgents и LaunchDaemons, и разницу между ними. Директории LaunchAgents используются для запуска «сервисов», а LaunchDaemons – для запуска «демонов». Разница между «сервисами» и «демонами» в том, что «сервисы» обычно запускаются после загрузки графического интерфейса и предназначены для запуска графических приложений; «демоны» могут запускаться в фоне до загрузки графического интерфейса и предназначены скорее для загрузки приложений (служб) без графического интерфейса (консольные утилиты, bash и shell скрипты и так далее). Все конфигурационные .plist-файлы обрабатываются и запускаются фоновым процессом launchd во время загрузки ОС. Запущенные процессом launchd системные службы («сервисы» и «демоны») находятся в памяти и работают аж до завершения работы ОС, выполняя при этом указанные в них функции по указанным правилам. Сами по себе конфигурационные .plist-файлы никак нельзя назвать приложениями – это всего лишь «сценарии» для запуска других приложений. И если говорить простым языком, то Services и Daemons – это файлы с набором неких правил, которые система использует от начала своей работы и до её завершения. Чем же интересен сам способ использования этих «правил»? – В первую очередь он интересен своей гибкостью и функциональностью. Я догадываюсь, что для многих сейчас мало что понятно из вышеописанного, но когда я начну разбирать примеры, надеюсь, вам всё стать ясным 😉

Share this article
0
Share
Prev Post

[AppStore] Rays. Лучи света на фотографии.

Next Post

[Киоск] MANSORY LIFESTYLE. Журнал для автомобилистов.

Comments 41
  1. Большое спасибо за отличную статью!
    С удовольствием слежу за вашими постами.

    1. Привет, Эд! Спасибо за отзыв. Раз уж iOS-разработчик так считает, значит не всё так плохо, как мне казалось 🙂

      1. Ну это вы зря, бетенька 🙂
        Честно, очень полезные статейки и хорошо все написано и объяснено.
        Не смотря на то, что я занимаюсь разработкой под iOS, но разница все-таки есть между настольной системой, хоть и не большая. Поэтому с удовольствием заполняю пробелы в знаниях, что бы позже еще писать софт и под Mac OS.
        Желаю творческих успехов! 😉

          1. Клятвенно обещаю, как что-то сделаю под взрослую систему (MacOS), сразу кину на MacDaily!
            Кстати, когда начну разработку ПО под мак, может вместе программку сварганим?

  2. Мне как новичку не понятно о чем речь, но как написано понравилось :). Буду следить за продолжением.

  3. Здравствуйте Александр!

    У меня следующая проблема:
    Мне нужно что бы следующий shell script выполнялся при старте системы:

    echo "Initializing kernel random number generator..."
            # Инициализировать генератор случайных чисел ядра значениями
            # последнего выключения (или запуска системы).  Загрузить и
            # затем сохранить 512 байт, которые составляют пул энтропии.
            if [ -f /var/random-seed ]; then
                    cat /var/random-seed >/dev/urandom
            fi
            dd if=/dev/urandom of=/var/random-seed count=1
    

    Я написал следующий XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    	<dict>
    		<key>Label</key>
    			<string>random-seed</string>
    		<key>ProgramArguments</key>
    			<array>
    				<string>echo "Initializing kernel random number generator..."</string>
    				<string>if</string>
    				<string>[ -f /var/random-seed ];</string>
    				<string>then</string>
    				<string>cat</string>
    				<string>/var/random-seed</string>
    				<string>></string>
    				<string>/dev/urandom</string>
    				<string>fi</string>
    				<string>dd</string>
    				<string>if=/dev/urandom</string>
    				<string>of=/var/random-seed</string>
    				<string>count=1</string>
    			</array>
    		<key>RunAtLoad</key>
    		<true/>
    	</dict>
    </plist>
    

    В логах вижу следующее:

    Jun 15 23:49:38 MacBook com.apple.launchd[1] (random-seed[57]): Job failed to exec(3). Setting up event to tell us when to try again: 2: No such file or directory
    Jun 15 23:49:38 MacBook com.apple.launchd[1] (random-seed[57]): Job failed to exec(3) for weird reason: 2
    Jun 15 23:49:38 MacBook com.apple.launchd[1] (random-seed): Job should be able to exec(3) now.
    

    Что я делаю неправильно? Заранее благодарен за помощь!

  4. <?xml version=”1.0″ encoding=”UTF-8″?>
    <!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
    <plist version=”1.0″>
    <dict>
    <key>Label</key>
    <string>random-seed</string>
    <key>ProgramArguments</key>
    <array>
    <string>echo “Initializing kernel random number generator…”</string>
    <string>if</string>
    <string>[ -f /var/random-seed ];</string>
    <string>then</string>
    <string>cat</string>
    <string>/var/random-seed</string>
    <string>></string>
    <string>/dev/urandom</string>
    <string>fi</string>
    <string>dd</string>
    <string>if=/dev/urandom</string>
    <string>of=/var/random-seed</string>
    <string>count=1</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    </dict>
    </plist>

    1. Ну да, при конвертации shell-скриптов в XML могут возникнуть некоторые непонятки. Можно попробовать вставлять или целостный скрипт, типа:

      <?xml version=»1.0″ encoding=»UTF-8″?>
      <!DOCTYPE plist PUBLIC «-//Apple//DTD PLIST 1.0//EN» «http://www.apple.com/DTDs/PropertyList-1.0.dtd»>
      <plist version=»1.0″>
      <dict>
      <key>Label</key>
      <string>random-seed</string>
      <key>ProgramArguments</key>
      <string>echo "Initializing kernel random number generator..."; if [ -f /var/random-seed ]; then cat /var/random-seed > /dev/urandom; fi; dd of=/var/random-seed count=1;</string>
      <key>RunAtLoad</key>
      <true/>
      </dict>
      </plist>
      

      Или вот так ещё можно попробовать:

      <?xml version=»1.0″ encoding=»UTF-8″?>
      <!DOCTYPE plist PUBLIC «-//Apple//DTD PLIST 1.0//EN» «http://www.apple.com/DTDs/PropertyList-1.0.dtd»>
      <plist version=»1.0″>
      <dict>
      <key>Label</key>
      <string>random-seed</string>
      <key>ProgramArguments</key>
      <array>
      <string>echo "Initializing kernel random number generator...";</string>
      <string>if [ -f /var/random-seed ]; then cat /var/random-seed > /dev/urandom; fi;</string>
      <string>dd of=/var/random-seed of=/var/random-seed count=1</string>
      </array>
      <key>RunAtLoad</key>
      <true/>
      </dict>
      </plist>
      

      Но самое проще (чтоб не манаться с «угадываниями») – создать bash-скрипт, например random-seed:

      #!/bin/bash
      
      echo "Initializing kernel random number generator..."
      
      if [ -f /var/random-seed ]; then
      cat /var/random-seed > /dev/urandom;
      fi
      
      dd if=/dev/urandom of=/var/random-seed count=1
      

      И в XML (.plist) указать к созданному скрипту полный путь:

      <?xml version=»1.0″ encoding=»UTF-8″?>
      <!DOCTYPE plist PUBLIC «-//Apple//DTD PLIST 1.0//EN» «http://www.apple.com/DTDs/PropertyList-1.0.dtd»>
      <plist version=»1.0″>
      <dict>
      <key>Label</key>
      <string>random-seed</string>
      <key>ProgramArguments</key>
      <array>
      <string>/bin/customs_cripts/random-seed</string>
      </array>
      <key>RunAtLoad</key>
      <true/>
      </dict>
      </plist>
      

      Естественно, скрипту random-seed нужно будет присвоит соответствующие права доступа и атрибуты.

      1. Громадное Спасибо, попробовал последний вариант – вроде всё получилось ( в логах поиск строки random-seed ничего не находится – это правильно?)

        И второй вопрос – Вы не знаете как сделать что бы скрипт выполнялся при ВЫХОДЕ из системы?

        Ответе пожалуйста!

        1. Да не за что. Проверить, отрабатывает ли скрипт, можно следующим способом. У вас в скрипте есть строка:

          echo "Initializing kernel random number generator..."
          

          Но так как это echo будет запускаться через launchd то в Консоль ничего выводиться не будет (кроме ошибок). Модифицируйте скрипт таким образом:

          #!/bin/bash
          
          LOGS='RandomSeed'
          (
          echo "Initializing kernel random number generator..."
          
          if [ -f /var/random-seed ]; then
          cat /var/random-seed > /dev/urandom;
          fi
          
          dd if=/dev/urandom of=/var/random-seed count=1
          
          ) | logger -s -t $LOGS
          

          И теперь все echo будут выводиться в Консоль. После перезагрузки запустите консоль и поищите слово «RandomSeed» или строку «Initializing kernel random number generator» в логах. Если они присутствуют – всё ОК, скрипт отрабатывает. Этот финт я использую как своего рода инструмент для отладки 😉

          По поводу выполнения скриптов перед выходом/выключением/перезагрузкой… До версии 10.4 Tiger, и включая её, была очень простая возможность исполнять любое множество криптов, просто поместив их в соответствующую папку. С выходом следующих версий Mac OS X этот принцип изминили, и в текущих 10.7 Lion и 10.8 Mountain Lion для запуска logout-скриптов необходимо использовать хук. Работает это так:
          1. Создадим shell-скрипт с нужными командами, например вот такой:

          #!/bin/bash
          
          LOGS='LogOutHook'
          (
          echo "I'm OS X and I logout now..."
          say "I'm OS X and I logout now..."
          ) | logger -s -t $LOGS
          

          2. Сохраним его, к примеру, в системной папке /sbin под именем logout_hook и назначим ему правильные права доступа и атрибуты:

          sudo chown root:wheel /sbin/logout_hook; chmod 555 /sbin/logout_hook;
          

          3. Теперь добавим хук:

          sudo defaults write com.apple.loginwindow LogoutHook /sbin/logout_hook
          

          Ну и попробуем… Я сам-то не пробовал, но, по идее, всё должно заработать.

              1. Оказывается я рано радовался:
                —————————————–
                Jun 16 22:08:01 MacBook.local loginwindow[343]: *** NSTask: Task create for path ‘/bin/customs_cripts/random-seed_Stop’ failed: 22, “Invalid argument”. Terminating temporary process.
                —————————————–
                random-seed_Stop:
                =======================
                LOGS=’RandomSeedStop’
                (
                echo “RandomSeedStop and I logout now…”
                say -v Milena “Я Random Seed Stop, Я завершаю работу…”
                # Сохранить источник случайности для генератора случайных чисел
                # при завершении работы. Сохранить 512 байт, которые составляют
                # пул энтропии для генератора случайных чисел.
                echo “Saving random seed…”
                dd if=/dev/urandom of=/var/random-seed count=1
                ) | logger -s -t $LOGS
                =======================
                Кто виноват и Что делать???

                  1. Так я же выше его привёл:

                    LOGS=’RandomSeedStop’
                    (
                    echo "RandomSeedStop and I logout now…"
                    say -v Milena "Я Random Seed Stop, Я завершаю работу…"
                    # Сохранить источник случайности для генератора случайных чисел
                    # при завершении работы. Сохранить 512 байт, которые составляют
                    # пул энтропии для генератора случайных чисел.
                    echo "Saving random seed…"
                    dd if=/dev/urandom of=/var/random-seed count=1
                    ) | logger -s -t $LOGS
                    
                    1. Во-первых, где #!/bin/bash вначале?
                      Во-вторых, если голосовые компоненты Milena будут отсутствовать в система – будет ошибка.
                      Лучше сделать так:

                      #!/bin/bash
                      
                      LOGS=’RandomSeedStop’
                      (
                      echo "RandomSeedStop and I logout now…"
                      say "I'm Random Seed Stop, and I end work now..."
                      # Сохранить источник случайности для генератора случайных чисел
                      # при завершении работы. Сохранить 512 байт, которые составляют
                      # пул энтропии для генератора случайных чисел.
                      echo "Saving random seed..."
                      dd if=/dev/urandom of=/var/random-seed count=1
                      ) | logger -s -t $LOGS
                      
  5. Огромное Вам спасибо за помощь и долготерпение!

    Все получилось – виноват был отсутствующий #!/bin/bash

      1. Здравствуйте, это опять я.
        После обновления до 10.9.2 в консоли для RandomSeedStart (см.выше) вот такое сообщение:
        20.03.14 1:33:57,305 locationd[73]: NBB-Could not get UDID for stable refill timing, falling back on random

        Как это исправить?

        1. Приветствую! Я догадался, что это снова вы 😉
          Сложный вопрос… Проверил у себя – у меня всё работает (тоже 10.9.2). Я не совсем понимаю суть фразы «NBB-Could not get UDID for stable refill timing, falling back on random», но, по-моему, там идётся речь о том, что не получается получить UDID за определённый (выделенный) промежуток времени.
          Попробуйте для начала при помощи Дисковой утилиты сделать проверку/восстановления диска (не прав доступа, а именно диска): Дисковая утилита → выбираете весь накопитель целиком → Первая помощьИсправить диск. Потом сделайте то же самое для системного раздела (Macintosh HD). Для проверки/восстановления системного раздела, скорее всего, придётся загрузиться с Recovery HD. Отпишитесь, пожалуйста, о результатах 😉

            1. Так это всего-лишь файл для автозапуска скрипта. У вас, судя по всему, скрипт отрабатывает правильно, проблема не в автозапуске, и не в скрипте. Проблема в том, что утилита dd не может вытянуть UDID из /dev/urandom, который, скорее всего, используется для генерации.

  6. Подскажите пожалуйста, если сталкивались. Есть сервер слушающий порт принимающий запросы и отправляющий ответы. При простом запуске под пользователем работает всегда без проблем. Прекрасно запускается в виде демона из любого каталога при старте системы. Диагностика показывает что все работает как надо, порт открыт. По безопасности порт открыт, но принимать соединения отказывается. Клиент не видит его. В нормальный рабочий режим переходит только если его снять и его снова поднимет launchd. Дальше проблем больше не возникает. В plist’e прописывал sockets по мануалу, но тогда клиенты зависают намертво, а сервер соединения все равно не видит и не принимает. Перезапуск в данном поведении ничего не меняет.

    Буду признателен за любую наводку так как в сети ничего найти пока не получилось. Да и применение Sockets разделов практически не рассматривается. Но точно знаю что есть ограничения по портам. Но опять же, какие…

  7. Доброго дня, подскажите что это за процесс, как его убить, RunOuc , в поиске не могу прогуглить, когда он запущен “кушает 90%” процессора, mac book air ^ при этом греется сильно, и батарея быстро садиться, после перезагрузки на какое то время пропадает, а потом может опять появиться…
    Спасибо заранее…

    1. Приветствую! Нужно смотреть. Это процесс автозагрузки (по моему). То есть что-то после старта системы должно было запуститься, но оно зависло и «кушает» проц.

  8. Добрый день. У меня выскочила вот такая ошибка. Делал действия на странице 2.
    2014-09-25 22:31:19.259 Net Monitor[629:507] *** WARNING: -[NSImage compositeToPoint:operation:] is deprecated in MacOSX 10.8 and later. Please use -[NSImage drawAtPoint:fromRect:operation:fraction:] instead.

    Программа не запускатся, и не перезапускается. Скрипт, полная копия вашего со второй страницы, только изменен путь к другому файлу.

  9. Простите что без уточнения. У меня не запускается все в автоматическом режиме. И на данный момент у меня Maveriks 10.9.5

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Read next