Jak spustit script před vypnutím/rebotováním PC (OS se systemd)

Potřeboval bych poradit se systemd. Non-root uživatelé si sami spouští "/bin/programX". Do "programX" se musí před vypnutím/rebootem PC poslat určitý příkaz, aby se ukončil korektně. To dělá script "stop.sh", který najde všechny běžící procesy "programX" a pošle do nich daný příkaz pro korektní ukončení. Nevím jak to udělat, aby se "stop.sh" spouštěl před vypnutím/rebotováním PC, ale ještě ve chvíli kdy běží "programX".

Co jsem zkoušel:
1.) Udělat z "programX" službu systemd - to funguje, ale je to pro mě nepraktické.
2.) Vytvořil jsem službu podle tohoto návodu
Kód: [Vybrat]
[Unit]
Description=Something

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/true
ExecStop=/bin/stop.sh

[Install]
WantedBy=multi-user.target
Služba funguje a "stop.sh" se skutečně spustí - bohužel se spouští příliš pozdě, kdy byl "programX" již ukončen.
2b.) Zkoušel jsem přidat "Before=shutdown.target" nebo změnit "WantedBy=default.target", ale nepomohlo.
3.) Umístit "stop.sh" do /usr/lib/systemd/system-shutdown/ - taktéž se spouští až v hodně pozdní fázi kdy programX již neběží.

Podle mě by stačilo u služby z bodu 2 přidat nějaké závislosti (Before=), aby se začala ukončovat v co nejranější fázi vypínání/rebootování, ale nevím co tam dát. Případně pokud existuje jiný způsob, tak budu rád za nějakou radu.


Re:Jak spustit script před vypnutím/rebotováním PC (OS se systemd)
« Odpověď #1 kdy: 25. 02. 2019, 13:04:00 »
Uvedenú možnosť som neskúmal. Ja to mám vyriešené cez /etc/gdm/PostSession/Default v ktorom som dopísal:
LOGOFF="/opt/sys/logoff.sh"
if [ -e $LOGOFF ]; then
    $LOGOFF $USER
fi
logoff.sh bude najskôr asi to, čo u teba stop.sh

Re:Jak spustit script před vypnutím/rebotováním PC (OS se systemd)
« Odpověď #2 kdy: 26. 02. 2019, 11:22:37 »
Gnome nepoužívám, ale u mě bych možná taky našel nějakou podobnou událost na kterou to navázat, ale zdá se mi to z principu špatné řešení kvůli přenositelnosti nebo kdybych změnil DE, tak by to přestalo fungovat.

Zkoušel jsem ještě službu systemd (z bodu 2) spouštět jako uživatelskou s parametrem "--user". Sice se spustila když ještě programX běžel, ale než se "stop.sh" stačil dokončit, tak se počítač vypl. Nepomohlo ani přidání "TimeoutStopSec=120" a "KillMode=none".

Re:Jak spustit script před vypnutím/rebotováním PC (OS se systemd)
« Odpověď #3 kdy: 26. 02. 2019, 12:05:09 »
ahoj,

SystemD ma jednu taku nepeknu vlastnost, ze vsetky user session processy, bezia v user scope pod user.slice. Pri shutdowne a reboote, sa tymto procesom posle SIGTERM. Akakolvek unita ktoru spravis, tak sa spusti pravdepodobne neskor ako systemd zakilluje tvoj programx.

Riesenie je, ze programx, bude reagovat na sigterm a ukonci sa korektne. 
Napriklad, takto ze uzivatelia nebudu spustat
/usr/bin/programx ale /usr/local/bin/programx ( ak spustaju len programx, tak sa spusti ten, ktory ma vyssiu prioritu v PATH)
a /usr/local/bin/programx bude shellskript asi taky

Kód: [Vybrat]
#!/bin/bash
exit_handler(){
   PID=$1
  # do whatever you want
}

/usr/bin/programx &
trap "exit_handler $! arg2 argn" TERM INT
wait
« Poslední změna: 26. 02. 2019, 12:09:15 od mslebodnik »

Re:Jak spustit script před vypnutím/rebotováním PC (OS se systemd)
« Odpověď #4 kdy: 26. 02. 2019, 23:53:49 »
Čau, trap jsem vyzkoušel a při ručním vypínání pomocí "kill -TERM <pid>" fungoval, ale když jsem vypl PC, tak se "exit_handler()" vůbec nespustil. Musel jsem za "TERM INT" přidat ještě HUP (nemám Ubuntu, ale zde je asi vysvětlení: https://askubuntu.com/questions/819730/no-sigterm-before-sigkill-shutdown-with-systemd-on-ubuntu-16-04 ). Po přidání HUP se "exit_handler()" při vypínání PC skutečně spustil, bohužel "stop.sh" se nestihl dokončit (trvá několik desítek sekund).

Takže v podstatě stejný problém jako u té mé "--user" služby, tzn. stop.sh se spustí, ale během provádění mi systemD KILLne (nebo spíše HUPne) programX. Zkoušel jsem u tvého příkladu změnit "/usr/bin/programx &" na "nohup /usr/bin/programx & disown" (nebo i bez disown), aby programX ignoroval HUP. Bez úspěchu.

Zjistil jsem, že systemD umí vytvářet dočasné služby, takže jediné funční řešení zatím je, že uživatelé musí programX spouštět takto:
Kód: [Vybrat]
systemd-run --user -p ExecStop=/path/to/stop.sh programX
Bylo by lepší kdyby to nějak šlo bez systemd-run, aby to pro uživatele bylo jednodušší, ale jediné co mě napadá je spouštět cron, který bude kontrolovat běžící procesy a pokud najde programX, tak z něj udělá dočasnou službu, ale to už se mi zdá trochu hardcore.

PS: Ještě jsem zkoušel "systemd-inhibit programX" (man systemd-inhibit), který by měl zablokovat vypnutí/resetování/spánek... Typickým využitím má být, zablokování vypnutí když se vypalují zálohy na DVD. Příkaz "systemd-inhibit --list" mi ukazoval, že programX tam je, ale k žádnému blokování při vypnutí nedošlo, protože programX se po signálu TERM nebo HUP asi normálně ukončil a tudíž nemohl nic blokovat, takže jsem nějak nepochopil jak to má fungovat.


Re:Jak spustit script před vypnutím/rebotováním PC (OS se systemd)
« Odpověď #5 kdy: 27. 02. 2019, 12:02:13 »
tak potom asi spravit
/usr/local/bin/programx


Kód: [Vybrat]
exec systemd-run --user -p ExecStop=/path/to/stop.sh programX

Re:Jak spustit script před vypnutím/rebotováním PC (OS se systemd)
« Odpověď #6 kdy: 27. 02. 2019, 16:42:10 »
Nevím jak moc je toto řešení běžné, mně se to zdá trochu prasárna podstrkovat uživatelům jiný program, ale je to asi nejjednodušší řešení. Díky.

Takže nakonec /usr/local/bin/programX vypadá takto:
Kód: [Vybrat]
#!/bin/bash

PARAM=Získání parametru z $@

exec systemd-run --user -p ExecStop="/path/to/stop.sh $PARAM" /bin/programX $@
a vypadáto, že vše funguje.

Útěchou je, že alespoň při hibernaci/spánku to jde podstatně jednodušeji - stačí umístit script stop.sh do /usr/lib/systemd/system-sleep/

Re:Jak spustit script před vypnutím/rebotováním PC (OS se systemd)
« Odpověď #7 kdy: 28. 02. 2019, 09:33:37 »
Muzes byt v klidu. Je to naprosto bezne reseni, navic docela elegantni a systemove. Vetsina enterprise produktu pouziva bash skripty pro start aplikaci.
p.s. Asi bys mel dat $@ na konci do uvozovek --> "$@".

Michal