Старт программы как сервиса

При работе в операционной системе нужно постоянно запускать разные программы. Следить за их состоянием. Перезапускать упавшее. Существует целый пласт утилит, который решает эту задачу (от простейшего init.d до навороченного svc). Сейчас в Linux стандартом де-факто стал systemd – его используют все современные дистрибутивы. Это – очень короткое и очень простое введение в systemd.

Минимум текста – максимум пользы.

SystemD – это менеджер загрузки. Он получает управление от ядра при старте операционной системы (init), запускает разные сервисы и следит за их состоянием (например – перезапускает упавшие). Идеолог – Леннар Поттеринг из RedHat. Systemd очень своеобразная штука, умеет она довольно много и устройство у нее довольно сложное.

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

Быстрый старт программы Схема

Эта статья – краткое практическое руководство. Теории тут – минимум. Если вас интересует устройство systemd – вам в официальное руководство.

Введение и терминология

Systemd отвечает за запуск программ и сервисов после старта операционной системы. Он следит за процессом загрузки, решает что запустить и как. Строго говоря, он делает ровно то же самое, что делал SystemV init. Systemd писали значительно позже init – в нем пытались починить вещи, которые в init сделаны плохо. Например, в init нет возможности сделать зависимость запуска одного сервиса от другого.

Так же init не поддерживает параллельной загрузки. Параллельная загрузка сервисов радикально ускоряет старт операционной системы.

Unit

Unit – это описание сервиса (в широком смысле этого слова). Unit-файл описывает все настройки сервиса, как его запускать, когда (очередность, зависимости) и что делать, если запуск не удался. Unit-ы, которые пишет пользователь руками – должны находится в /etc/systemd/system и иметь окончание .service в названии. Юниты, которые устанавливают пакеты – находятся в ином месте. Если в нескольких папках лежит юнит с одним и тем же именем – применяется тот, что лежит в /etc/systemd/system . Пример юнита:

[Unit] Description=etcd – highly-available key value store Documentation=https://github.com/coreos/etcd Documentation=man:etcd After=network.target Wants=network-online.target [Service] Environment=DAEMON_ARGS= Environment=ETCD_NAME=%H EnvironmentFile=-/etc/default/%p WorkingDir=/var/lib/etcd Type=notify User=etcd PermissionsStartOnly=true ExecStart=/usr/bin/etcd $DAEMON_ARGS Restart=on-abnormal RestartSec=10s LimitNOFILE=65536 [Install] WantedBy=multi-user.target

Я специально взял юнит посложнее, чтобы пример был наглядным. На что обратить внимание:

  • Description – человеко-читаемое описание. Показывается по команде service status
  • After – начать загрузку после того, как начнется загрузка сервиса (или цели)
  • Wants – опциональная зависимость. Подробнее ниже, в разделе про зависимости
  • Environment – создать переменную окружения при запуске этого сервиса
  • WorkingDir – демон запускается из этой папки. Аналогично cd /var/lib/etcd перед запуском
  • Type – тип сервиса. Подробнее ниже
  • User – имя пользователя, от которого будет запущен сервис
  • PermissionsStartOnly – используется, если перед стартом нужна какая-то специальная подготовка – создание папок, изменение прав и так далее. При PermissionsStartOnly=true эти действия будут выполнятся от root. Без – от имени User
  • ExecStart – что, собственно, запускать. Обязательно полный путь
  • RestartOn – при каких условиях перезапускать
  • WantedBy – в какой target должен быть установлен сервис. Подробнее – в разделе про target-ы

Виды Unit-ов

Как собрать команду и заработать на старте любого проекта, сервиса или продукта! Практикум.

Systemd может обслуживать процессы с разным поведением. Тип описывает, как systemd будет с ним взаимодействовать. Есть следующие варианты:

  • Type=Simple – самый стандартный тип. Процесс остается в foreground, stdout перехватывается systemd. Это тип по умолчанию.
  • Type=Forking – прямая противоположность. Процесс должен форкнуться и отсоединится от foreground. Для этого типа юнитов должен быть указан pid через директиву PIDFile .
  • Type=oneshot – процесс, который успешно выполняется (не делая fork) и завершается. Пример – монтирование файловых систем. Рекомендуется добавить RemainAfterExit=yes в юнит, чтобы результаты работы процесса остался в статусе юнита.
  • Type=notify – аналог simple, но в этом случае сам процесс сообщит systemd о том, что он закончил загрузку и готов к работе.

Взаимодействие с unit-ами

После каждого изменения файла юнита (создание/изменение/удаление) – нужно перечитывать изменения, так как состояния юнитов systemd кеширует:

systemctl daemon-reload

Запус, состояние, остановка:

#запуск systemctl start [unit] service [unit] start #состояние systemctl status [unit] service [unit] status #остановка systemctl stop [unit] service [unit] stop #сервис в автозагрузку systemctl enable [unit] #полностью запретить запуск сервиса (даже команда service [unit] start не поможет) systemctl mask [unit] #разрешить запуск обратно systemctl unmask [unit]

Systemd имеет свою собственную реализацию логирования (хотя по умолчанию в syslog копию сообщения он тоже отправляет). Чтение сообщений от сервисов – командой journalctl. Команда очень мощная, умеет много. Ниже примеры

#чтение информации по юниту journalctl -u [UNIT] #чтение по PID journalctl _PID=12 #аналогично по конкретному файлу journalctl /usr/bin/atd #чтение информаций о юнитах, завершившихся с ошибкой journalctl -xn #чтение журнала с момента загрузки journalctl -b #чтение журнала с определенного момента journalctl —since=»2018-01-24 10:15:10″ journalctl —since «10 minutes ago» #постоянное отслеживание событий (аналог tail -f) journalctl -f #по умолчанию systemd обрезает строки по длине экрана. Запретим ему это: journalctl -l #фильтры можно комбинировать journalctl -u redis -f -l —since «10 minutes ago»
Управление зависимостями, очередность загрузки юнитов

Для управления зависимостями в unit есть ключевые слова Wants , Requires и After :

  • After – сервис начнет загрузку после того, как начнет загружаться сервис, указанный в After .
  • Wants – сервис начнет загрузку после того, как закончит загружаться сервис, указанный в Wants . Статус загрузки этого сервиса не важен – даже если он упал и загрузится не смог – юнит попытается стартовать. То есть зависимость эта опциональная, и нужна она только для того, чтобы наш сервис начал загружаться не раньше, чем другой – закончит.
  • Requires – сервис начнет загрузку после того, как сервис, указанный в Requires закончит загрузку успешно. Если сервис-зависимость загрузится не смог – наш сервис так же упадет с ошибкой (точнее – он даже не будет стартовать).

Targets

Target – целевое состояние системы. Именно Target определяет, какие сервисы будут загружены и в каком порядке. Аналог из мира sysV init – runlevel. Основные виды таргетов:

  • poweroff – отключение системы
  • rescue – режим восстановления, однопользовательский (init 1)
  • multi-user – сетевой режим без графической оболочки, (init 3)
  • graphical – сетевой режим с графической оболочкой (init 5)
  • reboot – перезагрузка
  • emergency – аварийная командная строка, минимальный функционал
Читайте также:
Показатели эффективности программы обучения

Цели могут наследоваться друг от друга. Например, graphical включает в себя загрузку всего, что есть multiuser + после этого – подгрузку графической оболочки.

Взаимодействие с целями:

#список целей systemctl list-units —type=target #перейти в нужную цель (например – загрузится из сетевого режима в графический) systemctl isolate graphical.target #выбрать target по умолчанию systemctl set-default multi-user.target
Заключение

systemd на данный момент — стандарт в linux-based операционных системах. Инструмент мощный, удобный и популярный, пусть и не без особенностей. Надеюсь, эта статья поможет начать им пользоваться.

Источник: prudnitskiy.pro

Как создать свой сервис для Linux

linux

Несмотря на большое количество проблем и ненависть некоторых пользователей, systemd уже стал стандартом де-факто во многих дистрибутивах Linux. С его помощью из десяток строк настроек можно за несколько минут создать несложный процесс . В то же время многие более интересные возможности документированы не очень понятным языком или требуют углубленных познаний в тонкостях работы systemd.

Основы создания сервиса Linux

Если вы еще никогда не делали свои сервисы, начнем с основ. Systemd оперирует абстрактными единицами (unit), которые бывают разных типов, могут предоставлять различные ресурсы (процессы, сокеты, абстрактные «цели») и требовать других ресурсов для запуска.

Самый распространенный вид ресурса — сервис (service). Файлы с описаниями сервисов и всего прочего лежат в каталоге / lib / systemd / system / . Чтобы systemd нашел новый сервис, достаточно положить в этот каталог свой файл. Если этот сервис ранее не существовал, systemd прочитает файл и загрузит его в память. Однако, если вы редактируете файл ранее запущенного сервиса, не забудьте заставить systemd перечитать файлы командой sudo systemctl daemon — reload !

Сервисы типа oneshot — долой rc.local

Когда-то основным способом добавить выполнение команд в загрузку системы было дописать их в /etc/rc.local. Очевидный недостаток — нет способов следить, насколько успешно они выполнились. В systemd легко создать для такой цели свой сервис типа oneshot, и им можно будет управлять через systemctl, как любым другим. В этом случае systemd выполнит команду и посчитает запуск сервиса успешным, если она завершилась с кодом ноль.

Сохраним следующий файл в / lib / systemd / system / dumb — test . service :

Description = Dumb test
ExecStart =/ bin / true
Type = oneshot
WantedBy = multiuser . target

Дополнительных действий не требуется, и теперь вы можеыр делать с ним все то же, что с системными сервисами: запустить с помощью sudo systemctl start dumb — test . service , поставить на загрузку с помощью sudo systemctl enable dumb — test . service и так далее.

Создание сервиса из любой программы

Любой долгоживущий процесс можно легко превратить в сервис с помощью опции Type = idle . В этом случае systemd перехватит стандартные потоки ввода-вывода и будет следить за жизнью процесса.

Для демонстрации напишем программу на Python, которая просто выводит сообщение в бесконечном цикле. Сохраним в / usr / local / bin / test . py следующее:

import time
while True :
print ( «Test service is alive» )
time . sleep ( 5 )

Затем создадим для нее файл сервиса в / lib / systemd / system / smart — test . service :

Description = Smart test
ExecStart =/ usr / bin / python3 — u / usr / local / bin / test . py
Type = idle
KillMode = process
SyslogIdentifier = smart — test

SyslogFacility = daemon
Restart = on — failure
WantedBy = multiuser . target

Теперь можно запустить наш сервис и убедиться, что он работает:

$ sudo systemctl status smart — test . service
smart — test . service — Smart test

Loaded : loaded ( / usr / lib / systemd / system / smart — test . service ; static ; vendor preset : disabled )

Active : active ( running ) since Fri 2019 — 10 — 25 16 : 25 : 18 + 07 ; 1s ago
Main PID : 19893 ( python3 )
Tasks : 1 ( limit : 4915 )
Memory : 3.5M
CGroup : / system . slice / smart — test . service
└─ 19893 / usr / bin / python3 — u / usr / local / bin / test . py

Стандартный вывод программы пишется в journald, и его можно увидеть в journalctl — u smart — test . Ради интереса посмотрим на работу опции Restart = on — failure . Остановим наш процесс с помощью kill — 9 $ < Main PID >и заглянем в логи:

systemd [ 1 ] : Started Smart test .
Test service is alive
Test service is alive
smart — test . service : Main process exited , code = killed , status = 9 / KILL
smart — test . service : Failed with result ‘signal’ .
smart — test . service : Service RestartSec = 100ms expired , scheduling restart .
smart — test . service : Scheduled restart job , restart counter is at 1.
Stopped Smart test .
Started Smart test .
Test service is alive

Для настоящих демонов нужно использовать тип forking , но мы не будем вдаваться в детали — авторы таких пакетов наверняка все уже знают сами.

Зависимости и порядок запуска

Опций для настройки зависимости в systemd очень много. Прежде всего нужно отметить, что в нем есть два независимых механизма для указания порядка запуска сервисов и зависимостей между ними.

Порядок запуска сервисов

Порядок запуска сервисов определяется опциями Before и After . Если в настройках сервиса foo написано After = bar . service и оба сервиса должны запуститься, то systemd сначала выполнит попытку запустить bar, а затем foo.

Однако опция After = bar . service сама по себе не поставит сервис на загрузку. Более того, она никак не повлияет на решение запускать foo, даже если запуск bar завершится неудачей.

Причина существования этих опций — способность systemd запускать сервисы параллельно.

Для примера возьмем типичный веб-сервер с набором из веб-приложения FCGI, СУБД и обратного прокси. В каком порядке запускать процесс FCGI и обратный прокси, не так важно. Запросы будут работать, только когда они оба запущены, но «неверный порядок» никак не помешает им запуститься.

Если веб-приложение требует данных из базы для инициализации, то мало убедиться, что и процесс FCGI, и СУБД запущены, — приложение нужно запускать только после полного запуска СУБД. Именно для этих случаев и предназначены опции Before / After .

Зависимости

Зависимости бывают двух видов: мягкие и жесткие. Если оба сервиса запустились успешно, то никакой разницы между ними нет. Различие вступает в действие, если один из сервисов не смог запуститься: если зависимость мягкая, то зависимые сервисы все равно будут запущены, а если жесткая, то systemd не станет даже пробовать их запустить.

Мягкие зависимости указываются с помощью опции Wants = в секции [ Unit ] . Пример из sshd . service :

Description = OpenSSH server daemon
Documentation = man : sshd ( 8 ) man : sshd_config ( 5 )
After = network . target sshd — keygen . target
Wants = sshd — keygen . target

Цель sshd — keygen . target , очевидно, генерирует ключ хоста, если он отсутствует. Технически sshd не сможет запуститься без ключа хоста, поэтому, почему авторы решили сделать зависимость мягкой, можно только догадываться. Возможно, они посчитали, что в большинстве случаев ключ уже существует и устаревший ключ лучше неработающего SSH.

При копировании настроек из пакетов дистрибутива нужно быть осторожным. Разработчики дистрибутивов тоже люди и вполне могут создавать неоптимальные или ошибочные конфигурации. Кроме того, если что-то работает для одного сервиса, совсем не факт, что оно же подойдет для другого, так что сверяйтесь с документацией и тестируйте перед выпуском.

Жесткие зависимости можно указать с помощью опции Requires . К примеру, в systemd — journal — flush . service есть опция Requires = systemd — journald . service — очевидно, отправить команду journald невозможно, пока он не запущен.

Читайте также:
Что делать когда нет классных программ

У этой опции существуют вариации, например RequiresMountsFor . Посмотрим в файл logrotate . service :

Description = Rotate log files
Documentation = man : logrotate ( 8 ) man : logrotate . conf ( 5 )
RequiresMountsFor =/ var / log

Для работы logrotate нужен доступ к каталогу с логами и больше ничего. Опция RequiresMountsFor =/ var / log позволяет выразить именно это: сервис запустится, как только будет примонтирован каталог, содержащий путь / var / log , даже если он находится не в корневом разделе.

Внедрение в зависимости к чужим сервисам

В системах с System V init добавить что-то в зависимости к чужому сервису можно было, лишь отредактировав его скрипт. Такие изменения, очевидно, не переживут обновления системы, поэтому сделать их постоянными можно было бы только пересборкой пакета.

В systemd есть несколько способов решить эту проблему. Если нужно именно внедриться к кому-то в зависимости, можно попробовать опции обратных зависимостей: WantedBy и RequiredBy . Они должны находиться в секции [ Install ] , а не [ Unit ] . Подводных камня здесь два: они обрабатываются только при установке сервиса с помощью systemctl enable и, как все в systemd, не всегда нормально работают во всех версиях.

Второй вариант, который позволяет менять любые настройки: скопировать файл сервиса в / etc / systemd / system / . Если один файл присутствует в обоих каталогах, то файл из / etc / systemd / system имеет приоритет.

Третий, менее радикальный вариант: создать файл вида / etc / systemd / system / $ < unit >. d / local . conf и прописать туда только нужные настройки.

Источник: tech-geek.ru

Создание Windows сервисов средствами FPC и Lazarus

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

В Linux и прочих клонах UNIX они называются демонами, в Windows это службы. Хотя название этого класса программ в windows и Unix-подобных системах различно — принцип работы их одинаков. Программа запускается в фоновом режиме и не требует, чтобы оператор совершал при этом какие-либо действия.

Компилятор FPC имеет в своём составе прекрасные библиотеки для облегчения создания таких программ. IDE Lazarus с помощью своих визуальных средств — большое подспорье в этом интересном деле.

Процесс создания в общем не зависит от операционной системы, полностью проявляя главный девиз Lazarus: «Пишем код один раз — компилируем везде».

2. Общая настройка Lazarus

Сначала подготовим Lazarus к работе — этот этап общий для любой операционной системы, где он запускается.

Для этого необходимо установить в Lazarus дополнительные компоненты для создания сервисов. То есть необходимо открыть пакет lazdaemon.lpk и установить его. Он находится в каталоге стандартных компонентов — $lazarus/components/daemon ($lazarus — это та папка, куда установлен IDE lazarus). После успешной компиляции и перезапуска IDE в меню создания нового проекта (меню «Проект/Создать новый проект») будет доступен новый тип проекта (Daemon (service) Application:

А в меню «Файл/Создать. » будет доступно создание новых объектов (Daemon Application, Daemon Module, Daemon Mapper):

Пункт меню «Файл/Создать. » необходим в том случае, если планируется в одном исполняемом модуле разместить несколько служб.

3. Создание сервиса

3.1. Что происходит при создании проекта?

При создании нового проекта будет открыто автоматически 2 невизуальных формы — класс TDaemon и TDaemonMapper.

TDaemon — непосредственно экземпляр сервиса. Именно этот объект реализует сам сервис (его мы видим в Windows в «Управление компьютерами/Сервисы»).

TDaemonMapper — это компонент, отвечающий за управление работой сервисов. Особенно актуален он в случае включения в один исполняемый модуль нескольких сервисов.

3.2. Описание свойств компоненты TDaemonMapper

Рассмотрим свойства и события, доступные у TDaemonMapper.

Уникальным свойством компоненты TDaemonMapper является DaemonDefs.

property DaemonDefs : TDaemonDefs;

Именно оно определяет, какие сервисы содержит исполняемый модуль, производит регистрацию сервисов в системе. Это свойство — коллекция элементов. Ниже, в п.3.3 подробно рассматривается элемент, который может содержаться в данной коллекции.

Стоит обратить внимание на несколько событий используемой компоненты.

property OnCreate : TNotifyEvent;

Событие, которое возникает в момент запуска исполняемого модуля программы и создания экземпляра объекта TDaemonMapper. Полезно,например, когда необходимо при запуске считать значения некоторых переменных из конфигурационного файла, подготовить какие либо структуры данных и т.п.

property OnDestroy : TNotifyEvent;

Событие, которое возникает в момент завершения работы исполняемого модуля программы и уничтожения экземпляра объекта TDaemonMapper. Используется для «уборки» после работы программы: освобождение ресурсов, закрытие файлов и т.п.

property OnRun : TNotifyEvent;

Событие возникает при запуске на выполнение исполняемого файла с нестандартными параметрами запуска или вообще без параметров. Стандартными считаются 3 параметра командной строки: -i, -r, -u (подробнее параметры расписаны в Приложение А. Стандартные параметры командной строки)

property OnInstall : TNotifyEvent;
Событие возникает в момент регистрации сервисов в системе — запуск программы с ключом -i.
property OnUnInstall : TNotifyEvent;

Событие возникает в момент удаления регистрационной информации о сервисе из системы — запуск программы с ключом -u.

3.3. Более подробно класс TDaemonDef

Экземпляры данного класса хранятся в свойстве-коллекции DaemonDefs класса TDaemonMapper.

property DaemonClassName : String;

Свойство содержит имя типа регистрируемого сервиса — например TDaemon1. На данном этапе Lazarus не позволяет визуально выбрать значение — пока можно только вписать вручную. Но, я надеюсь, в ближайшее время будет реализован редактор.

property Name : String;
Наименование сервиса
property DisplayName : String;
Наименование сервиса — отображается в окне ОС просмотра списка зарегистрированных сервисов.
property RunArguments : String;

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

property Options : TDaemonOptions;

Параметры работы сервиса — может ли сервис быть остановлен или приостановлен вручную, может ли он взаимодействовать с рабочим столом оператора (см. Приложение В. Параметры сервиса — TDaemonOption)

property Enabled : Boolean;

Признак доступности сервиса. Если это свойство включено, то в момент запуска исполняемого файла с ключом регистрации сервисов (-i), сервис будет зарегистрирован. В противном случае — сервис не регистрируется. Аналогичное поведение и при деинсталяции сервисаов.

Этот параметр удобно менять в момент создания экземпляра TDaemonMapper из его соответствующего события OnCreate.

property WinBindings : TwinBindings;
property Dependencies : TDependencies;
Список служб, от которых зависит данная служба.
property GroupName : String;
Наименование группы, к которой относится служба.

property Password : String;

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

property UserName : String;
Наименование учётной записи, от имени которой производится запуск данной службы.
property StartType : TStartType;

Тип запуска службы — автоматический или ручной. Допустимые значения приведены ниже (см.

Приложение Б. Типы запуска служб).

Читайте также:
Как установить программу ворд на мак

property WaitHint : Integer;

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

property ServiceType : TServiceType;
property LogStatusReport : Boolean;
Писать в файл протокола факт изменения состояния сервиса (запуск / приостановка / остановка)
property OnCreateInstance : TNotifyEvent;
Это событие возникает в момент создания самого экземпляра сервиса.

3.4. Описание компоненты TDaemon

Эта невизуальная компонента является реализацией сервиса.

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

property Status;
Свойство содержит текущее состояние сервиса.
property OnStart : TDaemonOKEvent;
Событие возникает в момент начала выполнения сервиса.
property OnStop : TDaemonOKEvent;
Событие возникает в момент остановки сервиса.
property OnPause : TDaemonOKEvent;
Событие возникает в момент приостановки работы сервиса.
property OnContinue : TDaemonOKEvent;

Событие возникает в момент возобновления работы сервиса.
property OnShutDown : TDaemonEvent;
Событие возникает в момент выгрузки сервиса.
property OnExecute : TDaemonEvent;
Событие возникает в момент начала выполнения сервиса.
property BeforeInstall : TDaemonEvent;
property AfterInstall : TDaemonEvent;
property BeforeUnInstall : TDaemonEvent;
property AfterUnInstall : TDaemonEvent;

События, возникают до и после момента установки и деинсталяции сервиса соответственно.
property OnControlCode : TcustomControlCodeEvent;

Это событие возникает для обработки не стандартного сообщения от системы (не имеющего типового обработчика).

3.5. Пример кода

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

  • OnStart — Старт сервиса. В момент старта сервиса укажем для объекта подключения к базе данных (UIBDataBase1) имя пользователя, пароль и строку подключения. Устанавливаем соединение с базой данных. Затем для таймера (Timer1) установим временной интервал сканирования каталога. Последней операцией — включим таймер в работу.
  • OnStop — Остановка сервиса. Выключаем таймер. Отсоединяемся от базы данных.
  • OnPause — Оператор запрашивает с помощью инструментов операционной системы приостановку работы сервиса. Мы это реализуем просто выключением таймера.
  • OnContinue — Работа сервиса продолжается. Таймер можно включить.

procedure TTestDaemon.Timer1Timer(Sender: TObject); begin try DoScanFolder; if UIBTransaction1.InTransaction then //Если файлы были обнаружены — то транзакция будет запущена — //комитем данные в базу UIBTransaction1.Commit; except on E:Exception do begin //Если файлы были обнаружены — то транзакция будет запущена — //откатим всё if UIBTransaction1.InTransaction then UIBTransaction1.RollBack; //Чтобы наш сервис продолжал работать — //сообщение об ошибке запишем в протокол //А саму ошибку — подавим Application.Logger.Log(E.Message); end; end; end; procedure TTestDaemon.DoScanFolder; var R:TSearchRec; Code:integer; begin Code:=FindFirst(FScanFolder+FScanMask, faAnyFile, R); try while Code=0 do begin if (R.Attr and faDirectory) = 0 then begin DoSaveFileNameToBD(R.Name); DeleteFile(FScanFolder+R.Name); end; FindNext(R); end; finally FindClose(R); end; end; procedure TTestDaemon.DoSaveFileNameToBD(AFileName: string); begin if not UIBTransaction1.InTransaction then UIBTransaction1.StartTransaction; UIBQuery1.Params.ByNameAsString[‘sys_log_name’]:=AFileName; UIBQuery1.ExecSQL; end;

Таким образом, функционал всего сервиса обеспечивается 3-мя методами.

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

Метод DoScanFolder выполнит сканирование указанного каталога. Если при этом будут обнаружены файлы удовлетворяющие условию, их названия будут записаны в БД программы с помощью процедуры DoSaveFileNameToBD, а затем, после успешной записи — удалены. Метод DoSaveFileNameToBD просто проверяет была ли начата транзакция в данной итерации, если нет — то открывает её и записывает данные в базу.

3.6. Замечания из личного опыта

  • Использование таймера таит странную на первый взгляд ошибку. Если просто пытаться использовать таймер — то ничего не выйдет. Это обусловлено реализацией кода компонента таймера в Lazarus. Код таймера находится в библиотеках, которые относятся к визуальным виджетам — просто в разных операционных системах механизм таймеров реализован по разному. Из-за этого без подключения модуля Interfaces тут не обойтись. Первый раз я долго голову ломал — что не так?
  • Объект Application служб реализует систему протоколирования работы. Эта удобная вещь таит не очень явную трудность для случая, если в одном исполняемом файле находится несколько сервисов. В случае когда Application.Logger пишет протокол в системный протокол — то всё будет нормально. Но если запись протокола идёт в файл (а именно так по умолчанию всё работает) — то возникнет ошибка при попытке запуска второго и последующих сервисов. Всё дело в том, что Application.Logger открывает файл протокола на запись в монопольном доступе и имя, если не задано явно, будет равно имени исполняемого модуля с расширением .log. В этом случае я просто в момент создания экземпляра TDaemonMapper определяю какой именно сервис запускается и Application.Logger.FileName присваиваю необходимое имя файла протокола для этого сервиса. Получается, что на каждый сервис у меня ведётся отдельный протокол.

4. Специфика для Windows

Рассмотрим создание сервиса с помощью FPC и Lazarus для использования его в OS Windows. Само создание сервисов не отличается от описанного выше. Специфика начинается в момент установки.

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

Весь процесс установки сервиса сводится к запуску исполняемого файла с ключом командной строки -i. После выполнения этой команды сервис зарегистрирует себя в реестре Windows. Можно открыть окно управления компьютером и запустить сервис. Для деинсталляции сервиса необходимо просто запустить сервис с ключом -u. По умолчанию протоколирование ведётся в системный каталог — %SYSTEM_ROOT%system32 .log.

5. Заключение

Данная статья написана с использованием Lazarus версии 0.9.27 (18965) и FPC версии 2.3.1. Но эти же примеры должны заработать и на предыдущих версиях (не очень старых). Лично для меня эта статья была инициирована необходимостью написания сервиса рассылки СМС сообщений. Так что всё проверено опытным путём.

6. Приложение

6.1. Приложение А. Стандартные параметры командной строки

Параметр Длинная форма параметра Описание
-r —run Запуск сервисов на выполнение
-i —install Регистрация сервисов в системе
-u —uninstall Удаление регистрационной информации о сервисе

6.2. Приложение Б. Типы запуска служб

Тип запуска Описание
stBoot Используется только для драйверов устройств. Для обычных служб эквивалентно stAuto.
stSystem Используется только для драйверов устройств. Для обычных служб эквивалентно stAuto.
stAuto Служба будет запущена в момент старта операционной системы.
stManual Запуск данной службы производится вручную либо оператором, либо по требованию другой службы.
stDisabled Запуск данной службы запрещён.

Источник: www.freepascal.ru

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru