daemon
前回調べたatが色々な名前に変身出来る件。どんな仕組みか調べてみる。atのソースを/tmpに 持ってきてコンパイル。色々足りないって言われた。言われるままに、cronのソースから取り寄せていたら、コンパイル出来た。
これは、きっとatのソース外に、__progname が定義されてるに違いない。持ってきたものの 中から探したら、
ob6$ grep __prog *.h globals.h:extern char *__progname;
以前やった、envの類と一緒だな。取り合えず、テストプログラムに放り込んでみる。
ob6$ cat test.c #include <stdio.h> extern char *__progname; int main(){ printf("%s\n", __progname); return 0; } ob6$ cc test.c
そして、実行。
ob6$ ./a.out a.out ob6$ mv a.out hogefuga ob6$ ./hogefuga hogefuga
アプリ名を変えても、フォローしてくれてる。
/usr/src/lib/libc/dlfcn/init.c:
/* * In dynamicly linked binaries environ and __progname are overriden by * the definitions in ld.so. */ char **environ __attribute__((weak)) = NULL; char *__progname __attribute__((weak)) = NULL;
疑問が氷解しました。これって、OpenBSD固有の事?
ob6$ grep __progname at.c | head -2 shortformat = strcmp(__progname, "at") == 0; openlog(__progname, LOG_PID, LOG_CRON); ob6$ cc -E at.c | grep __progname | head -2 extern char *__progname; shortformat = strcmp(__progname, "at") == 0;
ローカルなヘッダーファイルは、要注意だな。個別に調べなくても、cppで展開したやつを荒く調べておけば、吉。
Windows golang
前回やった、お手製のタイマー。Windowsで実現するとしたらbatファイルを作るのだな。 だって、それだけの為に、タイマーを入れて、Windowsを汚したくない。じゃ自作だな。 それで、必要なのは、sleepとwallか。残念ながら両方共Windowsには無い。移植出来ないじゃん。まてまて、探してみはなれ。
Windowsの「timeout」「sleep」コマンドでバッチファイルの実行を一時停止するこれ、使えそう。
後はwall相当。wallの正式名称は、write a message to all users だった。でもオイラーのつたない英語辞書では、壁ってのが登録されてるよ。一応辞書したら、障壁なんて意味もあるのね。正に移植に立ちはだかる障壁だな。
で、ふと思ったんだ。画面一杯に壁を拡げて、そこに休憩とかと書いておけばいいじゃん。 ノートパッドを立ち上げて、windowを精一杯広げる。そこに、墨痕鮮やかに、休憩と書く。 後はそれを保存。時間待ちした後、ノートパッドで、そのファイルを読み出せば、いくらなんでも、気が付くだろう。ノートパッドはそのために有るんです。それ以外の使い道は残念ながら、ありません。
でも、こんなおバカなバッチは書かないぞ。
テキストからモールス信号のwavファイルを生成する『cwwav』コマンドなんてので音源を作り、それを鳴らすなんて言う、偏向した方法も一応可能。
普通の人なら、 golangを知った今、Windowsにもgoを入れて、豪華に、上の妄想と同じ事をやってみよう。何たって、Windowsネイティブなアプリが簡単に作成出来るはずですから。芸の肥やしになるだろう。
入れたぞ。ソースを書いたり、実行したりが面倒。だったら、WSLと組み合わせちゃえ。 そうすれば、emacs上でソースを書ける。実行は、Windowsにとんぼ返りして行う?
馬鹿言ってんじゃないよ。一度emacsを立ち上げたら、そこから出ないで何とかするのが、emacs屋さんの流儀。プロフェッショナルなるの流儀ってもんです。Makefileを書いておけば、 コンパイルコマンド一発で、実行出来るよ。
(define-key global-map (kbd "C-c c") 'compile)
こういう設定を .emacs.d/init.elに書いておく。そうすれば、Ctl-C c で、簡単にコンパイルする為にmakeを呼び出せる。
go屋さん流のMakefileなんてのが有った。そうでしょ、そうでしょ。オイラーもちょいと書いておくかな。
SRC= bt.go run: go.exe run ${SRC} build: go.exe build -o a.exe ${SRC} ./a.exe clean: rm -f a.exe
指定するアプリは、go.exeってのがポイントかな。WSL上でも、ちゃんとWindows側のgoが起動してくる。
sakae@atom:/mnt/c/work$ cat bt.go // break time package main import ( "os/exec" "time" ) func main() { time.Sleep(60 * time.Second) cmd := exec.Command("notepad", "info.txt") cmd.Run() }
info.txtは、休憩と大書したファイル。これで不格好ながらも、壁の代わりが出来る。
C-C c すると make -k まで聞いてくるから、MakefileのTAGを指定する。(一番最初に定義したTAGの場合は省略可能)
Compile command: make -k build
後は、画面が割れて、コンパイルの進行状況と、実行結果が表示される。エラーが発生しても 簡単に修正と再実行が可能。なお、-kのオプションは、致命的なエラーじゃ無い場合、頑張ってコンパイルを続けるよってオプション。一度のコンパイルで、多数のエラーを検出出来るぞ。
-*- mode: compilation; default-directory: "/mnt/c/work/" -*- Compilation started at Sat Sep 15 14:08:23 make -k build go.exe build -o a.exe bt.go ./a.exe Compilation finished at Sat Sep 15 14:08:41
なお、emacsからgo-modeを使っていると、M-x godoc で、任意のパッケージの説明を参照出来る。でも、折角Windows用のgoが入っているんで、cmd端末から
c:\work>godoc -http=:8080
を実行して、http版のgodocサーバーを起動。 http://localhost:8080/ で、アクセスすれば良い。各パッケージに例が載ってて、これを コピペして使うと便利だぞ。
上のMakefileで作成される、a.exe は、残念ながらCUIのアプリになっている。ダブルクリックして起動しても、不格好な黒い窓が出て来るぞ。ちょっとかっこ悪いな。 GUI版にするには、下記が参考になるかな。
そして、転ばぬ先の杖、 Goのpanicと向き合う
sound on Windows10
Windows10で音を鳴らすには、Grooveミュージックなんてアプリを使う。ソースを見てたりする時、CDを聞いてたりする。きっとwaveファイルも聞けるんだろうね。
CUIの端末からでも大丈夫かな。兎も角アプリの在り方が分からんと始まらない。検索したけど見つからず。だったら削除時にファイルの絶対PATHを指定するはず。削除の検索ワードを付けたらどう? そんな事で、在処の調べ方は下記のようにするみたい。
c:\work>powershell Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. PS C:\work> get-appxpackage *Microsoft.ZuneMusic* Name : Microsoft.ZuneMusic Publisher : CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmon d, S=Washington, C=US Architecture : X64 ResourceId : Version : 10.18081.11121.0 PackageFullName : Microsoft.ZuneMusic_10.18081.11121.0_x64__8wekyb3d8bbwe InstallLocation : C:\Program Files\WindowsApps\Microsoft.ZuneMusic_10.18081.1 1121.0_x64__8wekyb3d8bbwe IsFramework : False PackageFamilyName : Microsoft.ZuneMusic_8wekyb3d8bbwe PublisherId : 8wekyb3d8bbwe IsResourcePackage : False IsBundle : False IsDevelopmentMode : False Dependencies : {Microsoft.VCLibs.140.00_14.0.26706.0_x64__8wekyb3d8bbwe, M icrosoft.ZuneMusic_10.18081.11121.0_neutral_split.language- ja_8wekyb3d8bbwe, Microsoft.ZuneMusic_10.18081.11121.0_neut ral_split.scale-125_8wekyb3d8bbwe} IsPartiallyStaged : False SignatureKind : Store Status : Ok
何これ? リナなんかで言うパッケージ情報じゃん。
仰せに従ってインストール場所に行こうとしたら、アクセス制限に阻まれた。突破しようと思えば何とかなるだろうけど、ヘタにいじって壊したくない。諦めムード。
何気に音源ファイル(debian様の所で作ったモールス音ファイル)を、ダブルクリックしたら、 Grooveミュージックが立ち上がって、音が聞こえてきた。これって、音ファイルのサフィックスで、起動するアプリが決定されるって事だよね。 Windowsの事、きっとMacの真似を徹底的にしてるに違いない。
c:\work>break.wav
こんな風に音源だけ指定したら、勝手にアプリが立ち上がって、音が聞こえた。やっぱりファイルを何のアプリで開いたらいいか知ってんだな。それにしてもshellで、いきなりデータファイルを指定して、アプリが起動するってUnix側の住人(CUIの人間)には、信じられない挙動です。
これに気を良くして、例のタイマーに組み込んでみた。"/C" を挟んでやるのが味噌かな。
cmd := exec.Command("cmd.exe", "/C", "break.wav") cmd.Run()
でも、Grooveミュージックの制御画面が盛大に出て来て美しくない。CUIでさり気なく音するアプリってあるのかな? 探してみたけどWindowsはGUI命って事で以外に無いんだよな。GUIは使って天国作って地獄ってのを、初期のMACで経験してるオイラーには信じられないです。
まあオイラーの方針を押し付ける訳にもいかないので、使うとすれば、このあたりかなあ。 mpg123 - Fast console MPEG Audio Player and decoder library
ネットサーフィンしてる時、CDをかけている。ABBAとかユーロビート等の軽快な音楽。エコノミック症候群にならないように、軽くステップを踏んでいるのさ。お前の場合は、ステップじゃなくて、単なる貧乏ゆすりだろ、ってのはさておき、疑問発生。
先に動いているGrooveに先住権が有って、後からモールス音を鳴らそうとしても拒否されるんじゃなかろうか? いわゆる排他制御問題。 そんなの、どうなるかやってみた方が早い。
先に立ち上がってCD演奏してるのに、無粋なモールスが割り込んできたぞ。そして、CDの演奏は停止されたよ。そんなもんなんですかね。
最後にこれを実用に供しようとして、若干手直しした。
sakae@atom:~$ cat /mnt/c/work/bt.go // break time package main import ( "os/exec" "time" "fmt" ) func main() { dt := 45 * time.Minute fmt.Printf("\nWait %v and morse sound\n", dt) time.Sleep(dt) cmd := exec.Command("cmd.exe", "/C", "C:\\app\\break.wav") cmd.Run()
このアプリをDeskTopに置くんで、音源の場所は固定にした。ダブルクリックで黒い窓が出て来るんだけど、余りに不愛想なので、ちょいとメッセージを表示させた。起動したら、窓はタスクバーに仕舞っちゃうんだけど、カウントダウンの時間を、そこはかとなく表示した方が良かったかな? そんなに凝ってもしょうがないって。
あっ、今気が付いた。CD1枚の演奏時間って、大体45~50分ぐらいじゃん。
switch to CentOS
前回lxde風のウブにgolangを入れた。上でやったようにemacsと共に使おうってんで、emacsも 導入したんだ。そして、.emacs.dとかをサーバー版のウブからscpしてきた。
でも、なぜか、.emacs.d/init.el が機能してなかった。んでもって、go-modeにも成れず。 なんじゃこりゃって思って調べたら、emacs導入時に勝手に、.emacsが入っちゃったみたい。 どうも信用ならんな。
えーいい、lxde使うの止めよう。違った系統のOSに入れ替えよう。そんでもって選んだのが、赤帽風職業的OSである、CentOSです。
VMWarePlayerに入れたんだけど、Fedora一族って見做されて特別コースが走った。でも、ライブ版になってるんで、HDDに入れるには、システムメニューから、選択しなければならなかった。そんなものなんかね。
で、無事に起動してGnomeを拝んでから、独自のパッケージをyumした。取り合えずは .emacs.dと.tmux.confをよそからscpしたよ。
パンチを喰らったぞ。go-modeを起動しようとしたら、何とか言うシンボルが見つけられないと言われた。ググルと色々な対策が出てた。でも、オイラーの所とはちっともマッチしない。
ふと、emacsのverを確認すると、24系と古かった。コピー元は26系。バージョン間で互換性の 無い変更が有ったんだろうなと推測。一度消して入れ直した。そしたら、24系にフィットするelcが出来たみたいで、正常に動き出した。
もう一つのパンチは、.tmux.confの非互換性で、設定が反映されない問題。設定例が無いかと システム内を彷徨って、手がかりを見つけた。変更してやっと動き出した。
#bind-key -T prefix Space next-window bind-key Space next
バージョンダウン時の忌々しい事例でした。そんなに新しい事をしたいかね? 少し保守的なものも並行に動かしておけって事かな。
become daemon
前回やったcronで、ダエモン君に簡単に変身してた。変身の術が公開されてるので、参考に見ておく。
/usr/src/lib/libc/gen/daemon.c
daemon(int nochdir, int noclose) { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir("/"); if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) (void)close(fd); } return (0); }
頭いいなあ。forkの使い方が冴えてる。こんな発想オイラーみたいな硬い頭では到底無理。
cronのmainの中から、deamonを呼んでるんで、どうやって親の内容をコピーするかと思ったら、forkして、親を子にcpしてる。そして、case 0: って事は、子だ。子が出来たら、親は殺しちゃうって言う戦国時代。
続く、ifからの部分では、子が動いているとな。後は、setsid()で、子が新しいセッションを作る。これでターミナルから独立するんだな。
そして、引数の指示により、トップdirに移動したり、標準入出力を闇に葬る操作をしてる。
トップdirに移動するのを推奨するのには訳が有る。(NFS)マウントされた先をワーキングdirしてダエモン君になっちゃうと、ダエモン君を停止してwdを変更しない限り、マウントを解除出来なくなっちゃうんだ。これは困るって事で、安全な場所 / に移動するのさ。
daemon君への変身の理屈が分かった所で、早速応用したい所がある。
それは、前回作った、休憩タイマー。
プチ不満があったんだ。その1は、起動時にバックグラウンドに追いやる必要がある。 その2は、タイマーを簡単に停止出来ちゃう事。fg して、Ctl-C で停止しちゃうからね。 (それが便利だと言えば、それまでですが)
これらの不満は、daemon君化で解消出来る。daemon君は何もrootの特権じゃないよ。平民が使ったっていいはず。
んな訳で、C語で書いてみた。
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/wait.h> int main(){ int rv; printf("pid= %d\n", getpid()); daemon(0, 1); sleep(60); if (fork() == 0){ execl("/bin/sh", "sh", "-c", "banner break | wall", NULL); } printf("PID= %d\n", getpid()); wait(&rv); return(rv); }
printfを挟んでいるのは、親が子に殺される、下克上を確かめる為です。また、daemon関数の第二引数が1になってるのは、標準入出力を生かしておくためです。(生きていないと、2番目のprintfの結果が闇に消えてしまう)
看板を出すのに、パイプが必要なんだけど、そんなの自前でやってると何時完成するか分からないので、shにお願いしちゃいました。(/bin/shの引数を実行内容にしてる。)
今回初めて、execlを使ったけど、第二引数以降は、argvの内容を文字列で指定するのね。最後のNULLをお忘れなく。
ob6$ ./a.out pid= 52588 ob6$ PID= 98647 Broadcast Message from sakae@ob6.localdomain (/dev/ttyp1) at 16:21 ... ##### ##### ###### ## # # # # # # # # # # # ##### # # ##### # # #### # # ##### # ###### # # 8 # # # # # # # # # ##### # # ###### # # # #
バックグラウンドに追いやる操作をしてないのに、自分で裏側に回りました。そして、shellのプロンプトが直ぐに表示されてます。 内容はスクリプトで書いたのと同様です。これだけ看板が出てくれば、さすがに休憩時って気が付くだろう。
ob6$ ps awxl| egrep '(a.out|UID)' UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TT TIME COMMAND 1000 98647 1 0 10 0 308 496 nanosle Is ?? 0:00.00 ./a.out 1000 33459 292 0 28 0 108 280 - R+/0 p2 0:00.00 egrep (a.out|UID)
ダエモン君はinitの支配下に入るのか。そしてWCHANがnanosleになってるって事は、タイマーの事象待ちになってますって事だな。そしてSTATのIは、20秒以上の待ちに入ってます。sは、セッションリーダーでございますって事だな。
ダエモン君は、制御端末が無いので達磨さんです。証拠は、端末名が表示される所が??に なってる事です。このダエモンを殺すには、面倒でもkillコマンドを実行する必要が有ります。
CentOS get source
赤帽さんを入れたので、ソースの取得と鑑賞はどうやるか調べてみた。
[sakae@cent hoge]$ sudo yumdownloader --source at : (1/3): updates-source/7/primary_db | 93 kB 00:00 (2/3): extras-source/7/primary_db | 51 kB 00:01 (3/3): base-source/7/primary_db | 1.0 MB 00:04 at-3.1.13-23.el7.src.rpm | 148 kB 00:02
専用コマンドが有るのね。取得はrootで実行する必要が有る。
[sakae@cent hoge]$ rpm -ivh at-3.1.13-23.el7.src.rpm
取得したものは、一般ユーザー権限で展開出来る。~/rpmbuild/ が作られてその中のSOURCEに ソースが入る。オリジナルへのパッチ情報がわーと有って、tar玉が(無い事も)展開されない形で置いてある。一手間かけて、展開ぐらいしておいてくれても良いと思うぞ。
今回はatのソースを取ってきたので、atd(ダエモン君)が有るか興味のある所。 atd.cが有った。cronとは独立してんのね。atd(8)によると、オプションでforegroundで動かしたりdebugメッセージをstderrに出せたり出来る充実ぶり。
ダエモン君に変身する方法は、daemon.cにまとめられていた。
daemon_setup() : if (!daemon_debug) { close(0); close(1); close(2); if ((open("/dev/null", O_RDWR) != 0) || (open("/dev/null", O_RDWR) != 1) || (open("/dev/null", O_RDWR) != 2)) { perr("Error redirecting I/O"); } } if (daemon_foreground) pid = getpid(); else { pid = fork(); if (pid == -1) { perr("Cannot fork"); } else if (pid != 0) { exit(0); } (void) setsid(); }
大体deamon()と同じ事をしてる。だったら何故そちらを使わない? ちゃんとglibcには実装されているようだけど。推測するに、このdaemonは標準になっていないって事かな。 daemon(8)に
CONFORMING TO Not in POSIX.1-2001. A similar function appears on the BSDs. The dae‐ mon() function first appeared in 4.4BSD.
なんて書いてあったからね。BSDライセンスは嫌いって表明でしょうか? それとも、このdaemon.cは、1996年に書かれていて、そこ頃は こんな便利なやつが無かった。動いているなら、そのままでいいじゃん。下手に触るなのリナス閣下の 教えに忠実に従ってるからかな。
あれ? TopDirへ移動するのが省かれているようだけど、運用で逃げているのかしら?