>> 古い記事: ローカルファイルシステムのファイルをHTTPで取得できるようにする(Ruby/WEBrick)
<< 新しい記事: Yapra: クラスベースのプラグインの書き方とファイル配置、パスの通し方

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

whileループで動き続けるプログラムをログアウト時に終了させる(Ubuntu 10.10)

注)いろいろよく分かってない人が書いてます。


1 環境

  • Ubuntu Linux 10.10 Desktop
  • GNOME 2.32.0
  • GNU bash, version 4.1.5(1)-release (i686-pc-linux-gnu)
  • Ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]

2 期待する動作

whileループなどで動作し続けるプログラムをログイン時に自動的に実行し、 ログアウト時に自動的に終了するようにしたい。

3 詳細

サンプルとしてこのような Rubyスクリプト ~/test.rb を用意した。


while true
  puts Time.now
  sleep 60
end

ログイン時に自動実行させるため ~/.profile に次の行を追加した。


ruby ~/test.rb &

  • グラフィカルログインで GNOMEセッション開始 (要するに普通にログイン)。
  • test.rb (pid=1772)が動いていることを確認。
  • デスクトップ画面右上のボタンでログアウト。
  • さっきと同じようにログイン。

この時点で ps ax | grep ruby すると、前回のログイン時に実行されたものと 今のログイン時に実行されたものが両方表示される。

 1772 ?        S      0:00 ruby /home/testuser/test.rb
 2165 ?        S      0:00 ruby /home/testuser/test.rb

前回ログイン時に実行したものは前回のログアウト時に終了してほしい。


ちなみに pstree で確認すると以下のようになっている(余計な部分は省略してある)。

前回のログイン時に実行された test.rb :

init
  ├─ruby /home/testuser/test.rb

今回のログイン時に実行された test.rb :

init
  ├─gdm-binary
  │   ├─gdm-simple-slav --display-id /org/gnome/DisplayManager/Display1
  │   │   ├─gdm-session-wor
  │   │   │   ├─gnome-session
  │   │   │   │   ├─ruby /home/testuser/test.rb

参考:

3.1 SIGHUP は送信されているか?

可能性としては 2つ考えられる。

  1. ログアウト時に SIGHUP が送信されていない
  2. ログアウト時に SIGHUP が送信されているが、何らかの理由でプロセスが終了しない

これを切り分けるため、 test.rb で trap を設定した。

 
log = open(File.expand_path("~/test.log"), "a")
 
log.puts "#{Time.now} ** start **"
log.flush
 
# HUP 以外のシグナルも一応見張ってみる
%w(HUP TERM INT QUIT).each {|sig|
  Signal.trap(sig) {
    log.puts "%s - pid:%d sig:%s" % [Time.now, Process.pid, sig]
    log.flush
    exit
  }
}
 
loop do
  puts Time.now
  sleep 60
end

一旦 test.rb を すべて終了させて

  • 上と同じように ログイン
  • 実行されているのを確認
  • ログアウト・再ログイン

この状態では 2つのプロセスが動いており、 test.log を見ても SIGHUP , SIGINT などを受け取った形跡はない。

一方、手動で kill -HUP {PID} した場合は test.log にそのことを示すログが残り、 プロセスが終了する。

したがって、 SIGHUPtest.rb に送信されていない ( test.rbSIGHUP を受け取っていない) と考えられる。

3.2 明示的に SIGHUP を送信する

なぜログアウト時に SIGHUP が送信されないのか、という点については 難しい話になるようなので深追いしなかった(帰ってこれなさそうでした……)。

OSまかせ(?)にせず、 ログアウト時に明示的に SIGHUP を送信する方法で進めることにした。


まず ~/.bash_logout を利用することを考えたが、 GNOMEパネルのログアウトボタンを押してログアウトした場合、 ~/.bash_logout は呼ばれない。

ちなみに、bash の manpage には次のように書いてあった。

When a login shell exits, bash reads and executes commands from the file ~/.bash_logout, if it exists.

「ログインシェルが終了するとき」とあるので、 「ログアウトするときに呼ばれる」という認識だと正確ではないのかも。


他に何か方法はないかと調べてみたところ /etc/gdm/PostSession/Default を使えばよいと分かった。

参考: How to run a script during Gnome log out - Unix and Linux - Stack Exchange

exit 0; の行の前に次のように書く

 
TARGET_USER=testuser

echo "executing /etc/gdm/PostSession/Default" >> /home/${TARGET_USER}/test.log # 確認用
 
if [ ${USERNAME} = "${TARGET_USER}" ];then
  su ${TARGET_USER} -c /home/${TARGET_USER}/.gdm_postsession
fi

# ↓これだけでもいいかも?
# if [ -e /home/${USERNAME}/.gdm_postsession ] ; then
#   su ${USERNAME} -c /home/${USERNAME}/.gdm_postsession
# fi

呼び出される ~/.gdm_postsession の内容:

 
echo "executing ~/.gdm_postsession" >> ~/test.log # 確認用

chmod u+x ~/.gdm_postsession で実行権限を与えておく。

※ この .gdm_postsession というファイル名はこのように決められているわけではなく、 自分で考えて付けたもの


上と同様にログイン・再ログインしてログを見ると、ログアウト時に /etc/gdm/PostSession/Default~/.gdm_postsession が呼ばれていることが確認できた。

あとは .gdm_postsession で 終了させたいプロセスに SIGHUP を送ればいいはず。

送信するには対象となるプロセスの一覧をどこかから得なければならないため、 ~/test.rb の最初の方に次の行を追加し、 自身の PID を ~/.my_hup_list に追記することにした。 このファイル名も自分で決めたもの。


open(File.expand_path("~/.my_hup_list"), "a") {|f| f.puts Process.pid }

ちなみに、他の Rubyスクリプトも同じように(ログインしている間だけ)動作させたいなら、 この 1行だけ別ファイル append_to_hup_list.rb に抜き出して、 メインのスクリプトの方では


require "append_to_hup_list"

とだけ書くようにすると良いかも。


~/.gdm_postsession の方では、 ~/.my_hup_list にリストアップされている PID に SIGHUP を送り ~/.my_hup_list を空にする


kill -HUP `cat ~/.my_hup_list`
echo -n "" > ~/.my_hup_list

ここまでやってようやく期待通りの動作になった。

~/.my_hup_list は 1行ごとに PID が並んでいるだけのプレインテキストなので、 Ruby 以外のプログラムからでも簡単に利用できる。

4 まとめ

  • プログラムの側で自身の PID を ~/.my_hup_list に追記
  • gdm セッション終了時に /etc/gdm/PostSession/Default が呼ばれる
  • そこから ~/.gdm_postsession が呼ばれるようにする
  • ~/.gdm_postsession~/.my_hup_list の PID を参照し SIGHUP を送る

5 備考

やりたいことができるようにはなったが、 煩雑なので tcsh の組み込みコマンド hup のようなものが使えるならそうしたい。

参考: Manpage of TCSH

または、Bash の機能を使って shopt huponexit とする方法もあるようだが、 これだとグローバルな設定を変えることになり 影響範囲が把握できなかったので今回は使わないことにした。


GNOME の画面右上のログアウトボタンを押したときに起こることを 単に「ログアウト」と呼んでしまっていいのか、 それとも「gdmセッションの終了」のように呼ぶのがいいのか、 結局よく分からなかった。 それとログインシェルとの関係とかも詳しく調べてすっきりさせたいところ……。


2013-01-14 追記:
最近のUbuntuなど、LightDM を使っている場合は /etc/gdm/PostSession/Default の代わりに /etc/lightdm/lightdm.conf で指定します。
参考: [SOLVED] LightDM login and logout scripts - Ubuntu Forums

参考(外部リンク)

>> 古い記事: ローカルファイルシステムのファイルをHTTPで取得できるようにする(Ruby/WEBrick)
<< 新しい記事: Yapra: クラスベースのプラグインの書き方とファイル配置、パスの通し方
** ホームに戻る

コメント

コメントの投稿

管理者にだけ表示を許可する

|
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。