
Tenho dois serviços (um reprodutor de vídeo e um daemon de reconhecimento de imagem) que quero iniciar às 9h e terminar às 23h todos os dias (bem como na inicialização, embora o sistema funcione continuamente). Para cada serviço criei um -startup.service
e um arquivo -shutdown.service
. Os serviços são combinados em a daemon-on.target
e a daemon-off.target
que são então acionados por daemon-on.timer
e daemon-off.timer
respectivamente.
Após algumas tentativas e testes, descobri que os temporizadores inicialmente acionam os alvos conforme esperado, mas entram no estado com o horário NEXT definido como n/a.
É muito trabalhoso apenas colocar dois aplicativos em execução com um cronômetro. Tenho certeza de que está faltando algo óbvio e agradeceria qualquer dica!
mpv-startup.service inicia o serviço de player de vídeo:
[Unit]
Description=MPV Video Player Startup
After=xorg.target
Requires=xorg.target
[Service]
Environment=DISPLAY=:0
ExecStart=/usr/bin/python3 /opt/videoplayer/app.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
[Install]
Also=daemon-on.timer
recog-startup.service inicia o serviço de reconhecimento de imagem:
[Unit]
Description=Recog Startup Service
[Service]
Type=simple
WorkingDirectory=/opt/recog
ExecStart=/opt/recog/recog run
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=30
[Install]
Also=daemon-on.timer
mpv-shutdown.service interrompe o serviço do player de vídeo como um comando oneshot
[Unit]
Description=MPV Video Player Shutdown
[Service]
Type=oneshot
ExecStart=/bin/systemctl --user stop mpv-startup.service
[Install]
Also=daemon-off.timer
recog-shutdown.service interrompe o serviço de reconhecimento de imagem como um comando oneshot:
[Unit]
Description=Recog Shutdown Service
[Service]
Type=oneshot
ExecStart=/bin/systemctl --user stop recog-startup.service
[Install]
Also=daemon-off.timer
daemon-on.target combina dois serviços de inicialização acima e também é habilitado na inicialização com default.target:
[Unit]
Description=Daemon Startup Target
Wants=recog-startup.service mpv-startup.service
After=recog-startup.service mpv-startup.service
[Service]
Type=oneshot
[Install]
WantedBy=default.target
Also=daemon-on.timer
daemon-off.target combina dois serviços de desligamento acima:
[Unit]
Description=Daemon Shutdown Target
Wants=recog-shutdown.service mpv-shutdown.service
After=recog-shutdown.service mpv-shutdown.service
[Service]
Type=oneshot
[Install]
Also=daemon-off.timer
daemon-on.timer está configurado para disparar para acionar daemon-on.target às 9h:
[Unit]
Description=Daemon Startup Schedule
[Timer]
OnCalendar=9:00
Unit=recog-on.target
Persistent=true
[Install]
WantedBy=timers.target
daemon-off.timer está configurado para acionar daemon-off.target às 23h:
[Unit]
Description=Daemon Shutdown Schedule
[Timer]
OnCalendar=23:00
Unit=daemon-off.target
Persistent=true
[Install]
WantedBy=timers.target
Depois de colocar tudo isso em ~/.config/systemd/user/
eu os habilito da seguinte maneira:
systemctl --user enable --now daemon-on.target
systemctl --user enable --now daemon-on.timer
systemctl --user enable --now daemon-off.timer
O que iniciará automaticamente o daemon a cada inicialização e aqui espero que os temporizadores iniciem/parem o daemon de acordo com a OnCalendar=
opção. Neste ponto, systemctl --user list-timers
me mostra que meus dois cronômetros serão acionados nos momentos certos (observe que eu configurei os cronômetros com apenas 3 minutos de intervalo para economizar tempo de espera):
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2019-06-26 12:12:00 JST 1min 4s left Wed 2019-06-26 11:35:02 JST 35min ago daemon-off.timer daemon-off.target
Wed 2019-06-26 12:15:00 JST 4min 4s left Wed 2019-06-26 11:40:48 JST 30min ago daemon-on.timer daemon-on.target
O cronômetro de desligamento é então acionado e meu daemon para, após alguns minutos o cronômetro de ativação é acionado e o daemon inicia conforme previsto. No entanto, olhando novamente para os temporizadores, vejo que eles foram redefinidos e os campos NEXT/LEFT estão definidos como n/a e nunca mais acionarão o daemon. Qual é o problema aqui?
NEXT LEFT LAST PASSED UNIT ACTIVATES
n/a n/a Wed 2019-06-26 12:12:06 JST 4min 18s ago daemon-off.timer daemon-off.target
n/a n/a Wed 2019-06-26 12:15:33 JST 50s ago daemon-on.timer daemon-on.target
Responder1
Depois de pesquisar um pouco mais e ler as páginas de manual do systemd consegui simplificar um pouco as coisas e também obter o comportamento desejado dos meus serviços.
Em primeiro lugar, cometi erros ao colocar seções [Serviço] em meus .target
arquivos, o que não fazia sentido. Em segundo lugar, descobri uma PartOf=
declaração que me permitiu fazer uma aplicação de nível superior com reprodutor de vídeo e serviços de reconhecimento como componentes. E por último, usando Conflicts=
a opção eu poderia iniciar/parar meu aplicativo usando dois alvos que entram em conflito entre si. Os .timer
s anexados a esses alvos alternarão entre si, desabilitando ou habilitando minha cadeia de aplicativos e respectivos arquivos .targets
. O resultado ainda está contido em 7 arquivos, o que é mais do que eu gostaria de manter, mas funciona conforme necessário.
app.service é o aplicativo de nível superior, embora seja um manequim, ele pode executar outra coisa:
[Unit]
Description=App Service
[Service]
Type=oneshot
ExecStart=/bin/true
RemainAfterExit=yes
[Install]
WantedBy=default.target
Also=app-on.timer app-off.timer
app-mpv.service é o componente reprodutor de vídeo do aplicativo e, portanto, também depende do xorg:
[Unit]
Description=App Video Player Service
PartOf=app.service
After=app.service
Requires=xorg.target
After=xorg.target
[Service]
Environment=DISPLAY=:0
ExecStart=/usr/bin/python3 /opt/videoplayer/app.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
[Install]
WantedBy=app.service
app-recog.service é o componente de reconhecimento de imagem do aplicativo e não depende do player de vídeo:
[Unit]
Description=App Recognition Service
PartOf=app.service
After=app.service
[Service]
WorkingDirectory=/opt/recog
ExecStart=/opt/recog/recog run
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=30
[Install]
WantedBy=app.service
app-on.target é um alvo virtual, ele se torna ativo assim que o app-on.timer é acionado e, por sua vez, habilita o app.service:
[Unit]
Description=App Startup Target
Conflicts=app-off.target
Wants=app.service
After=app.service
RefuseManualStart=yes
[Install]
Also=app-on.timer
app-off.target é um alvo virtual, ele se torna ativo assim que o app-off.timer é acionado e, por sua vez, desativa o app.service:
[Unit]
Description=App Shutdown Target
Conflicts=app.service app-on.target
RefuseManualStart=yes
[Install]
Also=app-off.timer
app-on.timer simplesmente aciona app-on.target:
[Unit]
Description=App Startup Schedule
[Timer]
OnCalendar=9:00
Unit=app-on.target
Persistent=true
[Install]
WantedBy=timers.target
app-off.timer simplesmente aciona app-off.target:
[Unit]
Description=App Shutdown Schedule
[Timer]
OnCalendar=23:00
Unit=app-off.target
Persistent=true
[Install]
WantedBy=timers.target
Possibilitando toda essa bagunça (com a ajuda deEste artigo):
systemctl --user enable app app-mpv app-recog
systemctl --user enable --now app-on.timer app-off.timer
systemctl --user start app
A aplicação começa a rodar e se eu verificar os timers, ambos estão ativos e o que ocorrer primeiro será executado primeiro:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2019-06-26 14:00:00 JST 1min 25s left n/a n/a app-off.timer app-off.target
Wed 2019-06-26 14:01:00 JST 2min 25s left n/a n/a app-on.timer app-on.target
Depois que o cronômetro desligado é acionado, ele interrompe o aplicativo e o app-off.target
alvo permanece agendado:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2019-06-26 14:01:00 JST 55s left n/a n/a app-on.timer app-on.target
n/a n/a Wed 2019-06-26 14:00:04 JST 3ms ago app-off.timer app-off.target
Quando o temporizador ligado é acionado, ele inicia o aplicativo e desativa o app-on.target
, mas reativa o temporizador desligado novamente:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Thu 2019-06-27 14:00:00 JST 23h left Wed 2019-06-26 14:00:04 JST 57s ago app-off.timer app-off.target
n/a n/a Wed 2019-06-26 14:01:01 JST 5ms ago app-on.timer app-on.target
E o ciclo continua. Ainda duvido que esta seja a melhor forma de configurar isso e adoraria ouvir outras sugestões!