Linux Gazette 2002年10月号 #83
今月のLinux Gazette の主な記事
n今月のニュース
 ・法制化
 ・一般ニュース
 ・ディストリビューション関連ニュース
 ・ソフトウエア及び製品関連ニュース
n Pytonでユーザを開放する−即ち−ユーザ入力を扱う
n Makefilesを作る:ミニ講義
n Muttを用いる複数アカウントE-メール
n Linux システムの複製−別法
n Perl モジュールの探検−パート2:GD::Graphを用いるチャート作成
n snuotrapsを用いる電源状態の取扱い
n Rubyでのプログラミング、パート2
n Ptraceを用いるプロセス追跡、パート2
n DVDオーサリング
n I/O メモリ−マップド・デバイスドライバのためのリスク・リソース割り当て
n Pythonを用いるApache ログ解析
n DHCPを用いるローカルネットワークのサブネット
nn
 
(訳者注)
原文を一括して一つのファイルでセーブするには、下記のリンクがあります。
TWDT 1 (gzipped text file)
TWDT 2 (HTML file)
前者はテキスト形式で、後者はHTML形式です。但し、HTML版のリンクが働くとは限りません。
 
 
 
 
今月のニュース
▼▼▼ 法制化 ▼▼▼
ベネズエラその他の政府のニュース
Registerは、ベネズエラが政府ソフトウエアに関しGPL寄りの政策を採用したと報じた reported 。政府向けに開発されるソフトウエアはすべてGPLでなければならず、GPLは在庫品が望ましい。LinuxToday's coverageで説明したように、ベネズエラ計画開発大臣フェリペ P. レズマートは、オープンソース、無料ソフトウエア及びこれら流行語に精通していると思われる。
オープンソースの使用は南アフリカ政府顧問団も推薦した recommended by a South African Government Advisory body。ウクライナ政府もまたオープンソースに好意的態度を表明しているexpressing legal preferences for open source solutions.

3番目には、知的所有権に関する英国委員会が、オープンソースと専用実施権制限が、開発途上国の将来発展に肝要essential to developing countries future progressとの結論に達したconcluded 。この報告はby Linux Weekly Newsby the Economistもまた取り上げている。

PSFが重点を変更
NewsForgeによればhas reported、無料ソフトウエア基金 The Free Software Foundation は、開発優先を変更して、デジタル権利管理及びGPL強化に重点を移しはじめた。同時に、リチャード・ストールマンは組織内で下働きをする計画である。
リチャード・ストールマンは、最近オープンソース、無料ソフトウエア及びセキュリティ問題について書いたrecently written 。この記事はSecurityFocusのJon Lasserの以前の投稿に答える公開状の形になっている。無料ソフトウエアの極めて政治的な支援に注意することの重要性及び、無料ソフトウエアとオープンソースの間の相違the differences に注意すべきことを説いている。ここで、GNU/Linuxを思い出させよう。
Palladium
マイクロソフトとPalladium計画及び将来の波及効果につき読む価値のある二つの文書は、by Ross Andersonby Seth Schoen.が書いた案内書だ。
DMCAがAdobeに噛みつく
Linux Weeklyは、Acobatに含まれる特徴に関しDMCAを使ったとしてAdobeが攻撃された皮肉な経過を解説している。一方Wiredは、ElcomsoftはそのDMCA問題がそのスタイルを束縛するのを許さず、ソフトウエア開発において風に逆らい続けたくないと報じている。
DMCAの話題については、ロバート・グリンゲリがDMCAの優れた用心棒マーク・イシカワとのインタビューを発表したhas published an interview 。CornellのDMCA政策に関するLawMemeの議論dissection も面白い(courtesy NewsForge)
 
 
 
 
 
▼▼▼一般ニュース▼▼▼
XVIDとGPL
XVID プロジェクトは、Sigma DesignにグループによるGPL化されたコード開発に関する侵害を止めさせてcease infringing GPLの勝利を記録した。XVIDチームが計画の将来展開をすべて中止すると発表した24時間後に、Sigma Designは態度を和らげて、ソースが利用出来るようにした。
Slapper
Slashdotは、二つの新型Linuxウイルス、Slapper.BとSlapper.Cの出現に気付いたSlashdot noted the emergence 。これは、OpenSSLのバッファオーバーランを食い物にする。OpenSSL0.9.6gに更新していなければ今がチャンスだ。元のSlapper作者と疑われた男は拘留されたauthor has been arrested
O'Reillyのニール・デービスは最近Linux Slapperウイルスと別の弱点を見つけたlooked at the Linux Slapper worm and other vulnerabilities.。
Bruce Perens
先月予告したように、Bruce Perens HP を退職した。Registerが報じたようにreported by The Register、分裂は友好的だったが、これはBruceのマイクロソフトに対する公然たる敵対政策から生じた。自由の身になってPenensは、彼が前から偽物だと非難していたマイクロソフトのソフトウエア選択Software Choiceに対する反動the Sincere Choice initiativeを立ち上げた。Newsforgeとの最近のインタビューで、彼の新計画の背景 behind his new projectを語った。
 
 
 
▼▼▼ディストリビューション関連ニュース▼▼▼
Debian
Debian週刊ニュースは、ロブ・ブラッドフォードが発表したセキュリティ公告宣言Security Notification Script announced を報告した。宣言はローカルにインストールしたパケージをsecurity.debian.orgのものと比較し、問題点の説明と、パケージがDSA RDF fileに記述されたものであれば、Debian助言者の名を提供している。

X-Boxを持つ人は、Debianを走らせると良いmay want to try running Debian on it

ScratchからのLinux
来るべきLFS-4.0ブックの第一試験版がアプロードされ、ダウンロードすることが出来る。閲覧及び/又はダウンロードは、http://www.linuxfromscratch.org/
SuSE
最新のSuSE Linux バージョン, SuSE Linux 8.1が、10月始めから入手出来る。
UnitedLinux
Linux産業指導者Conectiva S.A., SCO Group, SuSE Linux AG, 及び Turbolinux, Inc.が資金提供する UnitedLinux は、熟練技術者で共同体指導者であるポーラ・ハンターを世界ゼネラルマネージャに指名したことを発表した。ハンターの指名は即時有効である。グループはまた、UnitedLinux製品のベータ版が9月23日から公開で入手できることを発表した。オープンのベータソースは、download.unitedlinux.comから無料でダウンロードすることが出来る。
残念乍ら、UnitedLinux はオープンソース開発共同体、特にUnitedLinux の非公開ベータとその配布方法に関する懸念を表明した公開状an open letter を発表した無料ソフトウエア基金 Free Software Foundationの中で友好関係を失っている。UnitedLinux 関係者はこれらの懸念に対し回答したresponded
WinLinux
WinLinux 2003は、 WinLinux システムの重要な更新である。WinLinuxは、Windows 95/98/Millenniumと完全互換であると主張する。WinLinuxはまたRed Hat Linuxとも互換性があり、Linux側で両システムの利点を提供する。オンラインサポート付きCD上のWinLinux 2003完全版は、the websiteから注文することが出来る。 
Xandros
Slashdotは、Xandros Desktop のベータ3bを予め見たthe story on OSNewsにリンクlinked している。Xandrosは、Corel Linuxであったものの現在の管理人である。
 
 
 
 
▼▼▼ソフトウエア及び製品関連ニュース▼▼▼
 
Linuxゲーム出版ニュース・メールリスト
Linuxゲーム出版は、Pyrogon Inc.とその最新のゲームをLinux市場に持ち込む提携を発表した。これは、高速のパズルゲームCandy Cruncherから始まる。詳細は press release を参照。
TinyTEAM 4.3

Century Software は、TinyTERM Version 4.3. のリリースを発表した。TinyTEAMにより、Windows PCを容易に正確に文字ベースのデータ及びUNIX、Linux及びIBMがホストするアプリケーションに使用することが出来る。これは、エミュレーション20個、ネットワークユティリティ、ソースファイル転送及び無料技術サポートを含む。TinyTEAMは、ダウンロードで入手することが出来るavailable for download

組込McAfeeアンチウイルスプログラム付きKerioメールサーバー
Kerio Technologies は、Linuxベースのアンチウイルス製品、Kerio MailServer 5.1.6を提供している。これにはMcAfeeアンチウイルスプログラムが組み込まれておりLinux RedHat上で働く。KMSは、広範な特徴とユーザに親しみやすいインターフェイスを一つのパケージで胎教する。KerioはMcAfeeアンチウイルスプログラムを組み込んだものは他にないと主張する。
IBM AlpaWorksが移植マネージャを提供

AlphaWorks は、C及びC++アプリケーションをSolarisからzシリーズLinuxに移植出来るようにして、Linuxプラットホームに対するアプリケーション開発速度を上げる無料移植マネージャ "porting manager" を提供した。

XimianがRed Carpet付きLinux用に特注Operaを提供
Opera は、その販売委託提携先 Ximian がOperaウエブブラウザの特注版を、ダウンロードオプションとしてLinux用 Red Carpetソフトウエア管理プログラムに含むと発表した。Red Carpetは多数のXimianデスクトップユーザが使用するソフトウエア管理ソリューションで、Linuxシステムを効率良く安全に更新する。このプログラムにより、ユーザはインターネットを通じて、ソフトウエアをXimian、主要Linuxディストリビューションプロバイダ及びその他の独立ソフトウエア販売者からインストールし保守することが出来る。
ActibeStateがKomodo 2.0を初公開
ActiveState Corp.は、プラットホーム横断オープンソース言語プログラミング用に強力なワークスペースを提供する新IDEを発表した。Komodo 2.0 は、ActiveState GUI ビルダ, Visual パケージマネージャ、ソースコード管理、プロジェクトマネージャ、マクロ、ウエブサービス発生その他を含む大きい力、融通性及び自動化を与える。唯一のオープンソースプログラム言語、Komodoは、単一スペースでエディット、デバッグ、テストをすることが出来る。
Komodoは、Perl, Python, PHP, Tcl, XML, XSLT及びRuby や JavaScriptを初めとする多数のその他の言語用に最適化されている。
 
Copyright © 2002, Michael Conry. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 83 of Linux Gazette, October 2002
 
 
 
 
Pytonでユーザを開放
−即ち−
ユーザ入力を扱う

By Paul Evans

ユーザ入力を必要とするプログラムを書く遙か前まで、Python を使おうと考えたことはないだろう。好奇心に満ちたPythonプログラム初心者として、君は簡単にユーザーに入力を頼み、その通りして呉れると信じているだろう。

警告: 前の記事をベテランに見せたら、腹を抱えて笑うだろう。

ユーザーはそんな風にしてくれない。

例えば、「イエス」「ノー」の返事を求めたとしよう。ユーザは丁寧に名前をタイプしたり−昼のおかずを入力したり、何もしないかも知れない。挙げ句に、プログラムが故障する。(殆どは)わざとしているのではない。哀れな奴が気が散って君が丁寧に催促した言葉を無視して、プログラムに関する限り頓珍漢なことをタイプするのだ。続いて、奴らはプログラマに文句を言う。君は、馬鹿だと思われて落ち込む。

この悲劇を避けるには、先ず、ユーザの背後にあるものは何でもチェックして、微かにでも自分の求めるものに近いことを確かめる必要がある。Pythonには、これを助ける機能が多数あるので、以下でこれを経験して頂く。

別に出来ることは、入力ウイジェットにバリデータを使うことだ。この作業の方法は、追随しないキイ打ちを投げ出すことだ。例えば、文字列ウイジェットに数値バリデータを設定すると、ユーザーは"ABC"などを好きなだけ打てるが何も起こらない。有効なのは多分10進数又は金額の0-9だけだ。ずっと後でこれを試す。

最後に、自分が望む通りの入力をしてくれる良く訓練された素直なユーザに運良く出会ったときであっても、入力は望み通り正確な形式になっているとは限らない。不注意で「jOHN sMith」(大文字ロック)とタイプしたり電話番号を「604555-1212」とすることもある。

冗談はさておき、ユーザが出来るだけ早く正確にデータを入力出来て、矛盾のない形式で提示され記憶されるようにするのは、プログラマの仕事だ。それに、自分も満足することが出来る。

入力の取得

先ずプログラムがユーザ入力を取得する必要がある。このためPythonはコンソールから二つのメソッド'raw_input("Prompt")' と 'input("Prompt")'を提供する。('input'を使わないこと、以下を参照)。入力は古き良きコマンド行引数又は環境変数から取得することも出来る。

その他、余り興奮しないで、 Xdialog, Gdialog (gnomeの一部)又は Kaptainなど、もっとグラフィックな方法も利用することが出来る。

完全なGUIキットへのアクセスは、PythonからPyQT , TKinter, WxPython 及び PyGTKなどを使ってアクセスすることが出来る。

ここで一寸注意しておくのが良いだろう。殆どのユーザは、満足した素直な生物だが、中にはで、 ぶち壊しの好きな悪党もいる。

その理由で、ユーザ入力がコマンドスペースに入るのを許してはならない:

'input()' の代わりに 'raw_input()' を使うこと。'input()' は、プログラムがそれを入手刷る前に 'eval()' に渡される。これは自動的にタイプを転換するので、整数一つが入用で、ユーザが偶然一つ与えたときは便利だが、ユーザは文字列を引用する。最悪の場合、悪いユーザが'os.system("rm -r *")'とタイプすると、悲惨だ。'raw_input()' は、ユーザーの入力を文字列として返す。これで検証が容易になる。どんな型かが分かって、加えることの出来る作用が分かるからだ。
・os.system(), os.popen() 又は os.exec*() 呼出に対する入力は常にチェックすること.
・ユーザ入力をウエブ頁にプリントする前又はSQLクェリーで使う前に、常にユーザ入力をエスケープすること. これは入力チェックの追加だ。エスケープしない特殊文字は、無効なHTMLを生じ、ペイジフォーマットを台無しにして、ユーザが別のユーザに対するJavascript "特性"を探ることが出来るようにする。SQLクェリーの中のエスケープしない特殊文字は、SQLシンタクスエラーを起こすか又は、クェリーに予定以外のことをさせる。HTMLをエスケープするには'cgi.escape()'を用いる。SQLクェリーのエスケープ方法は、データベース文書を参照のこと。

気味の悪い場所はこれで終わりだ。

xtermを開いて 'python' とタイプしインタプリータに入る。注意:これらの例の多くは、Pythonのバージョン2以降を使う必要がある。Redhat は未だに規定値で1.5xを出荷しているので、Redhatユーザは、代わりに'python2'とタイプする必要がある(それに多分先ず'add-ons'からrpmをインストールする必要がある。念のため云うと、バージョン1.5は、数字1と9から始まる年にリリースされた。

文字列オブジェクトの内容チェック

プログラム言語には、通常この種のチェックのための方法が含まれ、Pythonも例外ではない。上述のような最初の課題:一つを要求したときユーザが有効な数字を与えたかを確かめること、を考える。

Pythonの中の文字列オブジェクトはすべて、thを簡単にする埋込メソッドを有する。以下の行を'>>>' プロンプトで入力する。


>>>input = '9'

>>>input.isdigit()

1

これは、'1' (真)を返すので、'if' ステートメントの中で条件として簡単に使うことが出来る。この種の別の属性は:


s.isalnum() は、文字全部が英数字のとき真を、さもないとき偽を返す。

s.isalpha() は、文字全部が英字のとき真を、さもないとき偽を返す。

これら及び、その他多くのリストは、 Python 2.1 Quick Referenceを強く推薦する。私は常にこれを用い、速度を上げるため HNB に入った古いテキスト版まで持っている。

これはメニュー選択など簡単な場合に使えるが、浮動小数点や実数が欲しいときはどうだろうか?

次を考える:


input = '9.9' 又は

input = '-9'

両者とも有効な数字だが、input.isdigit()は'0'(偽)を返す。負号と小数点は、'digits'でないからだ。哀れなユーザはエラーメッセージを貰って慌てるだろう。

そこで、それらを明白に転換して試したいものであるとしよう。そのため、Pytonのtry/except 構造を使う。Pythonは、エラーに関して各種の例外を起こすので、これらのエラーを名称によって個別に捉えることが出来る。

'-9'のような整数が欲しいとしよう。数値演算子'int()' を使って、転換を明白におこなわせることが出来る。


次を試す:

    someVar = int(input)

    print 'Is an integer'

except (TypeError, ValueError):

    print 'Not an integer'

ここで二つの事に注目する。第一は、二つの異なる例外、型と値、をチェックしていることである。こうして、ユーザーが浮動小数点('9.9'など)を入力するのを扱うだけでなく、どんな種類の数値も入力しない可能性−ハムサンドと入力するかも知れない−をも許す。注目する第二の事柄は、捉える興味のある種類の例外を実際に入力したことである。どんなエラーを捉えるかに悩むことなくオープンエンド例外をタイプするだけは次のように極めて容易である:

次を試す:
  someVar = int(input)
  print 'Is an integer'
except:
  print 'Not an integer'

これをしてはいけない。Pythonでは出来るが、例外すべてを捉えようとしているので、全部が壊れるなら、デバッグは悪夢であろう。thが一つであることに私を信じららたい;捉え用とするエラーを見ると、長い目で見て時間を節約することが出来る。

有用と思うその他の演算子は、long()とfloat()である。flip 側では、str() が何でも文字列に転換する事が出来る。

レンジチェックを忘れてはいけない−プログラムが整数を常に入手するのを確かめて喜んではいけない、月の日数に'42'を呑気に受け入れているかも知れない。演算子 '>, <, >=' などをつかって、数値が予期範囲に入っているかどうか確かめること。

入力の検証

見て来たように、入力を獲得した後それを検証することは出来るが、ユーザは最初に間違うのを防止することが出来ると良い。

ウィジェット・バリデータを入力する。

不要なキイストロークが文字列ウィジェットに現れるのを防止するグラフィック・ユーザインターフェイス・ツールキットに組み込まれたものがある。ツールキットは、通常、数値、英字、及び英数字などの埋込バリデータと一緒に来るので、容易く使用出来る。私は現在、GUI用に PyQT を使っているが、TKinter, WxPython 及び Kaptain にもバリデータがある。間違っているかも知れないが、 PyGTK には、未だ無いように思われる。これらの無いツールキットを使うときは、多分信号をフックアップしてロールすることが出来る。

埋込バリデータがPyQtに合わないとき、例えば自分で、特注バリデータを、規定することが出来る。

明らかに、ここですべてのツールキットに言及することは出来ないが、数値バリデータをpYQTの中のウィジェットに付着する方法を示す。ウィジェットの名称は'self.rate'で、'QDoubleValidator'を付着して、10進2桁までの場所で0.0と999.0の間の数値を受け入れるよう命じる:


self.rate.setValidator(QDoubleValidator(0.0, 999.0, 2, self.rate) )

どうだろう?レンジチェックもすることに注意!

ユーザが情報を入力するのを助ける別の方法は、 spinners, pick-lists 及び combo-boxesであるが、既に分かっている筈。

入力のフォーマット

緒言の'jOHN sMith' の例を思い出されたい。修復方法を示す:


>>>'jOHN sMith'.title()

'John Smith'

そう、Pythonの文字列オブジェクト全体の別の属性は 'title()' 属性で、これが各語を大文字で始める。'capitalize()' も同様だが、最初の文字だけに働く:


>>> 'jOHN sMith'.capitalize()

'John smith'

先に進んで、お望みなら 'upper()', 'lower()' 及び 'swapcase()' も試されたい。作用は推測出来る筈。

だが'rjust(n)' はどうだろうか?これは、レポートのレイアウトに使うことの出来る唯一の手軽な属性だ。下記を見られたい:


>>> 'John Smith'.rjust(15)

'     John Smith'

この文字列は、15文字の長さで右にジャスティファイされた。多分想像されるように、 'center(n)' と 'ljust(n)' もある。ここでも、Python 2.1 Quick Reference で全体を見られたい。

Pythonで重要な別の演算子は、'%' (パーセント) 演算子だ。リストオブジェクト及びprintf形式フォーマットコードの関連するこれの説明には数頁を必要とするので、ここでは、少数の例を示してで興味をそそるだけで勘弁して貰う。

最も簡単な形で '%' 演算子は、実行時に変更することの出来る変数を含む正式の文章を書くことが出来るようにする:


>>> 'This is a %s example of its %s.' % ('good', 'use')

'This is a good example of its use.'

少なくとも、そうあって欲しい。これは能力のほんの入り口だ。 '%s' に文字列オブジェクトを代入するのに加え、'%r' 及び'C'言語からのprintf仲間:c, d, i, u, o, x, X, e, E, f, g, G.もある。

Python 2.1 Quick Referenceから一例を示す:


>>> '%s has %03d quote types.' % ('Python', 2)

'Python has 002 quote types.'

右側はマッピングでもよい。それによりフィールドを名称で示すことが出来る。

少し難しいが、普通に使う事柄に進もう。

電話番号

電話番号は長さが変化する。内線電話では2、3桁のこともあるし、国際電話では15桁にも達する。'#'や星印、さらにはコンマを含む事もある。最悪の場合、ユーザーは入力書式を決めることもあるし、部分的に決めたり決めなかったりもする。

少なくとも正しく入力しようとさせないとユーザに不満が残る。バリデータは0-9の数字と同時に、#, *, コンマ, -, ), ( のすべてを受け入れ、

文字列:


'(250) 555-1212' の代わりに:


実際に(北米電話番号について)必要な


'250-(555)-12-12'

で終わらせる。何にでも通用する一般的対策を作るので、御心配なく。

このような問題に直面したときの第一感は Google - 特に Google Groupsで人様の作品をコピイすることだった。見付かったコードは常に私が作るものより遙かに優れていたので、良い考えだった。残念乍ら、今回はGuido van Rossum (Pythonの発明者)から、Pythonにはこのようなものがない、多分次のように出来るだろうとe-メールを貰った:


import string

def fmtstr(fmt, str):

    res = [] i = 0

    for c in fmt:

        if c == '#':

            res.append(str[i:i+1]) i = i+1

        else:

            res.append(c)

    res.append(str[i:])

    return string.join(res)


これは良いスタートで作者の信用に文句はつけられないが、何桁の数字を与えられたかを勘定して正しい長さの書式文字列を選らぶには、'if/then' を沢山使わないとすべての場合を扱うことは出来ない。先に進みこれを xterm に貼り付けて次のように呼び出そう:


>>> fmtstr('###-####', '5551212')

'5 5 5 - 1 2 1 2 '

事実、私はこれをコピイしてエディタに貼り付け、電話番号、日付その他も型の入力のため長たらしい 'if/thens' を構築したが、それでも全部は扱えない。その上、似たことをおこなうのに膨大な行が出来上がった。そこで、あきらめた。

おこなった方法を示す。。先ず、ユーザにタイプさせた「余計な」書式文字を篩にかける:


def filter(inStr, allowed):

    outStr = ''

    for c in inStr:

        if c in allowed:

            outStr += c

    return outStr

これを次のように呼び出すことが出来る:


>>>filter('250-(555)-12-12', string.digits)

'2505551212'

又は、第二引数を自分で '0123456789#*,' と定義して、許容出来る文字全部を含ませる。

ここで、Guidoのコード断片を取り上げ両入力因数を逆にする。この方法で、長い書式文字列を一つだけ規定することが出来、入力の外に出るまで整合される。余分の入力が付け加わるので、文字を失うことはない。


# 規則的表現モジュールのインポート

import re



def formatStr(inStr, fmtStr, p = '^'):

    inList = [x for x in inStr] #list from strings..

    fmtList = [x for x in fmtStr]

    # the good bit

    inList.reverse(); fmtList.reverse()

    outList = []

    i = 0

    for c in fmtList:

        if c == p:

            try:

                outList.append(inList[i])

                i += 1

            # break if fmtStr longer than inStr

            except IndexError:

                break

        else:

            outList.append(c)

    # fmtStr より長い inStr の扱い

    while i < len(inList):

        outList.append(inList[i])

        i += 1

    # これを見出した方法で元に戻す

    outList.reverse()

    outStr = ''.join(outList)

    # 迷走 parens/- などを削除

    while re.match('[)|-| ]', outStr[0]):

        outStr = outStr[1:]

    # close any legit parens

    while outStr.count(')') > outStr.count('('):

        outStr = '(' + outStr

    return outStr

[Text version of this listing.](このリストのテキスト版)

これは、Guidoのものと同じだが、'#' を使う必要があるため、規定値位置ホルダ文字がここでは '^' (カレート)になっている点だけが異なる。代わりに、出力に実際のカレートを必要とするときは、任意選択の第三引数として規定することも出来る。

出力のサンプルを幾つか示す:


>>> formatStr('51212', ' ^^^ ^^ (^^^) ^^^-^^^^')

'5-1212'

>>> formatStr('045551212', ' ^^^ ^^ (^^^) ^^^-^^^^')

'(04) 555-1212'

>>> formatStr('16045551212', ' ^^^ ^^ (^^^) ^^^-^^^^')

'1 (604) 555-1212'

>>> formatStr('1011446045551212', ' ^^^ ^^ (^^^) ^^^-^^^^')

'1 011 44 (604) 555-1212'

実用では、自分の電話番号書式文字列だけを早くから決めるのが良いだろう。例えば:


phone_format_str = ' ^^^ ^^ (^^^) ^^^-^^^^'

文字列の初めに空白があって、追加文字が入れられる。次のように呼び出す:

... 'filter()' ファンクションなどを用いて「入力」を掃除した後、


formatStr(input, phone_format_str)


郵便番号


カナダ郵便番号を知らない人のため言うと、次の形になっている:


'V8G 4L2'

タイプするまでは、何の変哲もない。殊にタイプの下手な人は、大文字ロックを掛けないと−偶に解除を忘れる−[shift]+英字, 数字, [shift]+英字などとタイプし、手順から出るとき多くは'v*g $l@'で終わる。言うまでもないが、ユーザーはこの入力を嫌い、正しいことは少ない。申し込みの多くには、ユーザを悩まさないため、郵便番号を受け付けないものもある。似たような郵便番号の国もある。

ここで、我々の書式ファンクションを使うと、お茶の子さいさいだ。先ず、入力を検証するつまり篩に掛ける。次いで、Pythonの埋込属性 'upper()' を用いて、英字の大きさを正しくする。最後に:


>>>formatStr('V8G4L2', ' ^^^ ^^^')

'V8G 4L2'

正確な郵便番号が申し込みに取って重要なときは、文字を勘定しパターンを検証する方法で、さらに検証する必要がある。だが、一般用途のため、他国の郵便番号も受け入れなければならない。クリーンアップ後の文字数=6の場合だけを考える。

社会保険番号も同様に扱う:


>>> formatStr('716555123', '^^^-^^^-^^^')

'716-555-123'

社会保険番号には先ず check digit ルーチンを走らせて、有効なことを確かめる。クレジットカードも同様だ。

これらの例がユーザインターフェイスのコードに役立つことを願う。ご意見や提案を歓迎するhear back。特に日付に付いて然り。これは常に面白い。

閑話休題、これら書式援助はユーザーには秘密に保つのが重要だ。「ヘルプ」に置いて、機能がそこにあることを知らせるには「ツールキット」又は「作用説明」を用いる。ユーザがサンザン苦労した後に、それを発見したら、ブーブー言うのが落ちだが、澄ました顔をしていれば良い。

 

著者紹介:Paul Evans

Paul Evans は、エレクトロニクス特にコンピュータに関することは何でも好きだ。青年期に Altair 8080A によだれを垂らしたほど年を取った。二人の子供と一緒にノーザンブリティッシュコロンビアの荒野に住んでいる。木材業ではないが、幸せだ。
 
Copyright © 2002, Paul Evans. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 82 of Linux Gazette, September 2002
 
 
 
 
Makefilesを作る:ミニ講義

By Tedi Heriyanto

 

この記事は、Makefileを作るときの指針として使えるものを提供する。この記事は、Makefileの必要性とMakefileを作るとき考慮すべき事項を述べる。

緒言

foo と言うプログラムを開発しているとする。それは、1.h, 2.h, 3.h, 4.h, 5.h ヘッダ五つと、1.cpp から 5.cppまでのC言語ソースコード六つと、main.cpp ファイルを含んでいるとする(実際にはこのようなファイル名称計画は使わない方が良い)。

1.cpp にバグを見付けて修正したとする。新しい foo プログラムを得るには、ファイル一つだけ変更したときも、ヘッダーやソースコードなどファイル全部をコンパイルし直さなければならない。これは、コンピュータがコンパイルするのを待つだけの、特にコンピュータの速度が遅いときは、退屈な作業だ。

解決策はないものか?

御心配なく。この種の問題はコンピュータ・ベテランが数年前に解決した。解決策として make と言う名のプログラムを作った。これはソースコード全部を構築する代わりに変更されたソースコードだけを構築する。file 2.cpp を変更したら、make はそれだけを構築する。面白いだろう。

以下が make を必要とする別の理由だ [2] :

・多数のソースコードから構成されるソフトウエア・プロジェクトは、複雑で長いコンパイラ・コマンドを有する:make を用いるとこれらを減らすことが出来る。
・プログラム計画には、滅多に使わなくて覚えるのが難しい特殊コンパイラを必要とすることがある:make を用いるとこれらを減らすことが出来る。
・首尾一貫した開発環境を維持する。
・構築処理を自動化するので、make をシェルスクリプト又はクロンジョブから容易に呼び出すことが出来る。

Makefileを必要とする理由

ソースコード構築に当たっての問題を make が解決すると思ったら、もう一度考え直されたい。make は、プログラマが命令を与えないと何も出来ないからだ。make make に与えるコマンドを含む別のファイルが付属しなければならない。それを makefile と呼ぶ。

このファイルは makefile 又は Makefile と名付ける。便宜のため、GNUプログラムには、探し易いので Makefileと言う名の makefile がある("ls" を実行すると、このファイルは普通常に先頭になる)。これを別の名で呼ぶと、そのファイルを使うことを知らせるには、make オプション -f を与えなければならない。

例えば、makefile の名を bejo とすると、このファイルに対し make を走らせるコマンドは、次のようになる:


make -f bejo

Makefile 構造体

Makefileは、target、dependencies 及び rules のセクションを含む。Dependecies は、target を作るのに必要なものとソースコードで:target は通常実行可能ファイルである。Rules は、target を作るのに必要なコマンドである。

下記に makefile の簡単な記述を示す。


target: dependencies

	command

	command

	...

Makefile の一例

以下は簡単な makefile の一例である(記事のため行番号を追加):


1 client: conn.o 

2	g++ client.cpp conn.o -o client

 

3 conn.o: conn.cpp conn.h

4   g++ -c conn.cpp -o conn.o

上の makefile において、dependencies は client: conn.o を含む行で、rules は、g++ client.cpp conn.o -o client. を含む行である。各 rule l行がタブで始まることに注意。これはタブであって空白ではない。タブを忘れたり空白を用いたりするのは、makefile を構築するとき、起こし易い間違いだが、タブがないと makefile は働かない。

上の makefile は次のように説明することが出来る:

・target として client 実行可能ファイルを作る、これはファイル conn.o に依存する。
・その target を作る rules は2行目にある。
・3行目では、target conn.o を作るため、ファイル conn.cpp to conn.h が必要である。
・rules は、4行目にある。

コメント

makefile にコメントを入れるには、単に各行の第一列に '#' を置いてコメントする。

下記はコメントを入れた makefile の一例だ:


# "client" と言う名の target を作る

1 client: conn.o

2   g++ client.cpp conn.o -o client



# Creating object file "conn.o"

3 conn.o: conn.cpp conn.h

4   g++ -c conn.cpp -o conn.o

Phony Target [1]

phony target とは仮の名である。これは、明白な要求をするときに実行されるコマンドだけのための名である。phony target を使う理由は二つある:同じ名のファイルとの混同を避けるためと、makefile にファイル作成以外の仕事をさせるためである。

そのコマンドが target ファイルを作らない rule を書くと、これらのコマンドは target を作り直す毎に実行される。例えば:


clean:

	rm *.o temp

コマンド rmclean と言う名のファイルを作らないので、このファイルが存在することはない。コマンド rm は、clean を呼び出す毎に実行される。clean ファイルが常に新しいことを確かめるため、実行する必要があるからだ。

phony target は、誰かが現在ディレクトリの中に clean と言う名のファイルを作ると、働きを止める。dependencies を必要としないので、ファイル clean は、更新されたと見なされて、'rm' コマンドは実行されないからだ。この問題を解決するため、特殊 target .PHONY を用いて target を明白に phony と宣言することが出来る。例えば :


.PHONY : clean

上の makefile で clean は、は clean と言う名のファイルがあろうがなかろうが、常にclean コマンドを走らせる。

変数

makefileで変数を定義するには、次のコマンドを用いる:


$VAR_NAME=value

変数名は通常大文字で与える。例えば :


$OBJECTS=main.o test.o

変数の値を得るには、次のように、変数名の前に記号 $ を置く :


$(VAR_NAME)

makefile には、二種類の変数がある。繰り返し拡張変数(recursively expanded variable)と単純拡張変数(simply expanded variable)である。

繰り返し拡張変数では、make は拡張不能になるまでその変数を拡張し続ける。例えば:


TOPDIR=/home/tedi/project

SRCDIR=$(TOPDIR)/src

SRCDIR 変数は、先ず TOPDIR 変数を拡張することにより、拡張される。最終結果は、/home/tedi/project/src である。

しかし、繰り返し拡張変数は、次のコマンドには適当でない:


CC = gcc -o

CC = $(CC) -O2

繰り返し拡張変数を使うと、これらのコマンドは無限ループに入る。この問題を解決するため、単純拡張変数を用いる:


CC := gcc -o

CC += $(CC) -O2

':=' は、変数 CC を作る。'+=' はその値を付加する。
結びの備考
この講義で、Makefileを作るに充分な学習をした。その時まで、研鑽を。
文献
GNU Make Documentation File, info make. Kurt Wall他, Linux Programming 頒布,2001年.
 
Copyright © 2002, Tedi Heriyanto. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 83 of Linux Gazette, October 2002
 
 
 
 
Muttを用いる複数アカウントE-メール
By Kamil Klimkiewicz
 
三年か四年前、Windows から Linux に切り換えた。Linuxは使っていたが、第二OSとしてだった。主OSにしたとき、幾つかの問題にぶつかった。ほとんどは急速に解決出来た。面倒だったのは、e-メールアカウントを三つ持っていたことだった。
Windowsユーザーなら「e-メールクライアントをダウンロードして、数個のアカウントを使うよう構成しなさい」と言うだろう。だが「UNIX哲学」と言われるものがあって、「プログラマは一つのことだけを旨くやる小さいアプリケーションを書くべきだ」と言う。その意味は、メールを遠隔サーバーから取り出して、読み取り、書き込み、相手に送ることの出来る単一ツールは無いと言うことだ。
この短い記事で、fetchmail and mutt と言うツールの構成方法を示す。この問題に深入りしたい人は、
・Mail-Administrator-HOWTO,
・Mail-User-HOWTO.

を読まれたい。http://www.linuxdoc.org/ から入手することが出来る。

1.環境

e-メール環境を定義する:e-メールアカウントが三つある。それぞれ別のサーバーに置かれている。これらを「第一」「第二」「第三」と呼ぶ。それらのアドレスは、first@firstdomain.com, second@seconddomain.com, third@thirddomain.comで、第一アカウントは IMPプロトコルを使い、その他は POP3 を使っている。

メッセージ全部を受け取るローカルユーザは「john」である。(不安全で不便な)規定値 '/var/spool/mail/john' は使わないので、この人は、 $MAIL 環境変数用に新しい値を設定する必要がある。これをおこなうため、.bash_profile に次の行を追加する(別のシェルを使うなら、勿論、別のものを変更しなければならない):


MAIL=$HOME/Mail/Inbox

export MAIL

(ディレクトリ'$HOME/Mail' を作るのを忘れないこと!)。メッセージ読取用に追加のメッセージボックスを使用する(各アカウントに関連ボックスがある)。

2.Fetchmail

メールを読む前に、リモートサーバーから取り出さなければならない。それには、fetchmail と言うツールを使う。システムにはインストールされている筈だ。

fetchmailのコンフィギュアは極めて簡単だ。その上'fetchmailconf' を使うことが出来るので、更に容易になる。編集すべきコンフィギュレーションファイルは、$HOME/.fetchmailrc だ。簡単なもので、我々の環境に適切なものは次のようになる:


set postmaster "john"

set bouncemail

set properties ""

set daemon 300

poll First via firstdomain.com

 with proto IMAP

       user first there with password this_is_password is john here warnings 3600



poll Second via seconddomain.com

 with proto POP3

       user second there with password this_is_password is john here warnings 3600



poll Second via thirddomain.com

 with proto POP3

       user third there with password this_is_password is john here warnings 3600

fetchmail を走らせるには、fetchmail とタイプするだけだ。デーモンモードでスタートし、5分毎に新メールの到着をチェックする。

3.Mutt

メッセージがローカルマシンに入ったので、任意のメール・ユーザー・エージェントを使って読むことが出来る。この記事はmuttを扱うので、これはmuttであるとする。

Muttを好きなように働かせるには、コンフィギュアしておく必要がある。先ず、そのコンフィギュレーションファイル(通常 $HOME/.muttrc と言う)に幾つかの基本定義を入れなければならない。次のようになる:


set mbox = "~/Mail/Inbox"

set move = no

set folder = "~/Mail"

set record = +Sent

mailboxes +Inbox +First +Second +Third

これで実際にメールを読むことが出来るが、各出信メッセージのFromヘッダフィールドに john@localhost のようなものが入る。発信主アドレスを変更して、メッセージが firstdomain.com か seconddomain.com 又はアカウントのあるマシンのどれかからか、分かるようにしなければならない。

その目的で、追加メールボックス(第一、第二、第三)と、mutt の所謂るフック機構を使う。後者は、何かの作用が実行されたとき、ユーザ定義コマンドを実行する。ユーザが('c'キイを押して)メールフォルダを変えたとき呼び出される folder-hook がある。From フィールドを変更するには、from と ralname の mutt 変数を変えなければならない:


# Default action:

folder-hook . set from = first@firstdomain.com

folder-hook . set realname = First

# First account:

folder-hook First set from = first@firstdomain.com

folder-hook First set realname = First

# Second account:

folder-hook Second set from = second@seconddomain.com

folder-hook Second set realname = Second

# Third account:

folder-hook Third set from = third@thirddomain.com

folder-hook Third set realname = Third

We should also define the alternates variable so mutt can recognize messages sent to and by us:


set alternates = "first@firstdomain\.com|second@seconddomain\.com|third@thirddomain\.com"

注記: http://mutt.netliberte.org/ にMuttrcBuilder と言うツールがある。muttのコンフィギュアには、これを使うことが出来る。

 

Copyright © 2002, Kamil Klimkiewicz. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 83 of Linux Gazette, October 2002
 
 
 
 
Linux システムの複製−別法
By Ben Okopnik
複製の理由
複製とは、通常のインストール手順全体を繰り返す代わりに、搭載されたファイルシステムを一つのマシンから別のマシン(普通は新マシン)に写すことである。コンピュータの速度が改善されるので、数十時間も掛けてシステムをいじくり、好きなように調整したとき、ハードウエアは旧式になっている。複製により、コンフィギュア済みのシステムをそっくり移転して労力を節約することが出来る。
Linuxの下では、複製をおこなうための多数のオプションがあって、それらは、ここに示すように、方法をピッタリ状況に合わせる融通性と能力を備えているので、複製について独特の面白い経験させてくれる。他のOSは一般的に、たった一つの制限の多い方法に囚われている(実際はハードディスク内容を"dd"すること)。Linuxでは、必要な方法を何でも選ぶことが出来る。しかし−Linuxの他の何にでも通用するように−それなりの努力が要る。XFreeの人が言うように「方針を決めないで、機構を作れ」だ。しなければならないのは、与えられたメニュー駆動のアプリケーションを盲目的に走らせることでなく、a)手順を理解することと、b)そのためのツールを選ぶことだ。「小さい賢い」ツールが信条のUnixの世界では、これが仕事を進めるのに決められたレールだ。
一寸した愚行が永く続く
先に進む前に、警告:
これはLinuxシステム複製の標準方法ではない。下記に述べるのは、それが出来るかどうかの実験に過ぎない。私と同じ結果を再現することは出来るだろうし−私と同じように便利だと思う人もいるだろう−だが、やり損ねて、悲惨な結果を招いたからと言って、私を責めないで欲しい!
特に私を含む多くの人に取って、何かの働きを学ぶ最良の方法は、分解して組み立て直すことだ(5ドルの玩具が3日の労働と7ガロンの汗に変わるだろうが−満足だ)。数週間前に、誰かから古いコンピュータを貰った(Wind*wsなら良かったが、違った。働かせる方法はなかったが、その影響を楽しんだ。人々にP3-400/64MBは「旧式」だと思わせる。物を知らない人が沢山いるので、無料コンピュータの寿命を見ることが出来る。誰かが立派な486ゲートウエイ・ラップトップも呉れると良いのだが
勿論、私に関する限り、新コンピュータ=Linuxインストレーションだ。それは、そこいらにある食べ慣れた食物みたいなものだ。だが私のインストールCDは物凄く古いので、余り使いたくない。最近の私のネットアクセスは、PCの上でタバコをふかしながら1週間もつなぎ放しにするのも珍しくない。こんな生活を変えるのには、最新のCDセットを、CheapBytes.comに注文したが、手が空いたので、一寸した計画を立てた。
私のラップトップには、永い間掛けて自分の好きなように磨きあげたDebianがインストールしてある。全部を "dd”出来れば良いのだが、ラップトップドライブは12GBだ。掘り出したドライブは古い6GBで2GBのパーティションがあり、歴史的なデータと古いDebianがインストールされている。データをそのまま捨てたくないのでバックアップしたいが、12GBは6GBに入らない。良く考えよう。その12のほとんどは音楽コレクションだ。先頭に残そう。次に、「データ」:FTPアーカイブバックアップ、Comprehensive Perl Archive Network (CPAN)がある、これもそのままにしたい。一寸した意志決定で、プログラム全体が2GBに入る。名医の診断通りだが問題がある。パーミッション全部を保存しながら、どうやってここからそこに移すかだ。他にも問題があるかも知れない。
Linuxのインストールには沢山の標準方法がある。 CD又などのメディア、ネットワーク経由のアーカイブ又はループ搭載画像、同じサイズのドライバの間でのコピイ、及び私の希望に近いもの−類似システムの間のファイルシステムダンプだ(LG#68のJim Dennisのヒントを参照)。しかし、私はこれら全部をおこなわないで、片端から別のFSレイアウトに移した。旨くいったかって!私は自問自答した「まずければ、やり直せば良い」
暗い海に頭から飛び込む
この計画に使ったツールの一つは、LNX-BBC project からの Bootable Business Card だ。これを使って新マシンをブートし、"j"オプションを付けた "mke2fs" を使って(これは、新パーティションにジャーナルを作る、ラップトップでext3を使うのに私が必要としたものだ)Linuxの入る "/dev/hda2" を再度初期化した。次に、"/mnt/0" に "/dev/hda2" を取り付け、"insmod dmfe" (新デスクトップにはオンボードDavicomがあった)と入力してネットワークカードを起動し、それにIPアドレス("ifconfig eth0 192.168.0.5")を割り当てたら、ラップトップは準備を完了し、NetGear PCMCIA カードと渡り線のお陰で、192.168.0.3で待っていた。
ラップトップでは規定値でネットワーク・サービスを働かせない−ほとんどのインストールで先ずおこなうのは "inetd" を無効にすること−だがここでは、別の目的で "sshd" を使いたかった。そこで、Baldur (ラップトップ)に"su -c '/etc/init.d/ssh start'"のコマンド、Ymir (デスクトップ)に "rsync -e ssh -avz --exclude-from file.lst root@192.168.0.3:/ /" のコマンドを与えた。"file.lst" ("rsync" が無視すべきファイルとディレクトリ) と言う除外リストをは、短いもので、"/proc/" は "real" ファイルではなく、コピイの必要がない(コピイするのは余り良くない)、しかし、私は新しいパーティションに空の "/proc" ディレクトリを作った。"/lost+found/" も飛ばした−一つは "mke2fs" を走らせるときYmirの上に自動的に作られる(それにこれは簡単なだけのディレクトリではない。詳細は"mklost+found" を参照)。ジャーナル関係のファイル("/.journal" と"/.memdump")もBaldurからコピイしないよう注意した。これは、Ymir上のディスク状態に無関係なので、多分失敗の元になる。最後に、新システムにコピイしたくないディレクトリを加えた。

コピイをし終わると−100Mb/sでも時間が掛かる−Ymir上で "chroot /mnt/0 /bin/bash" をおこなった。これで、新ファイルシステムがルート("/")になり、そのシェル上で走る。旨くいった!「道程の4分の3」だと思った。重要なことで残っているのは、システムをブートすることが出来るようにすることだ。勿論システム特有の設定をしなければならない。"/etc/fstab" を修正して正しいパーティションを反映させること、"/etc/modules" を直して正しいものをロードすること、"/etc/lilo.conf" を調整すること、"/sbin/lilo" を走らせて正しい画像をブートすることだ。あっ・・・・。Baldurは、"initrd"経由でブートする。"initrd" 画像には、ルートとして「ピボット」されるパーティションの情報が含まれており、これはBaldurとYmirでは異なる。そこで、Baldurにジャンプして、 "mkinitrd" を "-r /dev/hda2" オプションで走らせた。これで新"initrd"画像がルートとしての正しいパーティションで構築される。これをYmirに"rsync"したら、旨くいくように思えた。

再ブートまでの最後のステップとして、"/sbin/lilo -v3"を走らせたら・・・おやおや、LILOの前のバージョン(そこにあった旧式のDebianディストリビューション)でブートセクタを作ったとのメッセージが出て、新しい方の書き換えが拒否された。普通そんな事はない筈だ。LILOのマニュアルには、初期インストールのためだけに使う筈の"QuickInst" スクリプトが説明してある。(LILOの"doc"ディレクトリ参照)。"QuickInst" はベーシックの設定中に働いており、私は簡単に書かれる "lilo.conf" に手を加えなかったので、私の設定に一致しかなかったのだ。これがブートセクタと書き直してしまったのだ。そこで "lilo.conf" の正しいバージョンを"/etc"にコピイし直し、あらためて"lilo -v3"を走らせた。

そこで再ブートし、BIOS設定を交換して、YmirがCDROMでなくHDからブートするようにした。少しエラーが出るのを見守っていると、ログインプロンプトが出た。これで九分通り完成だ。あと二つだけ。念のため言うと、エラーを生じたのは、"hdparm" ディスク同調パラメータを変更しなければならないこと、および古いモジュール一つが未だ"/etc/default/hotplug.usb"からロードしていたことだ。それ以外の残りは新システムの設定に似ているが、作業は少ない。見てみよう。

・違うビデオ・カード/モニタ用にXをコンフィギュアし直す
・サウンドハードウエア用に違うモジュールをロードする
・幾つかの "/etc" ファイル ("grep -rl Baldur /etc")中のホスト名を変更
・Ymir用新SSH-2 "fingerprints" のため"ssh-keygen"を走らせる
・Baldur上の "sshd" をシャットダウン
これが、したことの全部だ。一ヶ月以上掛かったが問題は全く生じなかった。旨く仕上げた振りをするため、この記事では沢山の事を省いた。 何かを忘れたため LNX-BBC CD と HD との切り換えは何度もおこなったが、上に述べたのは最短距離だ。二時間ほどの作業でLinuxシステムを好みに合わせて更新した。初めからおこなうともっと時間が掛かるが対して長くはならない。
処置後のレポート
これをおこなった結果、重要なことを幾つか学んだ。"initrd" ブートプロセスに関する知識を少々、LILO不具合を直す知識を少々、一つのシステムから別のシステムに移るとき影響を受けるのは何か−中でも最も重要なのは、この処理と落とし穴を扱う方法だ。これは私の「知恵袋」に入った。今これをタイプしているラップトップ−全く新品の Dell Inspiron −には、ほんの少し前まで Wind*ws XP が入っていた。クローンの作成に使った時間は、移動時間(12GBを横断するには少々時間が掛かる)を除くと10-15分だった。良い経験だった。Linuxで遊ぶのを楽しみ給え。

参考書

・"rsync", "ssh"/"sshd", "mke2fs"/"tune2fs", "chroot", "mkinitrd", "lilo" についてのマニュアル。
・"lilo" に関する文書 (/usr/share/doc/lilo/Manual.txt.gz)
・ LNX-BBC CD に関する文書
 
Copyright © 2002, Ben Okopnik. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 83 of Linux Gazette, October 2002
 
 
 
 
Perl モジュールの探検−パート2:
GD::Graphを用いるチャート作成
By Pradeep Padala
緒言
私の previous article on GD を読まれた方は、GDモジュールを使うチャート作成は面倒だと思われただろう。(あの記事には、Perl モジュールのロードについての一般情報も入っている)。Martien Verbruggenは、チャートを簡単に作る GD::Graph モジュールを作った。このモジュールには、棒グラフ、円グラフ、線グラフなど各種のチャートを作るための有用なファンクションがある。このモジュールは、ネットワーク統計、ウエブペイジアクセス統計など、動的チャートの作成にも極めて有用である。
この記事では、モジュールを使う一般的方法を説明し、各種チャートを作る例を示す。
GD::Graph モジュールの一般的使い方
チャートの作成に GD::Graph を用いる Perl スクリプトには以下のことが含まれる:
・データをアレーのアレーとして準備する(これについては更に後述)
・チャートの型を決める。次のような呼出を使う

    $mygraph = GD::Graph::chart->new($width, $height);

この場合、チャートは棒、線、点、点のつながり、混合又は円形とすることが出来る。例えば棒グラフが欲しいときは、次のようにする

    $mygraph = GD::Graph::bars->new($width, $height);

・必要に応じグラフのオプションを決める。これには「題名」、「X-ラベル」などが含まれる。チャート型特有のオプションを設定することも出来る。
・プロット・ファンクションを使ってグラフをプロットする。

    $myimage = $mygraph->plot(\@data);

・最後に、画像をファイルにセーブするか又はウエブに出力する。これはGDモジュールを使う画像セーブ using the GD module と同様である。

簡単な例

上述のステップにしたがって簡単なチャートを書いて見よう。このスクリプトは、画像をウエブペイジに出力するのに CGI を用いる。
(このリストのテキスト版)[Text version of this listing.]


#!/usr/local/bin/perl -w

# 上の行を自分の perl バイナリを指すよう変更する



use CGI ':standard';

use GD::Graph::bars;

use strict;



# 両アレーのエンリトリは同数でなければならない

my @data = (["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",

             "Sep", "Oct", "Nov", "Dec"],

            [23, 5, 2, 20, 11, 33, 7, 31, 77, 18, 65, 52]);



my $mygraph = GD::Graph::bars->new(500, 300);

$mygraph->set(

    x_label     => 'Month',

    y_label     => 'Number of Hits',

    title       => 'Number of Hits in Each Month in 2002',

) or warn $mygraph->error;



my $myimage = $mygraph->plot(\@data) or die $mygraph->error;



print "Content-type: image/png\n\n";

print $myimage->png;

プログラムの出力はここ here で見られる。

上のプログラムは、ほとんど自明であろう。@data 変数はアレーのアレーである。最初のアレーはX-軸のラベルをあらわし、それに続く残りのアレーは全部別のデータセットをあらわす。

オプションの採用

お分かりのとおり、上のプログラムが作るグラフは、平凡で簡単だ。オプションを工夫してグラフの見掛けを好みに合わせることが出来る。様々なオプションがあって、グラフの色々な側面を好きなように変えることが出来る。オプションは二つの型に分かれる。あらゆる型のグラフに共通なオプションと、グラフの各型に特有のオプションだ。

オプションはグラフを作っている間又は下記で設定することが出来る


    $mygraph->set(attrib1 => value1, attrib2 => value2, ...);

凡例、グリッド及び幾つかのオプションのスクリプトを書いて見よう。
(このリストのテキスト版)[Text version of this listing.]


#!/usr/local/bin/perl -w

# 上の行を自分の perl バイナリを指すよう変更する



use CGI ':standard';

use GD::Graph::bars;

use strict;



# 両アレーのエンリトリは同数でなければならない

my @data = (['Fall 01', 'Spr 01', 'Fall 02', 'Spr 02' ],

            [80, 90, 85, 75],

            [76, 55, 75, 95],

            [66, 58, 92, 83]);



my $mygraph = GD::Graph::bars->new(500, 300);

$mygraph->set(

    x_label     => 'Semester',

    y_label     => 'Marks',

    title       => 'Grade report for a student',

    # 幅3ピクセルの棒を描く

    bar_width   => 3,

    # 棒を4ピクセルで分離する

    bar_spacing => 4,

    # グリッドを示す

    long_ticks  => 1,

    # それぞれの棒の頭部に値を示す

    show_values => 1,

) or warn $mygraph->error;



$mygraph->set_legend_font(GD::gdMediumBoldFont);

$mygraph->set_legend('Exam 1', 'Exam 2', 'Exam 3');

my $myimage = $mygraph->plot(\@data) or die $mygraph->error;



print "Content-type: image/png\n\n";

print $myimage->png;

上のプログラムの出力はここ here で見られる。

背景にロゴを入れたグラフ

ここでも、GD::Graph が好みのグラフを作る方法を与えるのが分かる。ロゴの付いた別のチャートを作って見よう。


このファイルのテキスト版はここ here

#!/usr/local/bin/perl -w

# 上の行を自分の perl バイナリを指すよう変更する



use CGI ':standard';

use lib '/cise/homes/ppadala/mydepot/lib/perl5/site_perl';

use GD::Graph::bars;

use strict;



# 両アレーのエンリトリは同数でなければならない

my @data = (['Fall 01', 'Spr 01', 'Fall 02', 'Spr 02' ],

            [80, 90, 85, 75],

            [76, 55, 75, 95],

            [66, 58, 92, 83]);



my $mygraph = GD::Graph::bars->new(500, 300);

$mygraph->set(

    x_label     => 'Semester',

    y_label     => 'Marks',

    title       => 'Grade report for a student',

    # 幅3ピクセルの棒を描く

    bar_width   => 3,

    # 棒を4ピクセルで分離する

    bar_spacing => 4,

    # グリッドを示す

    long_ticks  => 1,

    # それぞれの棒の頭部に値を示す

    show_values => 1,

) or warn $mygraph->error;



$mygraph->set(logo => 'lglogo.png');

$mygraph->set(logo_resize => 0.5);

$mygraph->set(logo_position => 'LL');

$mygraph->set_legend_font(GD::gdMediumBoldFont);

$mygraph->set_legend('Exam 1', 'Exam 2', 'Exam 3');

my $myimage = $mygraph->plot(\@data) or die $mygraph->error;

print "Content-type: image/png\n\n";

print $myimage->png;

上のプログラムの出力はここ here で見られる。

ここでは Linux Gazette logo を使った。これは PNG フォーマットである。GD::Graph の現行版は、GIF以外の型の画像を認識しない(PNGは描くけれども、go figure)。これを直すため patch を送った。patchを適用するか又は古い版の GD 又は GD::Graph を使うと良い。

線を用いるグラフ

情報によっては線でグラフを示すのが良い。線グラフの例を示す。
(このリストのテキスト版)[Text version of this listing.]


#!/usr/local/bin/perl -w

# 上の行を自分の perl バイナリを指すよう変更する



use CGI ':standard';

use GD::Graph::lines;

use strict;



# 両アレーのエンリトリは同数でなければならない

my @data = (['Fall 01', 'Spr 01', 'Fall 02', 'Spr 02' ],

            [80, 90, 85, 75],

            [76, 55, 75, 95],

            [66, 58, 92, 83]);



my $mygraph = GD::Graph::lines->new(600, 300);

$mygraph->set(

    x_label     => 'Semester',

    y_label     => 'Marks',

    title       => 'Grade report for a student',

    # データセットを'実線', '破線' 及び '点線' で描く

    line_types  => [1, 2, 4],

    # 線の太さを設定

    line_width  => 2,

    # データセットのための色を設定

    dclrs       => ['blue', 'green', 'cyan'],

) or warn $mygraph->error;



$mygraph->set_legend_font(GD::gdMediumBoldFont);

$mygraph->set_legend('Exam 1', 'Exam 2', 'Exam 3');

my $myimage = $mygraph->plot(\@data) or die $mygraph->error;



print "Content-type: image/png\n\n";

print $myimage->png;

上のプログラムの出力はここ here で見られる。

ここでは GD::Graph::lines を使ってグラフハンドルを作った。だが、この変更のため、プログラムはグラフを作るのと同じパターンをたどる。

円グラフ

同じようにして円グラフを作ることが出来る
(このリストのテキスト版)[Text version of this listing.]


#!/usr/local/bin/perl -w

# 上の行を自分の perl バイナリを指すよう変更する



use CGI ':standard';

use GD::Graph::pie;

use strict;



# 両アレーのエンリトリは同数でなければならない

my @data = (['Project', 'HW1', 'HW2', 'HW3', 'MidTerm', 'Final'],

            [25, 6, 7, 2, 25, 35]);



my $mygraph = GD::Graph::pie->new(300, 300);

$mygraph->set(

    title       => 'Grading Policy for COP5555 course',

    '3d'          => 1,

) or warn $mygraph->error;



$mygraph->set_value_font(GD::gdMediumBoldFont);

my $myimage = $mygraph->plot(\@data) or die $mygraph->error;



print "Content-type: image/png\n\n";

print $myimage->png;

円グラフの出力はここ here で見られる。

'3d' オプションは円グラフを3次元で描く。

面積グラフ

面積グラフはデータを線の下の面積として示す。
(このリストのテキスト版)[Text version of this listing.]


#!/usr/local/bin/perl -w

# 上の行を自分の perl バイナリを指すよう変更する



use CGI ':standard';

use GD::Graph::area;

use strict;



# 両アレーのエンリトリは同数でなければならない

my @data = (["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",

             "Sep", "Oct", "Nov", "Dec"],

            [23, 5, 2, 20, 11, 33, 7, 31, 77, 18, 65, 52]);



my $mygraph = GD::Graph::area->new(500, 300);

$mygraph->set(

    x_label     => 'Month',

    y_label     => 'Number of Hits',

    title       => 'Number of Hits in Each Month in 2002',

) or warn $mygraph->error;



my $myimage = $mygraph->plot(\@data) or die $mygraph->error;



print "Content-type: image/png\n\n";

print $myimage->png;

出力画像はここ here で見られる。

結語

GD::Graph モジュールはチャートを作るための協力で融通性に富んだ方法を提供する。ウェブ上で役立てるためグラフを動的に作るのに極めて有用である。

この記事がお役に立ったことを祈る。来月は、PerlMagic モジュールを学ぶ。

 

Copyright © 2002, Pradeep Padala. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 83 of Linux Gazette, October 2002
 
 
 
 
snmptrapsを用いる電源状態の取扱い
By A B Prasad

この問題の新人へ

SNMPに対する新人は下記を参照されたい。

・net-snmp 文書
・rfc1678
・UPSHowto
・snmptrapd(8), snmptrapd.conf(8), snmptrap(8)のマニュアル

Net-snmp

Network Management Protocol に関する各種ツールは:

・増築可能エージェント
・SNMP ライブラリ
・SNMP エージェントからの情報を要求又は設定するためのツール
・SNMP トラップを作って取り扱うためのツール
・SNMPを用いる unix 'netstat' コマンドのバージョン
・mibブラウザに基づくグラフィック Perl/Tk/SNMP ....

NET-SNMP site を参照。

Snmptrapd

Snmptrapd は、SNMP-TRAP ポート(162)に送られれた snmp トラップメッセージを受信し記録する SNMPアプリケーションである。これは snmp トラップ受信に際し特殊プログラムを走らせるためコンフィギュアすることが出来る。

snmptrapd.conf

snmptrapd.conf は、トラップを受信したとき、SNMPトラップ受信デーモンの働く方法を定義するコンフィギュレーション・ファイルである。

ups−MIB

RFC1628 文書は、単純ネットワーク管理プロトコル(Simple Network Management Protocol (SNMP))経由で管理することの出来る無停電電源装置用管理オブジェクトを定義する。

Powerh を用いる snumptrapd の使い方

注記:'powerd' を'powerh' と言い換えたのは、ここではこれはデーモンではなくトラップ取扱ルーチンに過ぎないからである。

powerhを用いてシステムの電源状態を取り扱って来た。powerhは、シリアルポートを通じてUPSと連絡する。しかし、多数のマシンがUPSを使うネットワークシステムでは、各システムがUPSを使って直接連絡することは出来ない。最新高容量UPSは、直接又はプロキシを通じるのいずれかでSNMPプロトコルをサポートする。各種電力状況の取扱いは下記のステップにしたがう。

1. 自分の snmptrapd.conf に次の行を追加する


traphandle 33.2.3 powerh b

traphandle 33.2.4 powerh p

2. cc powerh.c -o powerhand copy powerh を入力して、 /usr/local/sbin/などのパスにあるディレクトリに、次の C code をコンパイルする。
(このリストのテキスト版)[Text version of this listing.]


#include <string.h>

#include <unistd.h>

#include <stdio.h>

#include <fcntl.h>

#include <signal.h>





#define PWRSTAT "/etc/powerstatus"



void powerfail(int);



main(int argc, char* argv[]) {

    char s[1000];

    int i=0;

    while(i<7) {

        scanf("%s",s);

        i++;

    }

    scanf("%s",s);

    if (!strcmp("b",argv[1]))

        if ((!strcmp(s,"33.1.6.3.3"))||(!strcmp(s,"upsMIB.upsObjects.upsAlarm.upsWellKnownAlarms.upsAlarmLowBattery")))

            powerfail(1);

    if (!strcmp("p",argv[1]))

        if ((!strcmp(s,"33.1.6.3.3"))||(!strcmp(s,"upsMIB.upsObjects.upsAlarm.upsWellKnownAlarms.upsAlarmLowBattery")))

            powerfail(0);

}

/* このプログラムは警報の場合にも起動されるので、内側の 'if' が必要 */

void powerfail(int event) {

    int fd;

    unlink(PWRSTAT);

    if ((fd = open(PWRSTAT, O_CREAT|O_WRONLY, 0644)) >= 0) {

          switch (event)

           {

           case 0:

                  write(fd, "OKWAIT\n", 7);

                   break;



             case 1:

                    write(fd, "FAIL\n", 5);

                     break;

             }

          close(fd);

       }

       kill(1, SIGPWR);

}





3. 自分のシステム上で snmptrapd を走らせる(これはinitスクリプトコンフィギュアすることが出来る)

システムは、UPSから「バッテリ電圧低下警報」を受けた二分後に停止する。次いで、停止二分前に電源が良くなると、停止を中止するか、又は/etc/inittabのpowerfail 及び powerokwait 行でコンフィギュアした通りにする。

コードの説明

trap 33.2.3 (upsMIB.upsTraps.upsTrapAlarmEntryAdded) を受け取ったとき、プログラムは、'b' オプションで実行される。プログラムはtrapの送る 'upsAlarmId' をチェックし 33.1.6.3.3 (upsMIB.upsObjects.upsAlarm.upsWellKnownAlarms.upsAlarmLowBattery)であれば、initに 電源不具合発生を通知する。バッテリの残留稼働時間がupsConfigLowBattTime以下であるとこれをUPSエージェントがアラームテーブルに追加する。これは電源が元に戻ったことをtrap 33.2.4.が認識したとき削除される。プログラムは次いで init に powerokwait を送る。

欠点

・このプログラムは、trap二つだけを取扱い警報型一つだけを探す。upsMIB には多数の警報があるので、プログラムはすべての条件に拡張すべきである。
・情報をinitに与えるのに陳腐な方法を使っている。これは変更すべきだ。
・snmp trap を送る UPS がなかったので snmp trap ゼネレータを使った。

snmptrap -v 2c localhost public '' 33.2.3 33.2.3.0 s "33.1.6.3.3"
snmptrap -v 2c localhost public '' 33.2.4 33.2.4.0 s "33.1.6.3.3"

これが正しいかどうかは不明

・snmptrapd は -f オプション無しで走らせる。
・RedHat Linux 6.2 でのみテストした。
なすべきこと
完成一般目的UPS管理ソフトウエアにこの数行を入れと、次の事が出来ると思う:
・upsMIB ノードの可能なMIB全部の監視と変更
・複数 UPS からの信号の取扱い
・コンフィギュレーションファイルからデータを使用
・認証の取扱い

あらゆる種類のご意見歓迎 mailto://prasad_ab@yahoo.com へどうぞ。私の home page も見られたい。

 

Copyright © 2002, A B Prasad. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 83 of Linux Gazette, October 2002

 
 
 
 
Rubyでのプログラミング、パート2
By Hiran Ramankutty
 
復習
異なる分野からの各種アプリケーションが色々なレベルの構成を必要とする。Part 1パート1でRubyの基本が分かったので、次のレベルの構成に進む。
規則的表現
Rubyでは、規則的表現をPerl及びawkのように引用符でなく '/' で囲む。規則的表現は、(パターン一致など)パターンを取り扱うときはいつでも、効率の良い表現力を持っている。メソッドの中には文字列を規則的表現に転換するものもある。

print "abcdef" =~ /de/,"\n"

print "aaaaaa" =~ /d/,"\n"

^D

3

FALSE

演算子 `=~' は規則的表現に関する整合演算子である。これは、整合の見出された文字列ないの位置を返すか、又はパターンが一致しないときは空白を返す。規則的表現が下に示すように特定種類の語彙を共有するのが分かるのは面白い:


   

  [ ]     範囲規定。(例えば, [a-z] はaからzの範囲の文字を意味する)

  \w      文字又は数字。[0-9A-Za-z_]と同じ。

  \W      文字でも数字でもない。

  \s      空白文字。[ \t\n\r\f]と同じ。

  \S      空白でない文字

  \d      数字。[0-9]と同じ。

  \D      数字でない文字。

  \b      単語の境界 (範囲規定の外側)。

  \B      単語の境界でない。

  \b      後退 (0x08) (範囲規定の内側)

  *       次に続く表現のゼロ回以上の繰り返し

  +       次に続く表現の1回以上の繰り返し

  {m,n}   次に続く表現の最低n回で、m回以上ではない繰り返し

  ?       次に続く表現の最低0回で、1回以上ではない繰り返し

  |       前又は後の表現のいずれかが一致する

  ( )     グループ分け

例えば、'^f[a-z]+' は、「'a'から'z'までの範囲の文字表現に続くf」を意味する。ここで、文字列が示された説明例えば「正確に一つの大文字に続く小文字'f'で始まり、その後に続く小文字がなくなるまで」の文字列を点検したいとしよう。Cでは、数十行を書かなければならない筈だ。面倒だ。Rubyでは、規則的表現/^f[A-Z](^[a-z])*$/との一致を文字列に要求するだけで済む。文字列における規則的表現のこの能力は、Unix環境で使われることが多い。典型的な例は `grep' だ。規則的表現に慣れて見よう。次の例を考える:


 #これを regx.rb として記憶する

 st = "\033[7m"

 en = "\033[m"

     

 while TRUE

	print "str> "

	STDOUT.flush

	str = gets

	break if not str

	str.chop!

	print "pat> "

	STDOUT.flush

	re = gets

	break if not re

	re.chop!

	str.gsub! re, "#{st}\\&#{en}"

	print str, "\n"

end

print "\n"

#ここで ruby regx.rb を走らせる。

このプログラムは、二度の入力を必要とする。文字列一度と規則的表現一度だ。テストは規則的表現に対する文字列につき実行し、一致した部分をリバースビデオで強調する。これは、リバースビデオ・エスケープ・シーケンスを使うので、ANSIターミナルを必要とする。プログラムの詳細を気ににないこと。


str>foobar

pat>^fo+

foobar

~~~

foo が逆転しているのが分かる。 Note that ``~~~'' は、テキストベースのブラウザーのために過ぎないことに注意。違う入力に付いて実験する。


str>asd987wonew06521

pat>\d

asd987wonew06521

   ~~~     ~~~~~

str>foozboozer

pat>f.*z

foozboozer

~~~~~~~~

foozbooz は一致して fooz はそうでないことに注意。これは、規則的表現が可能な最も長い文字列 と一致させるためだ。一瞥して解釈は難しい。次を試そう:


str> Wed Feb  7 08:58:04 JST 1996

pat> [0-9]+:[0-9]+(:[0-9]+)?

Wed Feb  7 08:58:04 JST 1996

           ~~~~~~~~

ここで、規則的表現を使って16進数を表して見る(0x123af00c や 0Xbc13590aeは16進数だ)。


def chab(s)   # "括弧内に16進数を含む"

	(s =~ /<0(x|X)(\d|[a-f]|[A-F])+>/) != nil

end

print chab "Not this one."

print "\n",chab "Maybe this? {0x35}" # 間違った種類の括弧の使用

print "\n",chab "Or this? <0x38z7e>" # これは HEX 数であるか

print "\n",chab "Okay, this: <0xfc0004>."

print "\n"

^D

false

false

false

true

イテレータ

イテレータとは「同じことを何度もおこなうもの」を意味する。次のCコードを考える:


char *str;

for (str = "abcdefg"; *str != '\0'; str++) {

  /* process a character here */

}

ループを作るためのCの for(...) 構文が与える抽象概念には留意するが、事実上、プログラマはnull文字の付いた *str をテストするには文字列の内部構造を知らなければならない。次のシェルスクリプト (/bin/sh) を考える:


for i in *.[ch]; do

  # ...  ファイル毎になすべきこと

done

カレント・ディレクトリの中のCソース及びヘッダファイル全部が処理され、コマンドシェルは、ファイル名の獲得と置換の詳細を取り扱う。これはCより高いレベルで働いていないだろうか?君はどう考えるか?

埋込データ型のためのプログラム言語にイテレータを備えるのは、好ましいとの事実を考えながらも、我々のデータ型を繰り返すため低レベルループを書かなければならないとすると失望する。00Pにおいて、これは容易でない問題となる。ユーザはデータ型を一つ宛次々に決めるからだ。

上の問題を解くには、各00P言語は繰り返しを容易にする方法を磨き上げなければならない。例えば、若干の言語ではクラスを制御する繰り返しを備えている、などである。他方、rubyによると制御構造を直接定義することが出来る。rubyの用語では、このようなユーザ定義制御構造をイテレータと呼ぶ。

数個の例を示す:


"abc".each_byte{|c| printf "%c\n", c}

^D

a

b

c

ここでは、each_byte が文字列中の各文字に関するイテレータである。ここではローカル変数 'c' が用いられ、各文字がそれに代入される。これはCコードに良く似たものに解釈することが出来る...


s="abc"

i=0

while i < s.length

	printf "%c\n",s[i]

	i+=1

end

^D

a

b

c

... しかし、each_byte イテレータの方が概念的に簡単で、ストリングクラスが将来激しく修正されることがあっても、引き続き働くと思われる。このイテレータの一つの利点は、このような変更に際して堅牢であることで、これは良いコードの特性であると考える。

文字列の別のイテレータは each_line である。


"a\nb\nc\n".each_line{|l| print l}

^D

a

b

c

行に関するデリミタの発見、補足文字列の発生などのような退屈な作業のそれぞれをイテレータが取り上げる。

forステートメントを使ってこの例を書いて見る。


for l in "a\nb\nc\n"

        print l

end

^D

a

b

c

for ステートメントは、各イテレータを用いて繰り返しをおこなう。文字列はそれぞれ前の例で見た each_line と同じように働く。

今の繰り返しは、一つの繰り返しループとの関連で制御構造体 'retry' を用いて最初からやり直すことが出来る。下記を参照:


c = 0

for i in 0..4

	print i

     if i==2 and c==0

		c = 1

          print "\n"

          retry

     end

end

^D

012

01234

イテレータの定義には、`yield' が発生することがある。これは、イテレータに渡されたコードのブロックに制御を移動する(後で詳細が分かる)。下記の例は、イテレータ繰り返しを定義する。これは引数で規定された回数だけコードのブロックを繰り返す。


def repeat(num)

	while num < num

		yield

		num-=1

	end

end

repeat(4) {print "hello world\n"}

^D

hello world

hello world

hello world

これでハッキリしなければ、`yield' が生じる前と後にnumの値をプリントしてみる。

`retry' を用いて、`while' と同じ働きのイテレータを定義することが出来るが、遅いので実用的ではない。


def MYWHILE(cond)

	return if not cond

	yield

	retry

end

i = 0

MYWHILE(i<3) {print i,"\n" ;i+=1}

^D

0

1

2

ここまでで、イテレータの考えがほとんど分かった筈だ。少し制限があるものの、自分で独自のイテレータを書くことが出来る筈で、事実、新しいデータ型を定義するときはいつでも、それに伴う適切なイテレータを定義するのが便利だ。その意味では、上例の `repeat() と `MYWHILE()' は余り有用ではない。クラスを良く理解した後に、実用的なイテレータを説明する。

オブジェクト指向の考え方

`オブジェクト指向' は全く覚えやすい熟語だ。Ruby はオブジェクト指向スクリプト言語であると言うが、「オブジェクト指向」の正確な意味は何だろう?

この疑問には色々な回答がある。それらすべてが、多分同じことに煮詰まるだろう。慌てて議論をまとめる前に、伝統的プログラム体制を一寸考えて見よう。

伝統的に、プログラム作成上の問題は、ある種のデータ表現、及びそのデータに作用する手順に伴って浮かび上がることにより取り組まれて来た。このモデルの下の「データ」には不活性、受動的、及び孤立無援の用語を結合することが出来るが、そのデータは完全に、活性、論理的、及び強力の用語が結合される大きい手順上の本体のなすがままであった。

この方法に伴う問題は、人間に過ぎなくて頭の中に一時に沢山の内容を明確に保つだけのプログラマがプログラムを書くことである。プロジェクトが大きくなるにつれ、手順の核は、全体の働きを覚えるのが困難な点に成長する。考えの一寸した間違いと印刷上の過ちが、全く隠されたバグになることがある。複雑で予期しない相互作用が手順上の核の中に現れはじめ、不気味に活動する。この伝統的領域の中でバグを最少にし見つけ出すのを助けることの出来るプログラム作成用指針があるが、作業の方法を根本的に変える良策がある。

オブジェクト指向がおこなうことは、平凡な繰り返し論理作業をデータ自体に委せることである。データの概念を受動から能動に変えることが出来る、言い換えると、

・データの各片を、手を突っ込んで中のものを周りに散らかす蓋の開いた箱として扱うことを、止める。
・データの各片を、蓋が閉じていて良く印の付いたスイッチとダイアルのある作業機械として、扱うことから始める。

上に「機械」と述べたものは、内部が極めて簡単なことも複雑なこともある。外からはわからないので、機械を自分で開くことはしない(その設計に何かの間違いがあると確信があるときを除く)、だから、データに働き掛けるためすることは、スイッチを入れてダイアルを読むのに似ている。一旦機械が構築されると、その作用方法は考えない。

沢山の作業をしようとしていると思われるだろうが、この方法は、あらゆる種類の事柄が間違った方向に進むのを防止する。

実用価値には簡単過ぎるが、少なくとも考え方の一部を示す例から始める。私の二輪車には距離計が付いている。その役割はリセットボタンを押してからの走行距離を追跡することだ。これをプログラム言語ではどのようなモデルにするだろうか?Cでは、距離計は、多分浮動小数点の只の数値変数である。プログラムはこの変数を、適当な時期にゼロにリセットしながら、小さい増分で増やして操作する。間違いがあるだろうか?このプログラムのバグは、予期しない色々な理由で変数に偽の値を割り当てる。Cでプログラムしたことのある人は知っているように、見付けて見れば簡単に思われる原因のバグを追跡するのに数時間も数日も掛かることがある。(バグを見付けた瞬間に大方の人は、額を叩く)

オブジェクト指向の文脈では、同じ問題に別の方法で取り組む。距離計を設計するプログラマは「慣れたデータ型のうちどれがこれに最も近いくなるか」とは聞かないで、「これは正確にどんな作用とするか?」と聞く思われる。この相違は意味深なものとなる。走行距離計が何で、外界はそれとどんな相互作用をするか、一寸考えてみよう。我々は、刻みで増加し、リセットし、その値を読み取ることが出来る他には何もしない制御を有する小機械を構築すると決める。

我々は、距離計に勝手な値を割り当てる方法は作らない。距離計はそんな風の働きをしないからだ。距離計に対して出来ることは限られており、それら全部は分かっている。だから、プログラム中の何か別のものが何か別の値(車両温度制御の目標値など)を間違って距離計に入れようとしたら、何を間違ったか即座に表示する。プログラムを走らせているとき(言語によっては多分コンパイルのとき)距離計オブジェクトに勝手な値を割り当てることは許されていないことを通告される。メッセージはこれ程明確でないかも知れないが、それに近い筈だ。これはエラーを防止しない。だが、原因の方向を迅速に指し示す。これは00プログラム作成が無駄な時間を節約する幾つかの方法の一つに過ぎない。

これを越える抽象化の一歩を踏み出す。多くの機械を作る工場を建てるのは、個別の機械を作るのと同じように容易だからだ。単一の距離計を直接作ることはない、むしろ、任意の数の距離計を単一のパターンで作る手配をする。そのパターン(お好みなら距離計工場)は、クラスと呼ぶものに相当し、工場はオブジェクトに相当する。ほとんどの00言語は、新しい種類のオブジェクトを持つことが出来る前にクラスが定義されることを必要とする。だがrubyでは必要がない。

00言語の使用は、正しい00設計を強化しないとの事実を強調したい。実際、どの言語においても、不明確で、ずさんで、計画の悪い、バグだらけで、ガタガタのコードを書くことが出来る。rubyに出来ることは(特にC++に対して)、00プログラム作成の実用を充分自然な感じにするので、小規模の作業をするときでも、労力を節約するため醜いコードの助けを借りる必要を感じない。この案内書が進むにつれて、rubyがこの好ましい目的を達する方法を論じる。次の話題は、「スイッチとダイアル」(オブジェクト・メソッド)で、そこから「工場」(クラス)に移る。ご一緒なさいますか?

 

Copyright © 2002, Hiran Ramankutty. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 83 of Linux Gazette, October 2002
 
 
 
 
Ptraceを用いるプロセス追跡、パート2
By Sandeep S
 
ptraceの基本特性はパートI Part I で説明した。小さい例も見た。前に言ったように、ptraceの主用途は走っているプロセスのメモリ又はレジスタにアクセスすることである。(デバッグか不吉な目的かいずれかのため)そこで先ず、実行可能プログラムのバイナリフォーマットに関する基本的考え方を持たなければならない−このとき知っているのはアクセスする方法と場所だけである。だから、ELF、Linuxで使われるバイナリフォーマットに関する解説を示す。この記事の最後の部分では、別のもののレジスタとメモリにアクセスするプログラムを見出し、余分のコード幾つかを注入して、そのプロセスの出力を変更するようそれを修正する。

注記: 混乱しないで頂きたい。これはptraceに関する記事であって、ELFの記事ではない。だが、ELFに関する基本知識はプロセスのコアイメージに近づくのに必要である。だから、それを先ず説明しなければならない。

1.ELFとは?

ELFはExecutable and Linking Format(実行可能リンクフォーマット)の略である。これはLinuxで使用される実行可能バイナリのフォーマットを定義し−リロケータブル、共有オブジェクト及びコアダンプファイルのフォーマットもまた定義する。ELFはリンカーとローダーの両方で使用する。これらは、ELFを両側から見るので、双方とも共通のインターフェイスを持たなければならない。

ELFは、多くのセクションとセグメントを持つ構造となっている。リロケータブル・ファイルはセクション・ヘッダ・テーブルを、実行可能ファイルはプログラム・ヘッダ・テーブルを、共有オブジェクトファイルは両方を持っている。これらヘッダについては後の節で説明する。

2.ELFヘッダ

すべてのELFファイルはELFヘッダを有する。これはファイル中で常にオフセット0から始まる。これにはバイナリファイルの詳細が含まれ−ファイルに関連するデータ構造などに、翻訳される。

ヘッダのフォーマットはを下に示す( /usr/src/include/linux/elf.hから採用)



#define EI_NIDENT       16



typedef struct elf32_hdr{

  unsigned char e_ident[EI_NIDENT];

  Elf32_Half    e_type;

  Elf32_Half    e_machine;

  Elf32_Word    e_version;

  Elf32_Addr    e_entry;  /* Entry point */

  Elf32_Off     e_phoff;

  Elf32_Off     e_shoff;

  Elf32_Word    e_flags;

  Elf32_Half    e_ehsize;

  Elf32_Half    e_phentsize;

  Elf32_Half    e_phnum;

  Elf32_Half    e_shentsize;

  Elf32_Half    e_shnum;

  Elf32_Half    e_shstrndx;

} Elf32_Ehdr;


ファイルに関する説明は次の通り

1.e_ident : バイナリの扱い方に関する情報を含む。プラットホームに左右される。

2.e_type : バイナリのタイプ及び使い方に関する情報を含む。タイプはリロケータブル、実行可能、共有オブジェクト及びコアファイル。

3.e_machine : 推測通り、このフィールドは、アーキテクチャ−Intel 386, Alpha, Sparc など−を規定する。

4.e_version : オブジェクト・ファイルのバージョンを示す。

5.e_phoff : スタートから最初のプログラム・ヘッダまでのオフセット。

6.e_shoff : スタートから最初のセクション・ヘッダまでのオフセット。

7.e_flags : プロセッサ固有のフラッグ、i386 では使用しない。

8.e_ehsize : ELF ヘッダのサイズ。

9.e_phentsize & e_shentsize : それぞれプログラム・ヘッダとセクション・ヘッダのサイズ。

10.e_phnum & e_shnum : プログラム・ヘッダとセクション・ヘッダの数。プログラム・ヘッダ・テーブルはプログラム・ヘッダのアレーとなる(e_phnum 要素)。セクション・ヘッダ・テーブルの場合も同様。

11.e_shstrndx : セクション・ヘッダ・テーブルには、セクション名が含まれる。これはテーブル中のそのセクションに対するインデキス(下記を参照)

3.セクションとセグメント

上述のように、リンカーはファイルを、セクション・ヘッダ・テーブルが記述する論理セクションのセットとして取り扱い、ローダーはファイルを、プログラム・ヘッダ・テーブルが記述するセグメントの組として取り扱う。以下の節で、セクションとセグメント/プログラム・ヘッダの詳細を示す。

3.1 ELFセクションとセクション・ヘッダ

バイナリ・ファイルは、バイトが二重になっていないバイトのアレーであるセクションのコレクションであると見なされる。セクション内容を正しく解釈するためヘッダ情報があるけれども、アプリケーションはそれを自分の方法で解釈する。

セクション・ヘッダのアレーであるセクション・ヘッダ・テーブルがある。テーブルの0番登録は常にNULLでバイナリのどの部分も記述しない。各セクション・ヘッダは次のフォーマットを有する:(/usr/src/include/linux/elf.hから採用)



typedef struct elf32_shdr {

  Elf32_Word sh_name;           /* セクション名, 文字列テーブルのインデキス (yes Elf32) */

  Elf32_Word sh_type;           /* セクションのタイプ (yes Elf32) */

  Elf32_Word sh_flags;          /* セクションの雑属性 */

  Elf32_Addr sh_addr;           /* 実行時のセクション仮想アドレス */

  Elf32_Off sh_offset;          /* セクション・ファイル・オフセット */

  Elf32_Word sh_size;           /* セクションのバイト・サイズ */

  Elf32_Word sh_link;           /* 別のセクションのインデキス (yes Elf32) */

  Elf32_Word sh_info;           /* 追加セクション情報 (yes Elf32) */

  Elf32_Word sh_addralign;      /* セクション・アライメント */

  Elf32_Word sh_entsize;        /* セクションがテーブルを保持するとき登録数 */

} Elf32_Shdr;


ファイルの詳細を下記。

1.sh_name : これは e_shstrndx 文字列テーブルのセクション内容へのインデキスを含む。このインデキスは、セクション名として使われる null 終了文字列のスタートである。沢山あるが、数個を示す。

・.text このセクションは、プログラムの実行可能命令を保持する
・.data このセクションは、プログラム画像に役立つ初期化データを保持する。
・.init: このセクションは、プロセス初期化コードに役立つ実行可能命令を保持する。

2.sh_type : プログラム・データ、記号テーブル、文字列テーブルなどのセクションタイプ。

3.sh_flags : セクション内容の取扱い法などの情報を含む。

4.sh_addralign : セクション内容のアライメント要件を含む、一般的に 0/1(両方ともアライメント無し)又は4である。

残りのフールドは自明であると思われる。

3.2 ELFセグメントとプログラムヘッダ

ELFセグメントはロード中、つまりプロセスのイメージが

The ELF segments are used during loading ie, when the image of the process is made in the core. Each segment is described by a program header. There will be a program header table in the file (usually near the ELF header). The table is an array of program headers. The format of the program header is as follows.



typedef struct

{

  Elf32_Word    p_type;                 /* セグメント・タイプ */

  Elf32_Off     p_offset;               /* セグメント・ファイル・オフセット */

  Elf32_Addr    p_vaddr;                /* セグメント仮想アドレス */

  Elf32_Addr    p_paddr;                /* セグメント物理アドレス */

  Elf32_Word    p_filesz;               /* ファイルで表したセグメント・サイズ */

  Elf32_Word    p_memsz;                /* メモリで表したセグメント・サイズ */

  Elf32_Word    p_flags;                /* セグメント・フラッグ */

  Elf32_Word    p_align;                /* セグメント・アライメント */

} Elf32_Phdr;


1.p_type : 内容の取扱い法に関する情報を示す。次のようにプログラム・ヘッダのタイプを示す

・unused
・loadable
・Dynamic linking information
・reserved

など..

2.p_vaddr : セグメントがロードされると予想される相対仮想アドレス

3.p_paddr : セグメントがメモり内にロードされると予想される物理アドレス

4.p_flags : 保護フラッグを含む - 読取/書込/実行許可

5.p_align : メモリ中のセグメントに関するアライメントを含む。セグメントのタイプがローダブルであると、アライメントは予想ペイジサイズとなる。

残りのファイルは自明であると思われる。

4.ELFファイルのロード

ELFオブジェクト・ファイルの考え方が少し分かった。ここで、これらのファイルを実行のためロードする方法と場所を知らなければならない。通常は、シェルプロンプトでプログラム名をタイプするだけだ。実際は、リターンキイを押した後、興味あることが沢山起こる。

先ず、シェルが標準libcファンクションを呼び出し、これが次いでKernelルーチンを呼び出す。ここでボールはkernelに渡される。kernelがファイルを開き実行可能ファイルのタイプ/フォーマットを見出す。次いでELFと必要ライブラリをロードし、プログラムのスタックを初期化し、最後に制御をプログラムコードに渡す。

プログラムは、0x08048000 (これは /proc/pid/maps に見出すことが出来る)にロードされ、スタックは0xBFFFFFFF (スタックは数値的に小さいアドレスから伸びる)から始まる。

5.コード注入

プログラムがメモりにロードされる詳細を見た。そこで、プログラムが与えられ、そのメモリ空間が分かったとき、(パーミッションを持っていれば)それを追跡してプロセスのプライベートデータ構造体にアクセスすることが出来る。これは、言うは易いが、為すのは難い。やってみよう。

先ず最初に、別のプログラムのレジスタにアクセスして、それを変更するプログラムを書く。ここで要求に関する次の値を使用する。

・PTRACE_ATTACH : プロセス pid に付着。
・PTRACE_DETACH : プロセス pid から切り取り。

注記 : これを呼び出すのを忘れないこと。でないと、プロセスは停止モードのままに止まり復旧が難しくなる。

・PTRACE_GETREGS : これはプロセスのレジスタをデータの指定する構造体にコピイする (アドレスは無視)。この構造体は、asm/user.hで、struct user_regs_struct と定義された構造体である。


struct us