ファストユーザスイッチに対応する


ファストユーザスイッチは、ユーザが彼らのユーザアカウントにアクセスしようとする際に、毎回ログアウトすることなく、単一のマシンをユーザに共有させます。

ユーザは同じキーボード、マウス、モニタを含む、マシンへの物理的なアクセスを共有します。けれども、ログアウトする代わりに、新しいユーザは単純にログインして、以前のユーザは切り替えられます。

切り替えられた【switched-out】ログインセッション内のプロセス群は、以前と同様に実行を続けます。これらはデータの処理、システムとの通信、画面バッファへの描画も以前と同様に続けることができます。

けれども、これらのプロセスは切り替えられたため、キーボードやマウスからの入力を受け取ることはありません。同様に、もしこれらがチェックされたら、モニタはスリープモードで現れるかもしれません。

結果として、システムリソースの浪費を避けるために、切り替えられた状態における振る舞いを調整したアプリケーションは、利益をもたらすでしょう。

ファストユーザスイッチはユーザには便利な機能である一方で、アプリケーション開発者には幾つかの試練を与えます。特定のリソースへの排他的アクセスに依存しているアプリケーションは、ファストユーザスイッチ環境で動作するために、その振る舞いを変更する必要があるかもしれません。たとえば、一時データを /tmp に記録するアプリケーションは、そのディレクトリにある同じファイルを変更しようとする異なるユーザの下で第二のインスタンスが実行されたときに、問題が発生するかもしれません。

ファストユーザスイッチ対応のためのガイドライン

ファストユーザスイッチに対応するために、あなたのアプリケーション開発においてあなたが従うべき、いくつかのガイドラインが存在します。これらのガイドラインのほとんどは、システムリソースを識別し、共有するための安全な方法について説明したものです。これらガイドラインの概要は以下の通りです…

  • セッションIDを、以下を含む、グローバル名前空間に現れるどのエンティティの名前に対しても結合するようにしてください。
    • ファイル名
    • 共有メモリ領域
    • セマフォ
    • 名前付きパイプ
  • ユーザ切り替え通知を受け付けて、処理してください。詳細は「ユーザ切り替えの通知」(19ページ)を参照してください。
  • あなたはいかなるリソースに対しても排他的アクセスを持っていると想定してはいけません。以下の項目に対しては特にです…
    • TCP/IPポート
    • ハードウェア装置
  • ユーザ単位のサービスの、実行中のインスタンスが一つだけであると仮定してはいけません。
  • ファイルにアクセスするときは、ファイルレベル、もしくは範囲レベルのロックを使用しましょう。

アプリケーション固有のリソースにセッションIDでタグ付けすることは、別のセッションにおけるアプリケーションによって作成された同様のリソースからこれらを区別するために必要なことです。

システムのSecurityレイヤは、各ログインセッションごとにユニークなIDを割り振ります。このIDをキャッシュファイル名や一時ディレクトリ名に結合することにより、これらのファイルを作成する際の名前空間の衝突を防ぐことができます。セッションIDをどのように取得するかの詳細は、「ログインセッション情報を取得する」(14ページ)を参照してください。

TCP/IPポートといった共有システムリソースを取得することは、ファストユーザスイッチ環境に別の問題を持ち込みます。たとえば、もしあなたがチャット接続を確立していたとして、新しいユーザが入ってきたときにそのポートを保持しておきますか、それともそのポートを開放しますか?

こうした状況をあなたがどのように取り扱うかは、関連するリソースと、あなたのアプリケーションの設計に依存します。これをあなたがどう扱うかに関わらず、あなたはいつユーザスイッチが起こるのかを知る必要があります。そしてこのために、アプリケーションに対して使用可能な通知を説明している、「ユーザ切り替えの通知」(19ページ)のセクションをあなたは読む必要があるでしょう。

ログインセッション情報を取得する

もしあなたのアプリケーションがファイル、共有メモリ領域、その他のグローバル名前領域に属するオブジェクトを作成するのであれば、あなたはそのオブジェクトにあなたのユニークなセッションIDを記す必要があります。

Mac OS Xはセッション情報へのアクセスを、二つの異なるフレームワークから提供します。

Securityフレームワークは、ログインセッションについての基本的な情報と一緒にセッションIDを取得する関数を含んでいます。

Core Graphicsフレームワークは、ユーザ名やログイン過程が完了しているかどうかといった内容を含む、ログインセッションについての補足情報を提供します。

セッションID使用のガイドライン

セッションIDはセッションに対してユニークなので、セッション固有ではあるけれども、同じログインセッション内のエンティティによって共有されていることがあるようなリソースを識別するために、これらを用いてください。

たとえば、もしあなたのアプリケーションが共有メモリ領域を作成するのであれば、あなたはその領域の名前にセッションIDを含めることができます。(同一アプリケーションのコピーを含む)他のアプリケーションは、このメモリ領域に、共有される名前とセッションの情報を用いてアクセスすることができるでしょう。

あなたは、一時ファイル同士をグループ化し、見つけることを簡単にするために、ディレクトリ名の中でセッションIDを使用することができます。けれども、そのディレクトリ内のファイルのためのランダムな名前を生成するためには、依然としてmktemp系列の関数を用いることが推奨されます。

セキュリティフレームワークを使用する

Securityフレームワークは、ルートセッション内かログインセッション内に属する、どんなプログラムにもリンクすることができます。このフレームワークは、セッションIDやいくつかのセッション属性を含む、現在のセッションに関する基本的な情報を取得するための、SessionGetInfo関数を含んでいます。

現在のセッションのセッションIDを得るためには、SessionGetInfoの第一引数に、callerSecuritySessionという値を渡してください。もし、別のログインセッションのセッションIDを持つ必要が生じたら、あなたは他のセッションについての情報を得るためにこれを使用することができます。SessionGetInfoによって返されるパラメータはほとんどは情報であり、変更できません。従って、これらがログインセッションの安全性に影響を与えることはありません。

以下の例は現在のルートもしくはログインセッションのセッションIDを得るために、どのようにSessionGetInfoを呼ぶのかを示します。

#include 
OSStatus error;
SecuritySessionId mySession;
SessionAttributeBits sessionInfo;
error = SessionGetInfo(callerSecuritySession, &mySession, &sessionInfo);

セッションIDそのものは、ログインセッションの持続期間の間じゅう持続する整数値です。補足情報については「ログインセッションを見分ける」(11ページ)を参照してください。

属性ビットパラメータは、それがルートセッションであるか、使用可能なコンソールがあるかといった、セッションに関する追加情報を提供する整数ビットマスク値です。使用できるビット定数の説明については、ヘッダファイルを参照してください。

Core Graphicsフレームワークを使用する

Core Graphicsフレームワークはウインドウサーバの存在に依存しており、そのためログインセッションで動作するアプリケーションに対してのみ利用できます。

このフレームワークは、ユーザIDや名前を含む現在のログインセッションについての情報を得るための、CGSessionCopyCurrentDictionary関数を含んでいます。

注:CGSessionCopyCurrentDictionaryが返すユーザIDの値は、Securityフレームワークが返すセッションIDと混同してはいけません。ユーザIDはシステム上のユーザ同士を区別するためにシステムによって割り振られる固定値です。セッションIDはセッションをユニークに識別し、同じユーザであってもログインごとに異なるでしょう。

以下の例はCGSessionCopyCurrentDictionary関数を用いて、どのようにログインセッション情報を得るかを示しています。この関数はあなたが必要とする情報を得るために読むことのできる、いくつかのキーを持った辞書型の値を返します。

#include 
#include 
CFStringRef shortUserName;
CFNumberRef userUID;
CFBooleanRef userIsActive;
CFBooleanRef loginCompleted;
int MyCGGetSessionInfo
{
	CFDictionaryRef sessionInfoDict;
	sessionInfoDict = CGSessionCopyCurrentDictionary();
	if (sessionInfoDict == NULL)
	{
		printf(“Unable to get session dictionary.”);
		return(1);
	}
	shortUserName = CFDictionaryGetValue(sessionInfoDict,
		kCGSessionUserNameKey);
	userUID = CFDictionaryGetValue(sessionInfoDict,
		kCGSessionUserIDKey);
	userIsActive = CFDictionaryGetValue(sessionInfoDict,
		kCGSessionOnConsoleKey);
	loginCompleted = CFDictionaryGetValue(sessionInfoDict,
		kCGSessionLoginDoneKey);
}

配布される通知

ファストユーザスイッチの導入以前には、Core Foundation もしくは Cocoaインタフェースのいずれかを用いて送信された、「配布される通知」【distributed notifications】は、オブザーバとして登録されたどのプロセスに対しても配布されていました。

Mac OS X 10.3で導入されたファストユーザスイッチと共に、既存のインタフェースは現在のログインセッション内の登録されたプロセスへと配布を制限するように、変更を加えられました。

この変更はほとんどのアプリケーションの振る舞いに影響を与えることはないでしょうが、この影響を受けるおそれのあるアプリケーションのために、ログインセッション境界を越えた、配布される通知のための新しいインタフェースが追加されました。

全セッションへ通知を送る

もしあなたが配布される通知を他のプロセス群へ送信する必要があれば、一般に現在のログインセッション内のプロセス群に対してのみそうするべきです。

けれども、対応するログインセッションに関係なく、あなたがすべてのプロセスに対して通知を送る必要がある場合があるかもしれません。たとえば、その設定に依存するすべてのアプリケーションに影響を与えるような、システム-グローバルな設定の変更などです。

Core Foundationでは、CFNotificationCenterPostNotificationWithOptions関数があなたにすべてのログインセッションへ通知を送ることを可能にします。この関数をグローバル通知のために使用するには、optionsパラメータにkCFNotificationPostToAllSessions定数を渡してください。

Cocoaでは、NSDistributedNotificationCenterオブジェクトのpostNotificationName:object:userInfo:options:メソッドがあなたにすべてのログインセッションへ通知を送ることを可能にします。この関数【メソッド】をグローバル通知のために使用するには、optionsパラメータにNSNotificationPostToAllSessions定数を渡してください。

グローバル通知の使用は、常にできるだけ最小限にするべきです。

もし通知を開始したログインセッションを識別する必要があれば、通知のユーザ情報辞書にそのセッションIDを配置してください。セッションIDの取得についての詳細は、「ログインセッション情報を取得する」(14ページ)を参照してください。

配布される通知を取り扱う

グローバル通知のためのハンドラは、通知が異なるログインセッションからやってきた時であっても取り扱う用意がなければなりません。もしあなたのハンドラが同じログインセッションで発せられたことを仮定していたら、あなたのアプリケーションはファストユーザスイッチ環境において問題を経験することになるかもしれません。

幸い、配布される通知のための既存のメソッドは、配布の範囲を現在のログインセッションに制限しています。グローバル通知を送信する必要のあるアプリケーションは、Mac OS X バージョン10.3で導入されたインタフェースを用いて、これらの通知を明示的に送信するように変更されなければならないでしょう。

もしあなたがあなたのアプリケーションを特定のグローバル通知を受信するように設定していたら、あなたはその通知のすべてのインスタンスを処理する必要はないだろう、ということを心に留めて置いてください。たとえば、ルートセッション内のデーモンはアクティブセッション内のプロセスと通信するためにグローバル通知を使用することがあります。この状況においては、グローバル通知の使用は好ましいことです。なぜなら、デーモンはログインセッション内のプログラムについて、一切の知識を持たないからです。すべてのセッションへのブロードキャストによって、その通知はアクティブセッションへ届くことが保障されます。非アクティブセッション内のアプリケーションは、単純にその通知を無視します。

複数セッション対応を使用不能にする

もし必要があれば、あなたはMac OS Xに対して、あなたのアプリケーションを一つ以上のログインセッションで実行しないように告げることができます。

この選択肢はできれは避けるべきです。けれども、もしあなたのアプリケーションを複数ログインセッションにおいて適切に実行させることができなければ、この選択肢を用いることを検討してもよいでしょう。

あなたのアプリケーションに対する複数セッション対応を使用不能にするには、あなたのアプリケーションのインフォメーションプロパティリストに LSMultipleInstancesProhibited キーを含めてください。このキーが現れ、trueに設定されると、Launch Servicesはあなたのアプリケーションの二番目のインスタンスを起動しようという試みを、どのログインセッションにおいても拒否します。

このキーは複数セッション間でのあなたのアプリケーションの起動に対しても、同一セッションにおける二番目のインスタンスの起動に対しても、共に作用します。どちらの場合にも、Launch Servicesは呼び出したプロセスへ失敗の理由を示すエラーを返します。

LSMultipleInstancesProhibited キーの使い方の詳細は、Runtime Configuration Guidelinesの、"Property List Key Reference"を参照してください。