sobota 15. ledna 2011

Spouštěč aplikace s resuscitací po pádu

V minulém příspěvku jsem zmínil skript, který používám pro spouštění aplikací, u kterých chci mít jistotu, že poběží, když je budu potřebovat. V mém případě jde opět především o Gnome-Do, které má tendeci občas havarovat a neni moc příjemné, když stisknete klávesovou zkratku a nic se nestane. Skript jsem upravil, aby byl co nejjednodušeji a nejuniverzálněji použitelný, třeba se bude někomu hodit. Přinejmenším je to zajímavé cvičení. ...


launch-r
#!/bin/bash
# Don't let the application die.. At least launch it again when crash.
# by GdH

name=${0##*/}

_help()
{
  echo "Usage: $name [OPTION]... [APPLICATION]
Application launcher that restarts the application in two seconds when crash. Use launch-r to terminate this application
and only one instance of the application is allowed.

Options:
   -k kill application
   -r restart application
   -l log crash events in ~/.log/$name-[app name].log
   -h this help
"
}

terminate()
{
  echo "Terminating $app .."
  if pid=`pidof -x $app`
    then
      if ! pkill -P $pid
        then kill $pid
      fi
      pgrep -fd " " "$0.*$app.*"|sed "s/$$//" | xargs kill && echo Terminated || echo Termination failed
      exit 0
    else
      echo "Nothing to terminate"
      exit 1
  fi
}

restart()
{
  if pid=`pidof -x $app`
    then
      echo "Restarting $app .."
      if ! pkill -P $pid
        then kill $pid
      fi
      pidof -x $0|sed "s/$$//" | xargs kill -9
  fi
  sleep 1
}

if ! opt=`getopt -n "$name" -o krlh -- "$@"`
  then _help
  exit 1
fi
eval set -- "$opt"
eval app="$"$#
if [ $app == "--" ]; then _help; exit 1; fi
while :; do
  case $1 in
    -k) terminate ;;
    -r) restart; shift ;;
    -l) log=1; mkdir -p ~/.log ; shift ;;
    -h) _help; exit 0;;
    --) break ;;
  esac
done

if ! which $app
  then echo "$app: command not found"; exit 1
fi
if pidof -x $app
  then
    echo "$app already running"; exit 1
  else
    while sleep 1; do
      if pidof gnome-session > /dev/null; then $app; else exit 1; fi
      sleep 1
      [ $log ] && date >> ~/.log/$name_$app.log
      notify-send "$name" "Restarting $app`[ $log ] && echo '\n event saved to log'`"
    done
fi
exit 1

Skript mám uložený v adresáři ~/bin, jenž je v Ubuntu po jeho založení (a restartu systému) automaticky přidán do systémové proměnné $PATH, a není tudíž nutné při jeho volání zadávat celou cestu. Pokud budete chtít vyzkoušet, nezapoměňte skriptu nastavit spustitelný příznak. Skript opět obsahuje bublinovou notifikaci, která na pád upozorní. Pokud ji budete chtít použít, musíte mít nainstalovaný balík libnotify-bin, pokud ne, smažete ze skriptu červený řádek.

Použití je jednoduché, nápovědu dostanete spuštěním skriptu s přepínačem -h, ale i v případě špatného zadání. Základní použití vypadá takto:

launch-r gnome-do

Tím spustíte gnome-do a v případě jeho pádu bude do dvou vteřin znovu nastartován. Pokud budete chtít případné pády logovat, stačí použít přepínač -l:

launch-r -l gnome-go

Log se ukládá do domovské složky ~/.log/launch-r_[jméno aplikace]. Takto je tedy aplikace zajištěna proti totálnímu umření, ale to také znamená, že pro ukončení oné aplikace musíte ukončit i skript, který ji hlídá. Proto je v nabídce skriptu i přepínač pro ukončení aplikace -k:

launch-r -k gnome-do

A nakonec se někdy může hodit i ruční restart aplikace, přepínačem -r:

launch-r -r gnome-do
launch-r -rl gnome-do

Druhá možnost samozřejmě zapne logování pádů.

Tento příkaz mám přidaný do aplikací spouštěných po startu GNOME - tím, že obsahuje přepínač -r, nedojde k množení procesů při odhlášnení a opětovném zalogování do GNOME. Skript launch-r povoluje vždy pouze jednu instanci dané aplikace. Ještě musím zmínit, proč je ve skriptu to tuplované zabití. Binární aplikace jdou jednoduše vraždit přes příkaz kill, ale pokud je třeba pythonní aplikace spuštěna bash skriptem (jako např. můj Shutdown GTimer), který launch-r spouští, nedojde po zabití tohoto skriptu k ukončení hlavního programu. Proto pkill -P $pid. Jenže tato konstrukce zase nezabije ty klasické binárky a přiznám se, že ještě nerozumím proč. Zatím to vypadá, že skript funguje a snad jsem ošetřil většinu výjimek, nicméně není vyloučeno, že ještě narazím na nějaké ty "anomálie", které bude třeba pořešit. Například situaci, kdy spouštěná aplikace vrací po startu řízení zpět shellu (tedy ne až po jejím ukončení)... to je zatím problém, co mě aktuálně napadl - takové aplikace pomocí launch-r nespouštějte ;)

Poslední update 24.11.11 - při odhlášení uživatele se launch-r neukončil a dokola se snažil svého svěřence startovat, což se u grafických aplikací bez session nedá, takže jsem doplnil test, zda je session aktivní a pokud ne, ukončí se i launch-r.

5 komentářů:

  1. Pěkný kód, mám pár dotazů a připomínek:
    - proč máš např. "if [ `pidof -x $APP` ]", nestačilo by "if pidof -x $APP"?
    - nechceš místo gnome-do používat třeba kupfer, ten nepadá, a když už umíš v pythonu, upravíš si ho podle sebe ;)
    - pro systémové binárky by v upstartu měl podobným způsobem jako launch-r fungovat příkaz respawn (viz třeba /etc/init/cron.conf)

    OdpovědětVymazat
  2. Super, konečně se do mě někdo pustil :) S bashem stále docela bojuju, dává příliš mnoho možností (hlavně jak to podělat :) a pak to často překombinuju. Na alternativy Gnome-Do se chystám delší dobu, určitě otestuju, a na respawn se taky podívám, abych doplnil vzdělání. Každopádně díky za komentář!

    OdpovědětVymazat
  3. Mimochodem, arrange že? :) Ještě jsem to na tvůj popud projel a vyházel další zbytečnosti. Navíc je tam zajímavý řádek `pkill -f "$0.*$APP.*"`. Ten ukončí i sám sebe a bash vypíše pěkně Terminated, i když se to netýká ukončení zabíjené aplikace. Čistší řešení by bylo ono `pidof -x $0|sed "s/$$//" | xargs kill` a ukončit program exit 0. Řekl bych, že jsem ho tam nechal proto, abych ten příkaz nezapoměl, ale když jsem ho pěkně přepsal do komentáře, můžu to opravit :D

    OdpovědětVymazat
  4. Od Tebe se vždy něco přiučím, takže Tě rád čtu. Moje poznámky jsou jen pokusem také trochu přispět... Mimochodem, proč nepíšeš na Ubuntu blog? ;)

    OdpovědětVymazat
  5. Díky, jen přispívej, jsem rád, když mě někdo opraví a doplní.
    Ohledně psaní na Ubuntu blog jsem byl osloven a dokonce jsem slíbil, že pokud budu mít nápad, tak i něco dodám. Musím se nad tím zamyslet a nějaký ten nápad vyvinout. A co hůř, budu to muset i napsat :)

    OdpovědětVymazat

Zkuste prosím při komentováni používat místo volby Anonymní volbu Název/adresa URL, kde vyplníte nějakou přezdívku, adresu zadávat nemusíte. Vědět, které příspěvky jsou od jednoho člověka, je fajn. Díky.

Pokud by se vám náhodou odeslaný komentář na stránce nezobrazil, vytáhnu ho z koše hned jak si toho všimnu. I Google spam filter se občas sekne.